From d6cd04f3d9b9b04fbac05baea9069c896f066392 Mon Sep 17 00:00:00 2001 From: Rose Yemelyanova Date: Tue, 11 Jul 2023 14:56:57 +0000 Subject: [PATCH] Allow standalone system simulation component This involves changing the SystemSimulation input parameters, and making it so that the slave scheduler runs an 'initial' tick in the same way as master. --- .../standalone-system-sim-counter.yaml | 11 +++++++ examples/configs/standalone-system-sim.yaml | 12 +++++++ examples/configs/test.yaml | 8 +++++ .../core/components/system_simulation.py | 4 +-- .../core/management/schedulers/slave.py | 31 ++++++++++++++++--- .../schedulers/test_slave_scheduler.py | 3 ++ 6 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 examples/configs/standalone-system-sim-counter.yaml create mode 100644 examples/configs/standalone-system-sim.yaml create mode 100644 examples/configs/test.yaml diff --git a/examples/configs/standalone-system-sim-counter.yaml b/examples/configs/standalone-system-sim-counter.yaml new file mode 100644 index 000000000..1609744eb --- /dev/null +++ b/examples/configs/standalone-system-sim-counter.yaml @@ -0,0 +1,11 @@ +- tickit.core.components.system_simulation.SystemSimulation: + name: internal_tickit + inputs: {} + components: + - examples.devices.counter.Counter: + name: counter + inputs: {} + - tickit.devices.sink.Sink: + name: counter_sink + inputs: + input: counter:value diff --git a/examples/configs/standalone-system-sim.yaml b/examples/configs/standalone-system-sim.yaml new file mode 100644 index 000000000..be0ff29f7 --- /dev/null +++ b/examples/configs/standalone-system-sim.yaml @@ -0,0 +1,12 @@ +- tickit.core.components.system_simulation.SystemSimulation: + name: internal_tickit + inputs: {} + components: + - tickit.devices.source.Source: + name: internal_source + inputs: {} + value: 42 + - tickit.devices.sink.Sink: + name: internal_sink + inputs: + input: internal_source:value diff --git a/examples/configs/test.yaml b/examples/configs/test.yaml new file mode 100644 index 000000000..2b8eecc98 --- /dev/null +++ b/examples/configs/test.yaml @@ -0,0 +1,8 @@ +- tickit.devices.source.Source: + name: internal_source + inputs: {} + value: 42 +- tickit.devices.sink.Sink: + name: internal_sink + inputs: + input: internal_source:value diff --git a/src/tickit/core/components/system_simulation.py b/src/tickit/core/components/system_simulation.py index 61073f6a8..facfb8bf0 100644 --- a/src/tickit/core/components/system_simulation.py +++ b/src/tickit/core/components/system_simulation.py @@ -102,10 +102,8 @@ async def stop_component(self) -> None: class SystemSimulation(ComponentConfig): """Simulation of a nested set of components.""" - name: ComponentID - inputs: Dict[PortID, ComponentPort] components: List[ComponentConfig] - expose: Dict[PortID, ComponentPort] + expose: Dict[PortID, ComponentPort] = field(default_factory=dict) def __call__(self) -> Component: # noqa: D102 return SystemSimulationComponent( diff --git a/src/tickit/core/management/schedulers/slave.py b/src/tickit/core/management/schedulers/slave.py index ab3fb0420..828d4296e 100644 --- a/src/tickit/core/management/schedulers/slave.py +++ b/src/tickit/core/management/schedulers/slave.py @@ -1,3 +1,4 @@ +import asyncio import logging from typing import Awaitable, Callable, Dict, Optional, Set, Tuple, Type, Union @@ -48,10 +49,22 @@ def __init__( wiring = self.add_exposing_wiring(wiring, expose) super().__init__(wiring, state_consumer, state_producer) + reads_from_external: bool = False + for component in self._wiring: + input_components = [ + p.component for p in self._wiring[component].values() # type: ignore + ] + if "external" in input_components: + reads_from_external = True + break + + self.reads_from_external = reads_from_external self.raise_interrupt = raise_interrupt self.interrupts: Set[ComponentID] = set() self.component_error: ComponentException + self.first_tick: asyncio.Event = asyncio.Event() + @staticmethod def add_exposing_wiring( wiring: Union[Wiring, InverseWiring], @@ -127,17 +140,20 @@ async def on_tick( wakeup_components = { component for component, when in self.wakeups.items() if when <= time } - root_components: Set[ComponentID] = { - *self.interrupts, - *wakeup_components, - ComponentID("external"), - } + + root_components: Set[ComponentID] = {*self.interrupts, *wakeup_components} + + if self.reads_from_external: + root_components.update([ComponentID("external")]) + for component in wakeup_components: del self.wakeups[component] self.interrupts.clear() self.input_changes = changes self.output_changes = Changes(Map()) + + await asyncio.wait_for(self.first_tick.wait(), timeout=30) await self.ticker(time, root_components) _, call_at = self.get_first_wakeups() @@ -146,6 +162,11 @@ async def on_tick( async def run_forever(self) -> None: """Delegates to setup which instantiates the ticker and state interfaces.""" await self.setup() + await self.ticker( + SimTime(0), + self.ticker.components, + ) + self.first_tick.set() async def schedule_interrupt(self, source: ComponentID) -> None: """Schedules the interrupt of a component immediately. diff --git a/tests/core/management/schedulers/test_slave_scheduler.py b/tests/core/management/schedulers/test_slave_scheduler.py index 456bc9270..2904ecfac 100644 --- a/tests/core/management/schedulers/test_slave_scheduler.py +++ b/tests/core/management/schedulers/test_slave_scheduler.py @@ -179,6 +179,8 @@ async def test_slave_scheduler_run_forever_method(slave_scheduler: SlaveSchedule async def test_slave_scheduler_on_tick_method( slave_scheduler: SlaveScheduler, mock_ticker: Mock ): + await slave_scheduler.run_forever() + changes = Changes(Map({PortID("67"): 67})) slave_scheduler.ticker = mock_ticker output_changes, call_at = await slave_scheduler.on_tick(SimTime(8), changes) @@ -190,6 +192,7 @@ async def test_slave_scheduler_on_tick_method( async def test_slave_scheduler_on_tick_method_with_wakeups( slave_scheduler: SlaveScheduler, mock_ticker: Mock ): + await slave_scheduler.run_forever() changes = Changes(Map({PortID("67"): 67})) slave_scheduler.ticker = mock_ticker slave_scheduler.wakeups = {