Skip to content

Commit

Permalink
Allow disjoing components (#73)
Browse files Browse the repository at this point in the history
* Include disjoint components in (Inverse)Wiring

Co-authored-by: Garry O'Donnell <garry.o'[email protected]>

* Modify tests to reflect new expected behaviour

Co-authored-by: Garry O'Donnell <garry.o'[email protected]>

* Add example device and config for isolated device
  • Loading branch information
abbiemery authored Aug 2, 2022
1 parent e2e6c8f commit db5cde0
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 2 deletions.
3 changes: 3 additions & 0 deletions examples/configs/iobox.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- examples.devices.iobox.IoBox:
name: MrBox
inputs: {}
100 changes: 100 additions & 0 deletions examples/devices/iobox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from dataclasses import dataclass

from tickit.adapters.composed import ComposedAdapter
from tickit.adapters.interpreters.command import CommandInterpreter, RegexCommand
from tickit.adapters.servers.tcp import TcpServer
from tickit.core.adapter import Server
from tickit.core.components.component import Component, ComponentConfig
from tickit.core.components.device_simulation import DeviceSimulation
from tickit.core.device import Device, DeviceUpdate
from tickit.core.typedefs import SimTime
from tickit.utils.byte_format import ByteFormat
from tickit.utils.compat.typing_compat import TypedDict


class IoBoxDevice(Device):
"""An isolated toy device which stores a message.
The device takes no inputs and produces no outputs. It interacts exclusively with
an adapter which sets incoming messages to it and reads stored messages from it.
"""

#: An empty typed mapping of device inputs
Inputs: TypedDict = TypedDict("Inputs", {})
#: A typed mapping containing the current output value
Outputs: TypedDict = TypedDict("Outputs", {})

def __init__(self, inital_value: str = "Hello") -> None:
"""The IoBox constructor, sets intial message value."""
self.message = inital_value

def update(self, time: SimTime, inputs: Inputs) -> DeviceUpdate[Outputs]:
"""The update method which produces an output mapping containing the observed value.
For this device the DeviceUpdate produces no outputs.
Args:
time (SimTime): The current simulation time (in nanoseconds).
inputs (State): A mapping of inputs to the device and their values.
Returns:
DeviceUpdate[Outputs]:
The produced update event which contains the observed value, in this
case nothing is provided. The device never requests a callback.
"""
return DeviceUpdate(IoBoxDevice.Outputs(), None)


class IOBoxAdapater(ComposedAdapter):
"""A Composed adapter for setting and getting the device property."""

device: IoBoxDevice

def __init__(
self,
server: Server,
) -> None:
"""Constructor of the IoBoc Adapter, builds the configured server.
Args:
server (Server): The immutable data container used to configure a
server.
"""
super().__init__(
server,
CommandInterpreter(),
)

@RegexCommand(r"m=([a-zA-Z0-9_!.?-]+)", interrupt=True, format="utf-8")
async def set_message(self, value: str) -> None:
"""Regex string command which sets the value of the message.
Args:
value (str): The new value of the message.
"""
self.device.message = value

@RegexCommand(r"m\?", format="utf-8")
async def get_message(self) -> bytes:
"""Regex string command which returns the utf-8 encoded value of the message.
Returns:
bytes: The utf-8 encoded value of the message.
"""
return str(self.device.message).encode("utf-8")


@dataclass
class IoBox(ComponentConfig):
"""IO Box you can talk to over TCP."""

host: str = "localhost"
port: int = 25565
format: ByteFormat = ByteFormat(b"%b\r\n")

def __call__(self) -> Component: # noqa: D102
return DeviceSimulation(
name=self.name,
device=IoBoxDevice(),
adapters=[IOBoxAdapater(TcpServer(self.host, self.port, self.format))],
)
4 changes: 4 additions & 0 deletions tests/core/management/test_event_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ def wiring_struct():
ComponentID("Mid1"): {
PortID("Mid1>1"): {ComponentPort(ComponentID("In1"), PortID("In1<1"))}
},
ComponentID("In1"): {},
}


@pytest.fixture
def inverse_wiring_struct():
return {
ComponentID("Out1"): {},
ComponentID("Out2"): {},
ComponentID("Mid1"): {
PortID("Mid1<1"): ComponentPort(ComponentID("Out1"), PortID("Out1>1")),
PortID("Mid1<2"): ComponentPort(ComponentID("Out2"), PortID("Out2>1")),
Expand Down Expand Up @@ -136,6 +139,7 @@ def test_event_router_output_components(event_router: EventRouter):

def test_event_router_component_tree(event_router: EventRouter):
assert {
"In1": set(),
"Out1": {"Mid1"},
"Out2": {"Mid1", "In1"},
"Mid1": {"In1"},
Expand Down
6 changes: 4 additions & 2 deletions tickit/core/management/event_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def from_inverse_wiring(cls, inverse_wiring: "InverseWiring") -> "Wiring":
"""
wiring: Wiring = cls()
for in_dev, in_ios in inverse_wiring.items():
wiring[in_dev]
for in_io, (out_dev, out_io) in in_ios.items():
wiring[out_dev][out_io].add(ComponentPort(in_dev, in_io))
return wiring
Expand All @@ -82,7 +83,7 @@ def __init__(self, wiring: Optional[Inverse_Wiring_Struct] = None) -> None:
Defaults to None.
"""
_wiring = (
{dev: DefaultDict(None, io) for dev, io in wiring.items() if io}
{dev: DefaultDict(None, io) for dev, io in wiring.items()}
if wiring
else dict()
)
Expand All @@ -101,6 +102,7 @@ def from_wiring(cls, wiring: Wiring) -> "InverseWiring":
"""
inverse_wiring: InverseWiring = cls()
for out_dev, out_ids in wiring.items():
inverse_wiring[out_dev]
for out_io, ports in out_ids.items():
for in_dev, in_io in ports:
inverse_wiring[in_dev][in_io] = ComponentPort(out_dev, out_io)
Expand Down Expand Up @@ -174,7 +176,7 @@ def output_components(self) -> Set[ComponentID]:
Returns:
Set[ComponentID]: A set of components which provide outputs.
"""
return set(self.wiring.keys())
return {component for component, port in self.wiring.items() if port}

@cached_property
def input_components(self) -> Set[ComponentID]:
Expand Down

0 comments on commit db5cde0

Please sign in to comment.