Advanced Metal Research
GitHub Contact AMR

Connect to a cell and run a program

On this page
  1. Before you start
  2. 1. Commission the cell in the repository
  3. 2. Write the cell catalogue
  4. 3. Start OLP with the catalogue
  5. 4. Select the cell
  6. 5. Home
  7. 6. Arm
  8. 7. Plan and Load
  9. 8. Play
  10. 9. Stop and disconnect
  11. When something is refused
  12. Related pages

This guide takes the OLP server from offline work to a real cell: you describe the cell in a catalogue, select it, then Home, Arm, Load and Play a planned weld program. Each step shows the UI action and the HTTP call behind it, so you can script it or debug it.

Try the whole sequence on the simulated cell first. The dev stack adds a local-simulation cell to the catalogue for you. See Run everything in simulation.

Warning

Energised motion. This moves the robot. RosieOS has no software E-stop: keep the hardware E-stop within reach and clear the cell before you arm. Jog, moves and Home are checked against joint limits only, not against collisions. See the safety model.

Before you start#

  • The cell host runs rosie-rt-core and rt-control with the mutual-TLS listener enabled. See Install rt-core on a cell host and Remote access (mTLS).
  • You have a client certificate for OLP from the cell's PKI, issued with remote-pki.sh issue-client. It carries exactly one rosie-pair:<pair_id> URI, and OLP refuses the connection if that pair differs from the catalogue's binding (session_principal_mismatch). See Provision certificates.
  • The weld planner is running and OLP can reach it (OFFLINE_PROGRAMMING_WELD_PLANNER_MOTION_ORIGIN). Load fetches programs from it.
  • The hardware E-stop has been tested this session.

1. Commission the cell in the repository#

OLP only drives a cell whose deployment manifest is in the repository, at rt-core/config/cells/<cell_id>.json. When it reads the catalogue, it compiles that manifest's machine config and takes the listener address, pair ID, pair revision and configuration digest from it. The catalogue can only repeat these values; it cannot override them.

The manifest's native node looks like this. Cells, machines and positioners describes every field.

rt-core/config/cells/cell-a.json (native node)JSON
{
  "rt_core": {
    "machine_config": "rt-core/config/machines/cell-a.json",
    "configuration_sha256": "<64 hex from rtctl compile>",
    "pair_id": "cell-a",
    "pair_revision": 1,
    "remote_listen": "127.0.0.1:8443"
  }
}

Check it with the repository validator:

cd rt-core
go test -count=1 ./config/cells -run TestRepositoryCompatibility

Note

In this release OLP resolves only the cell IDs it knows by name. The list is cellManifestName in offline-programming/v1/internal/targets/cells.go. To add a cell, add its manifest and add its ID to that function. An ID that is not listed fails with cell_id_unknown.

2. Write the cell catalogue#

The catalogue is a JSON file, schema offline-programming.cell-catalogue.v1, that lists the cells the operator can choose from. Unknown fields are refused.

olp-cells.jsonJSON
{
  "schema": "offline-programming.cell-catalogue.v1",
  "cells": [
    {
      "cell_id": "cell-a",
      "label": "Cell A",
      "role": "WELD CELL",
      "models": ["rosie_1400_v3"],
      "requested_mode": "real",
      "execution": {
        "address": "https://127.0.0.1:8443",
        "ca_file": "pki/ca.pem",
        "certificate_file": "pki/clients/olp-1.pem",
        "key_file": "pki/clients/olp-1-key.pem",
        "binding": {"pair_id": "cell-a", "revision": 1,
                    "configuration_sha256": "<64 hex from rtctl compile>"},
        "axis_mask": 511,
        "expected_backend": "ethercat"
      }
    }
  ]
}
FieldRequiredRule
cell_idyesUnique. Must be a commissioned cell (step 1).
label, roleyesNon-empty. Shown in the Cells dialog.
modelsyesExactly one known model: rosie_1400_v3, rosie_1420_v1, bench_one_motor_1to1 or bench_nine_motors_1to1. It must be the model the manifest's machine config compiles to.
requested_modeyesreal or simulation. At selection it must match what the cell reports: real for an ethercat backend, simulation for simulation.
execution.addressyeshttps://host:port, no path or query. Must equal https:// + the manifest's remote_listen.
execution.ca_file, certificate_file, key_fileyesPEM files. Relative paths resolve from the catalogue's directory. They must exist.
execution.binding.pair_id, revisionyesMust equal the manifest's pair_id and pair_revision
execution.binding.configuration_sha256noIf given, must equal the compiled digest. OLP fills it in from the compile either way.
execution.axis_maskyesThe axes OLP commands, 1–511, J1 = bit 0. Home, Arm and Load address exactly these axes.
execution.expected_backendyesethercat or simulation. Describe must report the same.

