Step 2 - Build Gateway Subsystem
The cleaner robot tutorial provides a 2D arena simulator. The simulator is not the robot gateway that users should model. It is the environment that the robot runs against.
The Librux-facing lower boundary is a simulator-backed gateway subsystem named
gateway.device_gateway.
gateway.device_gateway owns the runtime bridge to the provided simulator.
- drive commands for the mobile base
- arm extension commands
- digital I/O commands for gripper and disposal outputs
- scan requests for the tutorial radar
It does not own simulator administration. Reset, trash spawn, and browser world state come from the provided simulator support API, not from the robot gateway.
The tutorial uses one compact gateway so the concept stays visible. A real robot could use one servo gateway for all motors, separate actuator and sensor gateways, or a vendor gateway that exposes several standard device API contracts.
The simulator process is separate.
tutorials/simulator/arena_server.py
It is not a Librux subsystem. run_sim_world.py starts only the simulator and
browser. This step starts gateway.device_gateway as one managed package with
librux launch run. Full deployment orchestration comes later, after the
platform and application exist.
Run This Stage
From the installed root.
cd /opt/librux
librux launch run tutorials/subsystems/gateway.device_gateway \
--instance gateway.device_gateway \
--set simulator.url=http://127.0.0.1:8092 \
--detach
Keep the simulator world from Step 1 running while this command starts. The gateway waits for the simulator API during startup because it is the robot-facing adapter over that already-running world.
Expected terminal output shape.
Launched package tutorial.robot_cleaner.device_gateway
mode: detached
instance: gateway.device_gateway
Open the browser with the same host rule from Step 1. For a local browser and runtime, use the following.
http://127.0.0.1:8091/
For a remote runtime host, use the following.
http://<runtime-host>:8091/
The robot still does not move from the browser. That is correct. This stage adds the gateway boundary only; there is no controller or application yet.
1. Open The Gateway Manifest
nano tutorials/subsystems/gateway.device_gateway/subsystem.yaml
The manifest declares robot-facing standard interfaces.
subsystem:
name: gateway.device_gateway
role: gateway
provides:
actuator_servo:
contract: api.actuator.servo.v1
digital_io:
contract: api.io.digital.output.v1
perception_query:
contract: api.perception.query.v1
These are public Librux contracts. The simulator remains an implementation detail behind them. A real gateway would translate the same public surfaces to EtherCAT, CAN, serial, a vendor SDK, or a simulator API. The endpoint names come from the referenced specs; the manifest does not repeat them.
2. Read The Gateway Code
nano tutorials/subsystems/gateway.device_gateway/device_gateway.py
Focus on on_initialize().
self.register_control_endpoint("servo_joint", self._servo_joint)
self.register_procedure("set_digital_output", self._set_digital_output)
self.register_procedure("get_digital_output", self._get_digital_output)
self.register_procedure("scan", self._scan)
self.register_procedure("front", self._front)
self.register_procedure("nearest", self._nearest)
The manifest and code must agree. The manifest says which contracts the gateway
claims to provide; the code registers the runtime endpoints required by those
contracts. servo_joint is the actuator-servo Control surface from
api.actuator.servo.v1. It is intentionally registered only as a
Control endpoint. Librux rejects a subsystem that exposes the same spec endpoint
as both Control and Procedure because that hides timing mistakes.
The 2D arena server and state logic live in the provided support modules.
nano tutorials/simulator/arena_server.py
nano tutorials/simulator/arena.py
You normally do not modify this simulator while learning Librux. Treat it like a small IsaacSim, Gazebo, vendor SDK, or hardware environment that already exists.
3. Understand Simulator State
The browser reads simulator state directly from the provided simulator API. That API is tutorial infrastructure, not a robot gateway contract. It carries.
robot pose
trash list
scan result
score
world size
This gives the cleaner UI a single source of truth for drawing the arena.
4. Understand The Gateway Endpoints
Components never edit simulator state directly. They call robot-facing gateway endpoints.
| Endpoint | Called by | Effect |
|---|---|---|
servo_joint control |
timing-aware actuator clients | sends base or arm actuator commands through the servo gateway surface |
set_digital_output procedure |
gripper component and compound subsystem | toggles gripper/disposal outputs |
get_digital_output procedure |
gripper component | reads a digital output state |
scan procedure |
perception component | requests a bounded object scan |
front procedure |
perception component | requests a forward field-of-view scan |
nearest procedure |
perception component | requests the nearest visible object |
Reset, spawn, and world-state sync are not listed because they belong to the provided simulator API. The world is visible, but it is not the robot-facing gateway contract.
5. Validate The Manifest Shape
Deployment validation will later validate this gateway as part of the whole robot. For now, read the file and check the registered endpoint names against the contracts.
rg -n "register_control_endpoint|register_procedure" tutorials/subsystems/gateway.device_gateway/device_gateway.py
The names should match subsystem.yaml.
Cleaner Robot Graph After This Step
The simulator can exist before the gateway starts. The Librux gateway subsystem is the adapter that exposes robot-facing procedures to future controls.
Verify
Inspect this stage.
librux subsystem status gateway.device_gateway
Then inspect the platform plan that will later assign the same gateway package to three robot slots.
librux deploy plan tutorials/deployments/stages/robot.deployment.yaml
Look for the following.
gateway.device_gateway [gateway] slots=actuator_gateway:actuator_servo,digital_output_gateway:digital_io,perception_gateway:perception_query
Continue to Step 3 - Add Component Subsystems.