From 108abea6f6ceac044dc8b2b526c139e4589bd1f7 Mon Sep 17 00:00:00 2001 From: Daniel Montanari Date: Tue, 24 Feb 2026 13:29:49 +1100 Subject: [PATCH 1/5] force reset of pins --- src/fixate/_switching.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/fixate/_switching.py b/src/fixate/_switching.py index f9209d7e..9e45396c 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) | 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: From 43d44fdf8c1c7e8cb7097042ce00ef8bb1e9c60a Mon Sep 17 00:00:00 2001 From: Daniel Montanari Date: Tue, 24 Feb 2026 13:44:58 +1100 Subject: [PATCH 2/5] bump release notes --- docs/release-notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 1d4a4ac4..979560ba 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -20,6 +20,7 @@ Improvements - Sequencer logic now handles exceptions raised on sequence abort. GUI will no longer hang when a test raises an exception during a test abort. - Fix bug where DSOX1202G appeared to hang both the program and scope - LCR Driver now supports instruments reporting as Keysight or Agilent. Newer models of the LCR meter report as Keysight, whereas older models report as Agilent. +- Jig switching fix to force sending the reset signal regardless of presumed jig state ************* Version 0.6.4 From dfb6ed1a19b784c2e9d90cc41192c94d73e6d210 Mon Sep 17 00:00:00 2001 From: Daniel Montanari Date: Wed, 24 Jun 2026 10:36:30 +1000 Subject: [PATCH 3/5] change away from bitwise --- src/fixate/_switching.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixate/_switching.py b/src/fixate/_switching.py index 9e45396c..eaf671ed 100644 --- a/src/fixate/_switching.py +++ b/src/fixate/_switching.py @@ -625,7 +625,7 @@ def _dispatch_pin_state(self, new_state: PinSetState, force: bool = False) -> No 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) | force: + 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 From b003541fe7cc0ca40b3d48565ace625138613ffe Mon Sep 17 00:00:00 2001 From: Daniel Montanari Date: Wed, 24 Jun 2026 11:59:11 +1000 Subject: [PATCH 4/5] add test --- test/test_switching.py | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/test_switching.py b/test/test_switching.py index 527f1d2c..89bb44a6 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,43 @@ def test_pin_update_or(): 2.0, ) assert expected == a | b + + +def test_jig_driver_reset_on_(): + 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, + } From c1ff9bb081c518e95919f6866799bd97017904bb Mon Sep 17 00:00:00 2001 From: Daniel Montanari Date: Wed, 24 Jun 2026 12:03:54 +1000 Subject: [PATCH 5/5] better name and explanation for test --- test/test_switching.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/test_switching.py b/test/test_switching.py index 89bb44a6..611d9dc9 100644 --- a/test/test_switching.py +++ b/test/test_switching.py @@ -597,7 +597,16 @@ def test_pin_update_or(): assert expected == a | b -def test_jig_driver_reset_on_(): +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}