diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 345d628..e3a9022 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -28,6 +28,7 @@ Improvements now be called after performing signal acquisition. - Invert channel and vtime functions implemented in the DSO driver. - Fxconfig fixes for cmd2 4.0.0 +- Jig switching fix to force sending the reset signal regardless of presumed jig state ************* Version 0.6.4 diff --git a/src/fixate/_switching.py b/src/fixate/_switching.py index f9209d7..eaf671e 100644 --- a/src/fixate/_switching.py +++ b/src/fixate/_switching.py @@ -619,13 +619,13 @@ def _do_pending_updates(self) -> None: time.sleep(collated.minimum_change_time) self._dispatch_pin_state(collated.final) - def _dispatch_pin_state(self, new_state: PinSetState) -> None: + def _dispatch_pin_state(self, new_state: PinSetState, force: bool = False) -> None: # check all pins actually have an address handler to send to if unknown_pins := (new_state.on | new_state.off) - self._all_pins: raise ValueError(f"Can't switch unknown pin(s) {', '.join(unknown_pins)}.") new_active_pins = (self._active_pins | new_state.on) - new_state.off - if new_active_pins != self._active_pins: + if (new_active_pins != self._active_pins) or force: self._active_pins = new_active_pins for pin_set, handler in self._handler_pin_sets: # Note that we might send an empty set here. We need to do that @@ -645,7 +645,7 @@ def reset(self) -> None: possible the state of each VirtualMux and its related pins will not be in sync. """ - self._dispatch_pin_state(PinSetState(off=self._all_pins)) + self._dispatch_pin_state(PinSetState(off=self._all_pins), force=True) def update_input(self) -> None: """ @@ -743,6 +743,9 @@ def reset(self) -> None: """ Reset all VirtualMux's to the default signal "" (all pins off) """ + # first reset the virtual map of pins to a known default state + self.virtual_map.reset() + # now reset the muxes to ensure the virtual map and muxes are synced self.mux.reset() def _validate(self) -> None: diff --git a/test/test_switching.py b/test/test_switching.py index 527f1d2..611d9dc 100644 --- a/test/test_switching.py +++ b/test/test_switching.py @@ -1,4 +1,7 @@ +from typing import Collection, Sequence + from fixate._switching import ( + Pin, _generate_bit_sets, VirtualMux, _bit_generator, @@ -592,3 +595,52 @@ def test_pin_update_or(): 2.0, ) assert expected == a | b + + +def test_jig_driver_reset_when_desynced(): + """ + test that reset forces the physical of the jig back to default + we do this by directly switching using the address handler to force a desync + between the virtual_map and the handler + + cases where this could happen in a real test are after force quitting a script + where the physical state of pins would not be cleared + """ + + class Handler(AddressHandler): + def __init__(self, pins: Sequence[Pin]) -> None: + self.physical_pin_states = {pin: False for pin in pins} + super().__init__(pins) + + def set_pins(self, pins: Collection[Pin]) -> None: + old_pins = self.physical_pin_states + self.physical_pin_states = {pin: (pin in pins) for pin in old_pins} + + handler = Handler(("x0", "x1", "x2")) + + class Mux(VirtualMux): + pin_list = ("x0", "x1", "x2") + map_list = ("sig1", "x1", "x2") + + class Group(MuxGroup): + def __init__(self): + self.mux = Mux() + + jig = JigDriver(Group, [handler]) + # directly switch some pins in the handler, the jig won't know about this + handler.set_pins(("x0", "x1")) + assert handler.physical_pin_states == { + "x0": True, + "x1": True, + "x2": False, + } + # the jig doesn't know about the state of the pins if not switching using the mux + assert not jig.active_pins() + + # resetting the jig should clear the pins regardless about what it thinks the state of the pins are + jig.reset() + assert handler.physical_pin_states == { + "x0": False, + "x1": False, + "x2": False, + }