diff --git a/docs/images/tickit-device-component.svg b/docs/images/tickit-device-component.svg new file mode 100644 index 000000000..9ce581874 --- /dev/null +++ b/docs/images/tickit-device-component.svg @@ -0,0 +1,17 @@ + + + + + + + + DeviceAdapterDevice Component \ No newline at end of file diff --git a/docs/images/tickit-device-simulation-cpt.svg b/docs/images/tickit-device-simulation-cpt.svg deleted file mode 100644 index 84a1c7f21..000000000 --- a/docs/images/tickit-device-simulation-cpt.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - DeviceAdapterComponent (Device Simulation) \ No newline at end of file diff --git a/docs/images/tickit-overview-with-system-component.svg b/docs/images/tickit-overview-with-system-component.svg new file mode 100644 index 000000000..546c2a728 --- /dev/null +++ b/docs/images/tickit-overview-with-system-component.svg @@ -0,0 +1,17 @@ + + + + + + + + DeviceAdapterDevice ComponentDeviceAdapterDevice ComponentNested SchedulerSystem ComponentDeviceAdapterDevice ComponentScheduler \ No newline at end of file diff --git a/docs/images/tickit-simple-overview-with-system-simulation.svg b/docs/images/tickit-simple-overview-with-system-simulation.svg deleted file mode 100644 index 84e5a7edb..000000000 --- a/docs/images/tickit-simple-overview-with-system-simulation.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - DeviceAdapterComponent (Device Simulation)SchedulerDeviceAdapterComponent (Device Simulation)DeviceAdapterSlave SchedulerComponent (System Simulation)Component (Device Simulation) \ No newline at end of file diff --git a/docs/images/tickit-system-component.svg b/docs/images/tickit-system-component.svg new file mode 100644 index 000000000..edf6cc82d --- /dev/null +++ b/docs/images/tickit-system-component.svg @@ -0,0 +1,17 @@ + + + + + + + + DeviceAdapterDevice ComponentDeviceAdapterDevice ComponentNested SchedulerSystem Component \ No newline at end of file diff --git a/docs/images/tickit-system-simulation-cpt.svg b/docs/images/tickit-system-simulation-cpt.svg deleted file mode 100644 index 38e2d5e0c..000000000 --- a/docs/images/tickit-system-simulation-cpt.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - DeviceAdapterComponent (Device Simulation)DeviceAdapterSlave SchedulerComponent (System Simulation)Component (Device Simulation) \ No newline at end of file diff --git a/docs/user/explanations/components.rst b/docs/user/explanations/components.rst index 98de201bc..85331d9fb 100644 --- a/docs/user/explanations/components.rst +++ b/docs/user/explanations/components.rst @@ -2,37 +2,37 @@ Simulation Components ===================== There are two types of components that can be used in a tickit simulation, -device simulations and system simulations. +device components and system components. -Device Simulation +Device Component ----------------- -Device simulations are the typical use case of a component. They encapsulate a +Device components are the typical use case of a component. They encapsulate a device and any adapters for that device. -.. figure:: ../../images/tickit-device-simulation-cpt.svg +.. figure:: ../../images/tickit-device-component.svg :align: center (See `DeviceComponent`.) -System Simulation +System Component ----------------- -System simulation components are themselves entire tickit simulations. They -contain their own device simulation components and a scheduler for orchestrating -them. However, the scheduler in a system component acts as a slave scheduler -which is driven by the master scheduler in the top level of the simulation +System components are themselves entire tickit simulations. They +contain their own device components and a scheduler for orchestrating +them. However, the scheduler in a system component acts as a nested scheduler +which is triggered by the master scheduler in the top level of the simulation it belongs to. -.. figure:: ../../images/tickit-system-simulation-cpt.svg +.. figure:: ../../images/tickit-system-component.svg :align: center -System simulations can also contain their own system simulation components +System components can also contain their own system components allowing for the construction of reasonably complex systems. -System simulations can be nested inside other components in the config so that +System components can be nested inside other components in the config so that the master scheduler's wiring is correct, for example: .. code-block:: yaml @@ -75,7 +75,7 @@ The Overall Simulation A simulation containing both types of component will look something like this: -.. figure:: ../../images/tickit-simple-overview-with-system-simulation.svg +.. figure:: ../../images/tickit-overview-with-system-component.svg :align: center diff --git a/docs/user/explanations/glossary.rst b/docs/user/explanations/glossary.rst index f05a3fd49..b77d9a7bf 100644 --- a/docs/user/explanations/glossary.rst +++ b/docs/user/explanations/glossary.rst @@ -20,7 +20,7 @@ Glossary unpredictable internal changes. System Simulation - A system simulation is a component which wraps a slave scheduler and a + A system simulation is a component which wraps a nested scheduler and a collection of components, allowing for infinitely nested systems. Device Simulation diff --git a/docs/user/reference/api.rst b/docs/user/reference/api.rst index fe566dd98..cda610a87 100644 --- a/docs/user/reference/api.rst +++ b/docs/user/reference/api.rst @@ -85,11 +85,11 @@ This is the internal API reference for tickit ``tickit.core.management.schedulers.master`` -------------------------------------------- - .. automodule:: tickit.core.management.schedulers.slave + .. automodule:: tickit.core.management.schedulers.nested :members: - ``tickit.core.management.schedulers.slave`` - ------------------------------------------- + ``tickit.core.management.schedulers.nested`` + -------------------------------------------- .. automodule:: tickit.core.management.event_router diff --git a/src/tickit/core/components/component.py b/src/tickit/core/components/component.py index ecc430555..1f6893333 100644 --- a/src/tickit/core/components/component.py +++ b/src/tickit/core/components/component.py @@ -35,7 +35,7 @@ class Component: An interface for types which implement stand-alone simulation components. Components define the top level building blocks of a tickit simulation (examples include the DeviceComponent which host a device and corresponding adapter or a - SystemComponent which hosts a SlaveScheduler and internal Components). + SystemComponent which hosts a NestedScheduler and internal Components). """ name: ComponentID diff --git a/src/tickit/core/components/system_component.py b/src/tickit/core/components/system_component.py index e145bbf80..cfbd4a08c 100644 --- a/src/tickit/core/components/system_component.py +++ b/src/tickit/core/components/system_component.py @@ -9,7 +9,7 @@ from tickit.core.adapter import AdapterContainer from tickit.core.components.component import BaseComponent, Component, ComponentConfig from tickit.core.management.event_router import InverseWiring -from tickit.core.management.schedulers.slave import SlaveScheduler +from tickit.core.management.schedulers.nested import NestedScheduler from tickit.core.state_interfaces.state_interface import StateConsumer, StateProducer from tickit.core.typedefs import Changes, ComponentID, ComponentPort, PortID, SimTime from tickit.utils.topic_naming import output_topic @@ -19,9 +19,9 @@ @dataclass class SystemComponent(BaseComponent): - """A component containing a slave scheduler and several components. + """A component containing a nested scheduler and several components. - A component which acts as a nested tickit simulation by wrapping a slave scheduler + A component which acts as a nested tickit simulation by wrapping a NestedScheduler and a set of internal components, this component delegates core behaviour to the components within it, whilst outputting their requests for wakeups and interrupts. """ @@ -49,7 +49,7 @@ async def run_forever( blocks until and of the components or the scheduler complete. """ inverse_wiring = InverseWiring.from_component_configs(self.components) - self.scheduler = SlaveScheduler( + self.scheduler = NestedScheduler( inverse_wiring, state_consumer, state_producer, @@ -75,10 +75,10 @@ async def run_forever( await asyncio.wait(self._tasks) async def on_tick(self, time: SimTime, changes: Changes) -> None: - """Delegates core behaviour to the slave scheduler. + """Delegates core behaviour to the nested scheduler. An asynchronous method which delegates core behaviour of computing changes and - determining a callback period to the slave scheduler and sends the resulting + determining a callback period to the nested scheduler and sends the resulting Output. Args: diff --git a/src/tickit/core/management/schedulers/slave.py b/src/tickit/core/management/schedulers/nested.py similarity index 90% rename from src/tickit/core/management/schedulers/slave.py rename to src/tickit/core/management/schedulers/nested.py index ab3fb0420..7076717ec 100644 --- a/src/tickit/core/management/schedulers/slave.py +++ b/src/tickit/core/management/schedulers/nested.py @@ -20,8 +20,8 @@ LOGGER = logging.getLogger(__name__) -class SlaveScheduler(BaseScheduler): - """A slave scheduler which orchestrates nested tickit simulations.""" +class NestedScheduler(BaseScheduler): + """A scheduler which orchestrates nested tickit simulations.""" def __init__( self, @@ -31,7 +31,7 @@ def __init__( expose: Dict[PortID, ComponentPort], raise_interrupt: Callable[[], Awaitable[None]], ) -> None: - """Slave scheduler constructor which adds wiring and saves values for reference. + """NestedScheduler constructor which adds wiring and saves values for reference. Args: wiring (Union[Wiring, InverseWiring]): A wiring or inverse wiring object @@ -40,10 +40,10 @@ def __init__( by the component. state_producer (Type[StateProducer]): The state producer class to be used by the component. - expose (Dict[PortID, ComponentPort]): A mapping of slave scheduler + expose (Dict[PortID, ComponentPort]): A mapping of nested scheduler outputs to internal component ports. raise_interrupt (Callable[[], Awaitable[None]]): A callback to request that - the slave scheduler is updated immediately. + the nested scheduler is updated immediately. """ wiring = self.add_exposing_wiring(wiring, expose) super().__init__(wiring, state_consumer, state_producer) @@ -57,22 +57,22 @@ def add_exposing_wiring( wiring: Union[Wiring, InverseWiring], expose: Dict[PortID, ComponentPort], ) -> InverseWiring: - """Adds wiring to expose slave scheduler outputs. + """Adds wiring to expose nested scheduler outputs. - Adds wiring to expose slave scheduler outputs, this is performed creating a + Adds wiring to expose nested scheduler outputs, this is performed creating a mock "expose" component with inverse wiring set by expose. Args: wiring (Union[Wiring, InverseWiring]): A wiring or inverse wiring object representing the connections between components in the system. - expose (Dict[PortID, ComponentPort]): A mapping of slave scheduler + expose (Dict[PortID, ComponentPort]): A mapping of nested scheduler outputs to internal component ports. Returns: InverseWiring: An inverse wiring object representing the connections between components in the system and the "expose" component which acts as the - slave scheduler output. + nested scheduler output. """ if isinstance(wiring, Wiring): wiring = InverseWiring.from_wiring(wiring) @@ -109,7 +109,7 @@ async def on_tick( An asynchronous method which determines which components within the simulation require being woken up, sets the input changes for use by the "external" mock - component, performs a tick, determines the period in which the slave scheduler + component, performs a tick, determines the period in which the nested scheduler should next be updated, and returns the changes collated by the "expose" mock component. @@ -122,7 +122,7 @@ async def on_tick( Tuple[Changes, Optional[SimTime]]: A tuple of a mapping of the changed exposed outputs and their new values and optionally a duration in simulation time after which the - slave scheduler should be called again. + nested scheduler should be called again. """ wakeup_components = { component for component, when in self.wakeups.items() if when <= time @@ -163,7 +163,7 @@ async def schedule_interrupt(self, source: ComponentID) -> None: async def handle_component_exception(self, message: ComponentException) -> None: """Handle exceptions raised from components by shutting down the simulation. - If a component inside a system simulation produces an exception, the slave + If a component inside a system simulation produces an exception, the nested scheduler will produce a message to all components it contains to cause them to cancel any running component tasks (adapter tasks). Afterwards the scheduler stores the ComponentException message, allowing its associated system simulation diff --git a/tests/core/components/test_system_simulation.py b/tests/core/components/test_system_simulation.py index d34edf7cf..e4c8adc4e 100644 --- a/tests/core/components/test_system_simulation.py +++ b/tests/core/components/test_system_simulation.py @@ -36,7 +36,7 @@ def mock_state_consumer_type() -> Mock: @pytest.fixture def patch_scheduler() -> Iterable[Mock]: - spec = "tickit.core.components.system_component.SlaveScheduler" + spec = "tickit.core.components.system_component.NestedScheduler" with patch(spec, autospec=True) as mock: def on_tick(time, changes): diff --git a/tests/core/management/schedulers/test_slave_scheduler.py b/tests/core/management/schedulers/test_nested_scheduler.py similarity index 66% rename from tests/core/management/schedulers/test_slave_scheduler.py rename to tests/core/management/schedulers/test_nested_scheduler.py index 456bc9270..ac6257411 100644 --- a/tests/core/management/schedulers/test_slave_scheduler.py +++ b/tests/core/management/schedulers/test_nested_scheduler.py @@ -6,7 +6,7 @@ from mock import AsyncMock, Mock, create_autospec, patch from tickit.core.management.event_router import InverseWiring, Wiring -from tickit.core.management.schedulers.slave import SlaveScheduler +from tickit.core.management.schedulers.nested import NestedScheduler from tickit.core.management.ticker import Ticker from tickit.core.state_interfaces.state_interface import StateConsumer, StateProducer from tickit.core.typedefs import ( @@ -63,15 +63,15 @@ def expose() -> Dict[PortID, ComponentPort]: @pytest.fixture -def slave_scheduler( +def nested_scheduler( mock_wiring, mock_state_consumer, mock_state_producer, expose, mock_raise_interrupt, patch_ticker, -) -> SlaveScheduler: - return SlaveScheduler( +) -> NestedScheduler: + return NestedScheduler( mock_wiring, mock_state_consumer, mock_state_producer, @@ -80,7 +80,7 @@ def slave_scheduler( ) -def test_construct_slave_scheduler(slave_scheduler, expose): +def test_construct_nested_scheduler(nested_scheduler, expose): pass @@ -91,8 +91,8 @@ def test_construct_slave_scheduler(slave_scheduler, expose): pytest.param(InverseWiring(), id="InverseWiring"), ], ) -def test_slave_scheduler_add_exposing_wiring_static_method(wiring): - """Method to test the static 'add_exposing_wiring' method of 'SlaveScheduler'. +def test_nested_scheduler_add_exposing_wiring_static_method(wiring): + """Method to test the static 'add_exposing_wiring' method of 'NestedScheduler'. This test passes if: 1) The object returned by the method is an instance of 'InverseWiring' regardless @@ -101,7 +101,7 @@ def test_slave_scheduler_add_exposing_wiring_static_method(wiring): with the ports to expose. """ expose = {PortID("42"): ComponentPort(ComponentID("test_component"), PortID("53"))} - result = SlaveScheduler.add_exposing_wiring(wiring, expose) + result = NestedScheduler.add_exposing_wiring(wiring, expose) assert isinstance(result, InverseWiring) assert result[ComponentID("expose")] == expose @@ -113,8 +113,8 @@ def mock_ticker() -> Mock: @pytest.mark.asyncio -async def test_slave_scheduler_update_external_component( - slave_scheduler: SlaveScheduler, +async def test_nested_scheduler_update_external_component( + nested_scheduler: NestedScheduler, mock_ticker: Ticker, ): target = ComponentID("external") @@ -124,17 +124,17 @@ async def test_slave_scheduler_update_external_component( changes=Changes(Map({PortID("31"): 30})), ) - slave_scheduler.ticker = mock_ticker - slave_scheduler.input_changes = input.changes - await slave_scheduler.update_component(input) + nested_scheduler.ticker = mock_ticker + nested_scheduler.input_changes = input.changes + await nested_scheduler.update_component(input) mock_ticker.propagate.assert_awaited_once_with( # type: ignore Output(target, input.time, input.changes, None) ) @pytest.mark.asyncio -async def test_slave_scheduler_update_exposed_component( - slave_scheduler: SlaveScheduler, +async def test_nested_scheduler_update_exposed_component( + nested_scheduler: NestedScheduler, mock_ticker: Ticker, ): target = ComponentID("expose") @@ -144,16 +144,16 @@ async def test_slave_scheduler_update_exposed_component( changes=Changes(Map({PortID("31"): 30})), ) - slave_scheduler.ticker = mock_ticker - await slave_scheduler.update_component(input) + nested_scheduler.ticker = mock_ticker + await nested_scheduler.update_component(input) mock_ticker.propagate.assert_awaited_once_with( # type: ignore Output(target, input.time, Changes(Map()), None) ) @pytest.mark.asyncio -async def test_slave_scheduler_update_other_component( - slave_scheduler: SlaveScheduler, +async def test_nested_scheduler_update_other_component( + nested_scheduler: NestedScheduler, mock_ticker: Ticker, mock_state_producer: AsyncMock, ): @@ -164,60 +164,60 @@ async def test_slave_scheduler_update_other_component( changes=Changes(Map({PortID("31"): 30})), ) - slave_scheduler.ticker = mock_ticker - slave_scheduler.state_producer = mock_state_producer - await slave_scheduler.update_component(input) + nested_scheduler.ticker = mock_ticker + nested_scheduler.state_producer = mock_state_producer + await nested_scheduler.update_component(input) mock_state_producer.produce.assert_awaited_once_with(input_topic(target), input) @pytest.mark.asyncio -async def test_slave_scheduler_run_forever_method(slave_scheduler: SlaveScheduler): - await slave_scheduler.run_forever() +async def test_nested_scheduler_run_forever_method(nested_scheduler: NestedScheduler): + await nested_scheduler.run_forever() @pytest.mark.asyncio -async def test_slave_scheduler_on_tick_method( - slave_scheduler: SlaveScheduler, mock_ticker: Mock +async def test_nested_scheduler_on_tick_method( + nested_scheduler: NestedScheduler, mock_ticker: Mock ): changes = Changes(Map({PortID("67"): 67})) - slave_scheduler.ticker = mock_ticker - output_changes, call_at = await slave_scheduler.on_tick(SimTime(8), changes) + nested_scheduler.ticker = mock_ticker + output_changes, call_at = await nested_scheduler.on_tick(SimTime(8), changes) assert output_changes == Changes(Map()) assert call_at is None @pytest.mark.asyncio -async def test_slave_scheduler_on_tick_method_with_wakeups( - slave_scheduler: SlaveScheduler, mock_ticker: Mock +async def test_nested_scheduler_on_tick_method_with_wakeups( + nested_scheduler: NestedScheduler, mock_ticker: Mock ): changes = Changes(Map({PortID("67"): 67})) - slave_scheduler.ticker = mock_ticker - slave_scheduler.wakeups = { + nested_scheduler.ticker = mock_ticker + nested_scheduler.wakeups = { ComponentID("first"): SimTime(1), ComponentID("second"): SimTime(2), } - output_changes, call_at = await slave_scheduler.on_tick(SimTime(1), changes) + output_changes, call_at = await nested_scheduler.on_tick(SimTime(1), changes) assert output_changes == Changes(Map()) assert call_at == SimTime(2) @pytest.mark.asyncio -async def test_slave_scheduler_schedule_interrupt_method( - slave_scheduler: SlaveScheduler, +async def test_nested_scheduler_schedule_interrupt_method( + nested_scheduler: NestedScheduler, ): interrupt = ComponentID("interrupt") - await slave_scheduler.schedule_interrupt(interrupt) - assert interrupt in slave_scheduler.interrupts + await nested_scheduler.schedule_interrupt(interrupt) + assert interrupt in nested_scheduler.interrupts @pytest.mark.asyncio -async def test_slave_scheduler_handle_exception_message( - slave_scheduler: SlaveScheduler, +async def test_nested_scheduler_handle_exception_message( + nested_scheduler: NestedScheduler, ): - await slave_scheduler.setup() + await nested_scheduler.setup() message = ComponentException( ComponentID("Test"), Exception("Test exception"), "test exception traceback" ) - await slave_scheduler.handle_message(message) - assert slave_scheduler.error.is_set() - assert slave_scheduler.component_error == message + await nested_scheduler.handle_message(message) + assert nested_scheduler.error.is_set() + assert nested_scheduler.component_error == message diff --git a/tests/system_tests/test_with_master_and_slave.py b/tests/system_tests/test_with_master_and_nested.py similarity index 100% rename from tests/system_tests/test_with_master_and_slave.py rename to tests/system_tests/test_with_master_and_nested.py