UniteLabs
Reference

Repeated Modules

Model a device that contains the same module several times, such as pump channels, doors, or plate slots.

Pick the pattern by asking whether the instances work independently:

The instances are…UseExample
Independent modules that each do their own workOne parametrized feature, registered per instancePump channels, pipette channels, bioreactor chambers
Interchangeable positions on one resourceOne feature with a slot parameterSlots in a centrifuge or plate hotel

For the reasoning behind this choice, see Designing Features.

Register one feature per independent module

Write one class and pass the instance index to its constructor. Inject the index into the feature name through name=, placing it before the role suffix:

features/pipette_service/pipette_service.py
class PipetteService(sila.Feature):
    """Aspirate and dispense with a single pipetting channel."""

    def __init__(self, protocol: "LiquidHandlerProtocol", channel: int):
        super().__init__(
            originator="io.unitelabs",
            category="liquidhandling",
            name=f"Pipette {channel} Service",
        )
        self.protocol = protocol
        self.channel = channel
__init__.py
for channel in range(1, 9):
    app.register(PipetteService(protocol, channel=channel))

Clients see Pipette1Service through Pipette8Service, each addressing one channel. Don't hardcode the index into separate classes, and don't put it after the suffix: Door1Controller, not DoorController1.

Use a slot parameter for interchangeable positions

When the positions belong to one logical resource, expose one feature and take the slot as a parameter, constrained to the valid range:

class AccessCompartmentController(sila.Feature):
    """Open and close the access compartments of the centrifuge rotor."""

    @sila.ObservableCommand()
    async def open(
        self,
        slot: typing.Annotated[int, sila.constraints.MinimalInclusive(1), sila.constraints.MaximalInclusive(4)],
        *,
        status: sila.Status,
    ) -> None:
        """
        Open the access compartment at the given slot.

        Args:
          Slot: The slot to open.
        """

This replaces Door1Controller.open(), Door2Controller.open(), and so on, which would split one concern across features that depend on each other. If the number of slots depends on the device, add the maximum at startup instead. See Constraints and Units.

Last updated