# Traffic Manager `TrafficManager` is the communication layer between your device class and the physical transport (Serial, VISA, TCP/IP, and others). It standardizes connection handling and command dispatch so device implementations can focus on device behavior instead of low-level I/O details. ## What It Does A traffic manager is responsible for: 1. Opening and closing a connection to a device resource. 2. Sending one command and returning one response. 3. Applying timeout behavior. 4. Providing consistent error handling through a shared interface. 5. Supporting context manager usage (`with ...`) for safe cleanup. In PlestyLib, the abstract base class is `plesty.lib.traffic.TrafficManager`. ## Lifecycle and Interface The public API is: 1. `open(...)` 2. `send_command(command, timeout=None, ...)` 3. `close()` When you implement a custom transport, you override three private hooks: 1. `_open(...)` 2. `_send_command(command, timeout=None, ...)` 3. `_close()` The base class wraps these hooks with common behaviors: 1. In-use resource protection (same address cannot be opened by multiple manager instances). 2. Error capture and reporting. 3. `__enter__` and `__exit__` support. ## Built-in Implementations Common built-in traffic managers include: 1. `SerialTrafficManager` for serial ports. 2. `VisaTrafficManager` for VISA resources. 3. `TcpIpTrafficManager` for TCP/IP connections. ## Demo 1: Use the Built-in SerialTrafficManager ```python from plesty.lib.traffic.serial import SerialTrafficManager manager = SerialTrafficManager( port="/dev/ttyUSB0", # Example on Linux baudrate=9600, timeout=5, write_termination="\r", read_termination="\r\n", ) try: opened = manager.open(parity="none", stopbits="one", bytesize=8) if not opened: raise RuntimeError("Failed to open serial port") # Command format depends on your device protocol. response = manager.send_command("MEAS:VOLT?") print("Response:", response) finally: manager.close() ``` Notes for `SerialTrafficManager`: 1. It appends `write_termination` automatically. 2. It reads until `read_termination`. 3. Current response logic treats lines containing `OK` as successful. ## Demo 2: Implement Your Own Serial Traffic Manager Use this pattern if your serial device protocol differs from the default behavior. ```python from typing import Any import serial from plesty.lib.traffic import TrafficManager class MySerialTrafficManager(TrafficManager): def __init__(self, port: str, baudrate: int = 115200, timeout: int = 3): super().__init__(address=port, timeout=timeout) self.port = port self.baudrate = baudrate def _open(self) -> None: self.inst = serial.Serial( port=self.port, baudrate=self.baudrate, timeout=self.timeout, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, ) self.inst.reset_input_buffer() self.inst.reset_output_buffer() def _send_command(self, command: str, timeout=None) -> Any: if timeout is not None: self.inst.timeout = timeout wire = (command + "\n").encode("utf-8") self.inst.write(wire) # Read one full line response for this example protocol. raw = self.inst.readline() if not raw: return False return raw.decode("utf-8", errors="replace").strip() def _close(self) -> None: self.inst.close() ``` Then use it through the standard public API: ```python tm = MySerialTrafficManager("/dev/ttyUSB0", baudrate=115200) with tm: print(tm.send_command("*IDN?")) ``` ## Integration with Device Classes In a synchronized device implementation, traffic managers are usually called inside low-level `query` and `write` paths: 1. Device receives a standardized operation. 2. Command solver builds the protocol command string. 3. Traffic manager sends/receives bytes or text. 4. Command solver decodes response into standardized value. This separation keeps each layer simple and testable. ## Best Practices 1. Keep transport-specific logic inside traffic managers only. 2. Keep protocol formatting in command solvers, not in traffic managers. 3. Always use `with manager:` or `try/finally` to guarantee `close()`. 4. Use explicit timeout values suitable for your device. 5. Validate port/resource settings early in `_open()`.