Drivers¶
The “drivers” directory contains implementation of all drivers for various devices. Sensors, effectors as well as the mobile platforms are included. The driver takes care of a single device, or better to say a single communication stream (serial.Serial for example). The data are collected, logged and parsed for further processing.
Logging¶
There is at least one stream logged per driver - the input for sensors and the output for effectors. If bidirectional communication is required then both streams are logged. The “stream ID” is part of the driver configuration.
Use cases¶
There are two basic use cases: real run and replay. In real run real port is opened and dedicated Thread::run() takes care of reading data with timeout and the processing.
In the “replay” mode the module receives bus data from a log file. This mode is mainly designed for debugging, development of parsers and also verification of identical outputs on different platforms (robot PC vs. notebook for example).
Replay does not automatically replace device connections opened by a driver. A driver that connects to an external server in its startup code still needs that server, or an explicit mock or replay implementation.
Drivers using async libraries¶
Inherit from osgar.asyncnode.AsyncNode when using a device library with an
asyncio API. Each instance runs an event loop in its own node thread.
Override main() to open the connection, then await listen_loop() to
process bus messages. For example, a TCP client can use:
import asyncio
from osgar.asyncnode import AsyncNode
class AsyncTCPWriter(AsyncNode):
def __init__(self, config, bus):
super().__init__(config, bus)
self.host = config['host']
self.port = config['port']
async def main(self):
reader, self.writer = await asyncio.open_connection(self.host, self.port)
try:
await self.listen_loop()
finally:
self.writer.close()
await self.writer.wait_closed()
async def on_raw(self, data):
self.writer.write(data)
await self.writer.drain()
Handlers can be synchronous or async. The listen loop finishes each handler
before dispatching the next message; awaiting I/O allows other event-loop
tasks to run but does not dispatch another bus message. Avoid blocking calls
inside handlers. Use await self.sleep_async(seconds) for an elapsed real-time
delay, and self.time for the timestamp of the current bus message.
Register outputs in __init__ as usual and call self.publish() without
await. Configuration and links use the same format as synchronous drivers.
During a live run, request_stop() shuts down the bus handle and requests
cancellation of main(). Use context managers or finally blocks for
connection cleanup and allow cancellation to propagate.
The AsyncEcho example demonstrates a TCP server and an async application communicating over the bus.