Because the address must match remote_listen, a cell pinned to 127.0.0.1:8443 is reachable only from the cell host itself or through a forwarded port.

A simulated cell served on the same machine uses a local entry instead. It needs the local binding file in OFFLINE_PROGRAMMING_RT_CORE_CONFIG (the dev stack writes it):

{"cell_id": "local-simulation", "label": "Local simulation", "role": "LOCAL SIMULATION",
 "models": ["rosie_1400_v3"], "requested_mode": "simulation", "execution": {"local": true}}

3. Start OLP with the catalogue#

export OFFLINE_PROGRAMMING_CELLS=/path/to/olp-cells.json
export OFFLINE_PROGRAMMING_WELD_PLANNER_MOTION_ORIGIN=http://127.0.0.1:8796
bash offline-programming/v1/start-offline-programming.sh

With the dev stack, set DEV_STACK_OLP_CELLS=/path/to/olp-cells.json instead. Its cells are added after local-simulation, and relative credential paths are resolved against your file.

The server checks the whole catalogue at startup and refuses to start on a bad entry, naming the rule: for example cell_endpoint_mismatch, cell_pair_mismatch, cell_configuration_mismatch, cell_credentials_missing or cell_model_mismatch. A cell whose manifest names no machine config stays listed but unavailable, with cell_configuration_missing. On success it prints:

dense execution: choose a machine; joint and Cartesian jog share its armed control session

4. Select the cell#

In the UI, open Cells and choose the cell. Over HTTP:

OLP=http://127.0.0.1:8794/api/offline-programming/v1
curl -s $OLP/targets | jq '.targets[] | {cell_id, backend, simulation, reason}'
curl -s -X POST $OLP/targets/select -d '{"cell_id":"cell-a","model_id":"rosie_1400_v3"}'

Selection connects to rt-control, runs Describe and checks it against the catalogue: backend, configuration digest, contract version, axis count. It takes no authority. If another cell was selected, OLP stops and releases it first.

Then confirm you have the machine you expect:

curl -s $OLP/dense-execution/status | jq '{target, robot_cell}'
curl -s $OLP/dense-execution/capabilities | jq '.rt_core.description | {backend, configuration_sha256}'

target.backend must say ethercat for a real cell. robot_cell.valid must be true, and robot_cell.model_id and robot_description_sha256 identify the robot description the machine was compiled with.

Every later request that can move the robot must carry X-RT-Target-Generation: <target.selection_generation> and X-RT-Target-Cell: <target.cell_id>. If anyone selects another cell in between, the request is refused with target_changed. The UI does this for you. See Target fencing. For the commands below:

STATUS=$(curl -s $OLP/dense-execution/status)
FENCE=(-H "X-RT-Target-Generation: $(echo "$STATUS" | jq -r .target.selection_generation)"
       -H "X-RT-Target-Cell: $(echo "$STATUS" | jq -r .target.cell_id)")

5. Home#

Clear the cell. In the UI, press Home. Over HTTP, with FENCE set to the two headers:

curl -s -X POST $OLP/dense-execution/home "${FENCE[@]}"

Home acquires the rt-control lease, runs native Home on every axis in the catalogue's axis_mask, and returns when Home is valid on all of them. Send {"axes": [0, 1]} to home only some axes. Home moves the robot but never arms it.

6. Arm#

curl -s -X POST $OLP/dense-execution/arm "${FENCE[@]}" -d '{"armed": true}'

Arm enables and arms the drives and waits until every configured axis reports ready. If an execution fault is latched and a reset clears it, Arm resets it first. From now on, while OLP holds the lease:

  • Send a heartbeat at least every 5 s: POST /dense-execution/heartbeat with {"session_id": "<status.session.id>"}. The UI does this while its tab is open. Without it, OLP stops the machine with ui_heartbeat_lost.
  • Any refused or failed operation makes OLP run Stop and Release before it answers.

