Files
dora-ulite6/dora-node.yml
T
cristhian a2bf59065d add dora-node.yml manifest and a service example dataflow
The manifest is the only contract a consumer has, since no source package
is shared between node repositories, and dora validate checks a dataflow
against it without running the node — so CI needs no robot.

request and response are deliberately untyped. They carry request_id, and
dora defines that as the marker for a pattern-correlated port where
several Arrow schemas legitimately flow, one per service; the runtime
skips type checks on those messages, so annotating them would claim a
contract neither side keeps.

examples/ carries its own manifest because dora resolves a node's
manifest by walking up from its path, and a manifest applies only when
<manifest dir>/<entrypoint> names the same file as the dataflow's path.
Without it the example caller would be matched against the arm node's
manifest and strict validation would fail on the entrypoint mismatch.
2026-09-12 11:47:06 -03:00

129 lines
5.2 KiB
YAML

# Node manifest: the typed contract a consumer wires against.
#
# No source package is shared between node repositories, so this file and its
# type URNs are the only contract a consumer has. `dora validate` checks a
# dataflow against it without running the node, so CI needs no robot.
#
# The `request` and `response` ports are deliberately **untyped**. They carry
# dora's pattern-correlation metadata (`request_id`), and dora defines that as
# the marker for a port where several Arrow schemas legitimately flow — one per
# service — distinguished by metadata rather than by a fixed Arrow type. The
# runtime skips type checks on such messages, so annotating them would claim a
# contract neither side keeps.
apiVersion: 1
name: dora-ulite6
namespace: liria
description: UFactory Lite6 arm node serving motion and control services, and publishing joint and tool pose
categories: [robot, actuator]
keywords: [ufactory, lite6, xarm, robot-arm, manipulator, service, vacuum, gripper]
runtime: python
entrypoint: dora-ulite6
# `dora build --uv` / `dora run --uv` rewrites this to `uv pip install .` and
# gives the node its own managed venv under `.dora/python-envs/`, so the
# dependencies it sees at runtime are the ones installed here.
build: pip install .
dora: ">=1.0"
inputs:
tick:
type: std/core/v1/UInt64
required: true
description: >-
Each tick publishes whatever the arm threads produced since the last one.
It does not drive the arm — the tick rate is the publication rate, and
TELEMETRY_INTERVAL_MS is how often the arm is actually read. Connect to
dora/timer/millis/N.
request:
required: true
description: >-
A service request: a one-row struct {service, ...arguments}, with the
caller's `request_id` in the metadata. Services: move_to, move_joints,
home, home_and_clear, clear, recover, stop, freedrive, grasp, release,
get_state. Answered on `response` under the same request_id, exactly once.
A motion takes `wait`: true (the default) holds the caller until the arm
stops; false answers at once with an acknowledgement and reports the rest
on `motion_state`.
jog:
required: false
description: >-
A continuous velocity command: a one-row struct {velocity} of 6 numbers
[vx, vy, vz, wx, wy, wz] in mm/s and deg/s. A topic rather than a service
because it expires on the controller's own TTL and a sender must keep
sending; nothing is published in answer. It drives the arm, so the first
non-zero one preempts a motion in play and robot_state turns to moving.
outputs:
response:
description: >-
The answer to one service request, under its `request_id`: a struct
{ok, service, message, ...extras}. get_state adds connected, ready,
tool_engaged, freedrive, pose and joints. Exactly one response per
request, including for one that was refused or preempted.
motion_state:
description: >-
Where the current motion stands, published on every change: a struct
{request_id, service, state, message} with state one of accepted, moving,
done, failed, preempted. The request_id is in the fields rather than the
metadata because this is the arm's state, not an answer to a request. A
new motion preempts the one in play, so there is only ever one.
joint_pose:
type: std/core/v1/Float32
description: The six joint angles in degrees, base to wrist.
tcp_pose:
type: std/core/v1/Float32
description: >-
The mounted tool's tip as [x, y, z, roll, pitch, yaw] in mm and degrees,
in the robot base frame. With TOOL_TYPE none this is the flange itself.
robot_state:
type: std/core/v1/String
description: >-
One of connecting, ready, moving, recovering, unavailable. Published on
every change, never repeated.
env:
ROBOT_IP:
type: string
description: Controller address. Defaults to 192.168.1.192.
TOOL_TYPE:
type: string
description: What is mounted at the flange - none, vacuum or gripper. Defaults to none.
TOOL_OFFSET_MM:
type: float
description: Extra reach past the flange, added to the mounted tool's own. Defaults to 0.
DEFAULT_SPEED:
type: float
description: Cartesian speed in mm/s for a move_to with no speed of its own. Defaults to 30.
DEFAULT_JOINT_SPEED:
type: float
description: Joint speed in deg/s for a move_joints with no speed of its own. Defaults to 20.
TELEMETRY_INTERVAL_MS:
type: int
description: >-
How often the reporter reads the arm, and so how often telemetry is
produced. Keeps running through a motion. Defaults to 100.
RECONNECT_INTERVAL_S:
type: float
description: How long to wait before trying an unreachable arm again. Defaults to 5.
INIT_MODE:
type: string
description: Where the arm goes on startup - none, home or pose. Defaults to none.
example: |
- id: ulite6
git: ssh://git@gitea.liria.cl:5001/liria/dora-ulite6.git
tag: v0.3.0
build: pip install .
path: dora-ulite6
inputs:
tick: dora/timer/millis/100
request: planner/request
outputs:
- response
- joint_pose
- tcp_pose
- robot_state
env:
ROBOT_IP: "192.168.1.192"
TOOL_TYPE: "vacuum"