Skip to content

Commit 44b6d98

Browse files
committed
Macro: Provide a way to define abstract macro state machine
See merge request Karabo/Framework!5523
2 parents 40ec3ff + c6a6999 commit 44b6d98

2 files changed

Lines changed: 53 additions & 8 deletions

File tree

src/pythonKarabo/karabo/middlelayer_api/macro.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ async def suppress_exception(coro):
8181
pass
8282

8383

84-
def _wrapslot(slot, name):
84+
def _wrapslot(slot, name, abstract_passive, abstract_active):
8585
if slot.allowedStates is None:
86-
slot.allowedStates = {State.PASSIVE}
86+
slot.allowedStates = {abstract_passive}
8787
themethod = slot.method
8888

8989
# Note: No need to re-raise CancelledErrors, we got cancelled
@@ -94,7 +94,7 @@ def _wrapslot(slot, name):
9494
async def wrapper(device):
9595
device._last_action = current_task()
9696
device.currentSlot = name
97-
device.state = State.ACTIVE
97+
device.state = abstract_active
9898
try:
9999
return (await themethod(device))
100100
except CancelledError:
@@ -104,13 +104,13 @@ async def wrapper(device):
104104
loop.create_task(coro, instance=device)
105105
finally:
106106
device.currentSlot = ""
107-
device.state = State.PASSIVE
107+
device.state = abstract_passive
108108
else:
109109
@wraps(themethod)
110110
def wrapper(device):
111111
device._last_action = get_event_loop()
112112
device.currentSlot = name
113-
device.state = State.ACTIVE
113+
device.state = abstract_active
114114
try:
115115
return themethod(device)
116116
except CancelledError:
@@ -120,12 +120,16 @@ def wrapper(device):
120120
loop.create_task(coro, instance=device)
121121
finally:
122122
device.currentSlot = ""
123-
device.state = State.PASSIVE
123+
device.state = abstract_passive
124124

125125
slot.method = wrapper
126126

127127

128128
class Macro(Device):
129+
130+
abstractPassiveState = State.PASSIVE
131+
abstractActiveState = State.ACTIVE
132+
129133
abstract = True
130134
subclasses = []
131135
_last_action = None
@@ -181,7 +185,9 @@ def register(cls, name, dict):
181185
for k, v in dict.items():
182186
# patch slots for macro state machine behavior
183187
if isinstance(v, Slot):
184-
_wrapslot(v, k)
188+
passive_state = cls.abstractPassiveState
189+
active_state = cls.abstractActiveState
190+
_wrapslot(v, k, passive_state, active_state)
185191
super().register(name, dict)
186192
# every macro cls is appended to the subclasses for the macro server
187193
# to instantiate
@@ -239,7 +245,7 @@ async def connect(key, remote):
239245
if isinstance(v, RemoteDevice)))
240246
for h in holders:
241247
ensure_future(h)
242-
self.state = State.PASSIVE
248+
self.state = self.abstractPassiveState
243249

244250
async def __holdDevice(self, d):
245251
"""keep the connection to a remote device

src/pythonKarabo/karabo/middlelayer_api/tests/macro_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ async def asyncSleep(self):
148148
await sleep(1)
149149

150150

151+
class LocalAbstract(Macro):
152+
abstractPassiveState = State.ON
153+
abstractActiveState = State.MOVING
154+
155+
@Slot()
156+
def start(self):
157+
"""Just sleep a bit for state change"""
158+
sleep(0.1)
159+
160+
161+
151162
class Tests(DeviceTest):
152163
@classmethod
153164
@contextmanager
@@ -638,5 +649,33 @@ async def test_async_slot_macro(self):
638649
self.assertEqual(d.state, State.PASSIVE)
639650

640651

652+
class AbstractMacroTest(DeviceTest):
653+
@classmethod
654+
@contextmanager
655+
def lifetimeManager(cls):
656+
cls.local = LocalAbstract(_deviceId_="local_abstract", project="test", module="test")
657+
with cls.deviceManager(lead=cls.local):
658+
yield
659+
660+
@async_tst
661+
async def test_state_machine(self):
662+
"""test the execution of abstract macro with new state machine"""
663+
with await getDevice("local_abstract") as d:
664+
self.assertEqual(d.state, State.ON)
665+
666+
seen = False
667+
async def show_active():
668+
nonlocal d, seen
669+
await waitUntil(lambda: d.state == State.MOVING)
670+
seen = True
671+
672+
task = background(show_active())
673+
# Give background a chance to post on loop!
674+
await sleep(0.05)
675+
await d.start()
676+
await task
677+
self.assertTrue(seen)
678+
679+
641680
if __name__ == "__main__":
642681
main()

0 commit comments

Comments
 (0)