-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Possible driver manager concept #182
base: main
Are you sure you want to change the base?
Conversation
At the moment, we sometimes create dm = JigDriverManager()
class DriverManager:
def __init__(self):
pub.subscribe(self.test_exception, "Test_Exception")
logging.debug("Driver Manager Init")
self._dmm = None
self._pps = None
self._relay_matrix = None
self._jig_J513 = None
class FTDIAddressHandlerJ513(AddressHandler):
pin_list = (
"1K1",
...
"3K16",
)
def update_output(self, value):
dm.relay_matrix.serial_shift_bit_bang(value) Note the reference to the global Happy to be proven wrong on that :) |
Starting to chip away at that problem over here master...clint-lawrence:Fixate:jig-mux-refactor |
Another aspect that would be helpful to tie into a rework of the driver manager is a way to decouple or delay the execution of opening drivers until they are used. I.e. for IPM3 we are testing a bunch of comms channels and using a mux to switch in either serial uart or RS485 converters. Would be ideal to have a simple test list such as:
With a generic test class:
With each of the serial ports being a property of the Driver Manager:
Since at import time the serial ports are trying to be accessed, if the device is not connected an exception will be raised before the sequencer and UI are up and running. |
Hey, I know I'm very out of the loop with current practices. Written on phone so excuse code mistakes. But is is better to do something like dependency injection and do away with the driver manager altogether as a global accessor. But rather just use as a context manager and to inject from it to the methods. Would give you the delayed initialisation. And makes type checking easier and local to the the test function. class SerialCommTest(TestClass):
def __init__(
self, mux: str, PortA: serial.Serial, PortB: serial.Serial, *args, **kwargs
):
super().__init__(*args, **kwargs)
self.mux = mux
self.port_a = PortA
self.port_b = PortB
def setup(self, jig_j511: JigJ511):
jig_j511.mux(self.mux)
def test(self, port_a: serial.Serial, port_b: serial.Serial):
port_a.write(self.Message)
message1 = port_b.readline()
chk_true(message1 == self.Message, f"Comms channel: {self.mux}")
message_return = self.Message + "-reverse".encode("utf-8")
port_b.write(message_return)
message2 = port_b.readline()
chk_true(
message2 == message_return, f"Comms channel: {self.mux} return"
) |
from contextlib import contextmanager
class DriverManager:
stack: ExitStack
...
def register(name:str, driver:Generator):
self.drivers[name] = contextmanager(driver)
def __enter__():
self.stack = ExitStack()
return self
...
def enter_context(name:str):
self.active[name] = self.stack.enter_context(self.drivers[name])
return self.active[name]
# properties for backwards compatibility
@property
def rs422_A(self) -> serial.Serial:
"""RS422 converters for serial comms"""
return self.active.get("rs422_A", self.enter_context("rs422_A")) |
Although, I guess the enter_context and stack should live on the test list, or sequencer not the driver manager. So that you could have set up and teardown of drivers at the same level required by the test lists. Or the sequencer could inject it into the test list etc. Either way, I think this approach simplifies specifying type annotations, at the cost of a "little bit of magic" with using the inspect module to find the appropriate driver for each test list / test by looking at the function signature and matching it by name. The driver manager also becomes generic instead of a per test script implementation. Not relying on dataclass field default factories, allows lazy loading. And provides actual clear definitions on when to clean up the driver. Eg. def dmm_factory():
dmm = DMM.open()
yield dmm
dmm.close()
def serial_factory(description:str, baud:int):
@wraps
def tmp():
device = serial.Serial(
findftdi.by_description(description),
baudrate=baud,
timeout=0.1,
)
yield device
device.close()
return tmp
dm = DriverManager()
dm.register("dmm", dmm_factory)
dm.register("rs422_A", serial_factory("J511-RS422-A",57600))
dm.register("rs422_B", serial_factory("J511-RS422-B",57600)) You could specify in the top level list if you need the dmm for all tests, or you could get down to specifying only a single mux for a test instead of jig drivers.mux Forgive me if I'm way off base, haven't really looked at it the past 5 years |
This is pretty scrappy, but mostly trying to test out the big picture idea.
Have a look at
test-script.py
. Here is the general concept:The doesn't work 100% yet, the example with the "dummy" driver works. obviously we will also need to think about backwards compatibility.