7. Plan and Load#

Plan the program in OLP against this cell. While a cell is selected, the plan request carries the cell's own calibration, and the planner stamps the robot and cell identity into the .rdt. See Program a weld from CAD.

Then Load it. In the UI, choose the Dry run · process outputs off run mode and press Load. Over HTTP, send the plan identity from the plan response:

curl -s -X POST $OLP/dense-execution/load "${FENCE[@]}" -d '{
  "trajectory_digest": "sha256:…", "plan_id": "bracket_fillet:3f1c0a9d2b7e",
  "program_id": "bracket_fillet", "program_digest": "sha256:…",
  "manifest_revision": 1, "plan_revision": 3, "dry_run": true}'

Load refuses before any byte reaches the robot unless:

  1. the .rdt exists in the planner's store, which it only does if every segment passed the verifier
  2. its header matches the identity you sent
  3. the plan's robot description and cell calibration match the machine
  4. every configured axis has valid Home

It then uploads the program with prepare_program, and rt-control checks positions, velocities and continuity against the live machine. The answer carries a session_id and the trajectory's segments.

dry_run: true removes the torch bits first. A program that still needs process outputs is refused: torch output is not supported in this release. See Process I/O and sensing.

8. Play#

Replay the program in simulation first if you have not: Simulate in OLP plays the exact bytes in the viewer or on the local simulator. Then:

curl -s -X POST $OLP/dense-execution/play "${FENCE[@]}" -d '{"session_id": "ds-9b1e4f07a2c3"}'

Play waits until the machine is armed and ready, then starts the program. Keep the heartbeat going. Watch status.session.state (playing) and status.rt_core.status for execution progress. The first point may start with a short alignment ramp from the held position; see Starting from rest.

9. Stop and disconnect#

Press STOP in the UI, or:

curl -s -X POST $OLP/dense-execution/stop

Stop needs no fence and no body. It inhibits outputs at once, then releases the lease. Retry it until it answers "ok": true. If a Stop or Release cannot be confirmed, OLP blocks further motion with rt_core_inhibited until a Stop succeeds.

A Stop receipt is not proof of standstill. Watch the robot. The STOP button is software; the hardware E-stop is the emergency stop.

To give up the cell, disarm ({"armed": false} on /arm, which runs Stop and Release) and clear the selection with POST /dense-execution/target and {"cell": ""}.

When something is refused#

CodeStepWhat to do
cell_id_unknown3, 4The ID is not a commissioned cell known to OLP. See step 1.
cell_endpoint_mismatch, cell_pair_mismatch, cell_pair_revision_mismatch, cell_configuration_mismatch3, 4The catalogue disagrees with the manifest, or the running rt-control serves another configuration. Recompile and update the pins.
cell_mode_mismatch, cell_backend_mismatch4The cell reports simulation where you asked for real, or the reverse
cell_unreachable, rt_core_transport_lost4OLP cannot reach the listener. Check the address, the port forward and the certificates.
session_principal_mismatch4The client certificate's pair differs from the binding
target_changed5–8Another client selected a cell. Read the status again and use the new generation.
control_already_owned5–8Another controller (a pendant, a motion server, another OLP) holds rt-control. Release it there.
home_required7Home the machine first
robot_cell_mismatch, robot_cell_missing7The plan was made for another robot description or cell calibration, or before the cell was checked. Replan against this cell.
robot_cell_unavailable7The machine cannot say which robot it is. Check its compiled configuration.
dense_blob_not_found7The planner's store no longer has that digest. Replan.
dense_identity_mismatch7The identity you sent differs from the .rdt header
program_identity_or_process_mismatch7The program still needs process outputs. Load with dry_run: true.
native_limit_exceeded, native_segment_rate_exceeded, outside_limits_outward7The program leaves the machine's limits. limit_violation in the answer names the segment, sample and axis.
not_ready, jog_not_ready6–8An axis is not ready. rt_core.status shows the first failing gate per axis.
ui_heartbeat_lost6–8The client stopped sending heartbeats. Arm again.
rt_core_inhibitedanyRetry Stop until it succeeds

All codes are in the Offline programming HTTP API and Error codes.