serve the web console from the node and drop the websocket bridge
The browser reached this node through `dora-websocket-transceiver`: a second node, a second port, and a round trip through the dataflow for every button press, to carry a UI only this node can interpret. The node now serves the page and holds the browsers' sockets itself on one port, so the console is one URL and a consumer wires nothing for it. `dora_teleop/server.py` runs aiohttp on a thread of its own and meets the control loop at two non-blocking points: a bounded queue the tick drains, and a snapshot handed back. Both queues are bounded deliberately — a flood is dropped rather than grown, and a slow browser loses stale snapshots rather than the newest — because nothing a browser does may stall the thread that stops the arm. A port already in use stops the node rather than leaving it announcing a console nobody can open. The web deadman moved but did not change: it was a `clients` message from the bridge and is now the open-socket count read each tick. One hop fewer, same meaning. Contract, so 0.2.0 -> 0.3.0: `ui_command` and `clients` are gone. `ui_state` stays published for `dora topic echo` and the bench, with no browser reading it. New WEB_HOST and WEB_PORT (7000; 0 opens no port). The console itself is laid out like the arm's own pendant — two rings quartered into arc sectors you hold, each with its out-of-plane axis as pills above. Directions and signs match xArm Studio on the controller, `Y+` on the left included: two consoles driving one arm that disagree about which way is +Y is a trap, not a preference. Also sends a velocity only when it changes, repeating an unchanged one on JOG_KEEPALIVE_S (0.2 s) purely to beat the consumer's own expiry. Repeating every tick made `dora-ulite6` re-plan the same motion twenty times a second. That turned out not to be the whole cause of the stepping an operator felt — the rest was fixed in dora-ulite6 v0.3.1, pinned here — but sending 18 messages where 83 went before is worth keeping on its own. `dora-joy` moves to v0.1.1: v0.1 is an annotated tag, and dora 1.0.1 records the tag object's id as the commit hash and then rejects its own checkout for sitting on the peeled commit. Neither dataflow could be built before this.
This commit is contained in:
+43
-27
@@ -5,7 +5,7 @@
|
||||
# dataflow against it without running the node, so CI needs neither a gamepad
|
||||
# nor a robot.
|
||||
#
|
||||
# `joy`, `jog`, `request`, `response`, `motion_state` and the two `ui_` ports are
|
||||
# `joy`, `jog`, `request`, `response`, `motion_state` and `ui_state` are
|
||||
# deliberately **untyped**. dora treats a port carrying `request_id` as
|
||||
# pattern-correlated and skips type checks on it, several Arrow schemas
|
||||
# legitimately flow over each — one per service, one per console message — and
|
||||
@@ -30,7 +30,8 @@ dora: ">=1.0"
|
||||
inputs:
|
||||
tick:
|
||||
description: >-
|
||||
Drives everything time-based: re-sending the held velocity, expiring a
|
||||
Drives everything time-based: noticing a changed velocity, repeating the
|
||||
held one, expiring a
|
||||
source that has gone quiet, and publishing the console's telemetry. Use 50
|
||||
ms. Slower than about 100 ms and the arm node's own velocity expiry starts
|
||||
firing between ticks, which an operator feels as the arm stuttering.
|
||||
@@ -53,20 +54,6 @@ inputs:
|
||||
concerned.
|
||||
|
||||
Leave it unwired for a browser-only console. The node says so and starts.
|
||||
ui_command:
|
||||
required: false
|
||||
description: >-
|
||||
One message from the web console, as the JSON string the WebSocket bridge
|
||||
publishes on `data_out`. Wire it from the bridge. A message this node does
|
||||
not recognise is dropped rather than refused, so a page from a newer build
|
||||
cannot take the arm down.
|
||||
clients:
|
||||
required: false
|
||||
description: >-
|
||||
Connect and disconnect events from the WebSocket bridge, as
|
||||
`{connected, event}`. **This is the browser's deadman.** Without it a tab
|
||||
closing mid-jog leaves the arm travelling until the arm node's own expiry
|
||||
catches up, because a socket that went away sends nothing to say so.
|
||||
response:
|
||||
required: false
|
||||
description: >-
|
||||
@@ -105,11 +92,13 @@ outputs:
|
||||
second and radians per second — in the frame named by `frame`, currently
|
||||
always `base`.
|
||||
|
||||
Re-sent every tick while a source is driving, and followed by exactly one
|
||||
all-zero Twist when it stops — once, not repeatedly, because a stream of
|
||||
zeroes keeps a consumer believing a jog is live. There is no answer to a
|
||||
Twist, ever: it expires on the consumer's own TTL and the sender keeps
|
||||
sending.
|
||||
Sent when the velocity **changes**, and otherwise repeated every
|
||||
`JOG_KEEPALIVE_S` so the consumer's own expiry never fires mid-motion — a
|
||||
velocity is a set-point a consumer holds, not a frame in a sequence, and a
|
||||
consumer that re-plans on every message moves in steps if it is fed one
|
||||
every tick. A stop is a change: exactly one all-zero Twist, not repeatedly,
|
||||
because a stream of zeroes keeps a consumer believing a jog is live. There
|
||||
is no answer to a Twist, ever: it expires on the consumer's own TTL.
|
||||
|
||||
Both control sources are normalised to a fraction of full speed before
|
||||
anything is scaled, so a gamepad and the web console asking for the same
|
||||
@@ -137,9 +126,13 @@ outputs:
|
||||
no standard equivalent yet and go only here.
|
||||
ui_state:
|
||||
description: >-
|
||||
The console's telemetry snapshot, as a JSON string for the WebSocket
|
||||
bridge to broadcast. Also carries error toasts. Every key in it is read by
|
||||
name in the console's page, so the two change together.
|
||||
The console's telemetry snapshot, as a JSON string. Also carries error
|
||||
toasts. Every key in it is read by name in the console's page, so the two
|
||||
change together.
|
||||
|
||||
The browsers get this straight from the node's own server and do not read
|
||||
this port; it is published for anyone else watching — `dora topic echo`,
|
||||
a recorder, a second screen — and costs nothing when nobody is.
|
||||
|
||||
# Reserved, not implemented. A joint-space robot — a lerobot SO-101, say — takes
|
||||
# joint targets rather than a Cartesian velocity, and no amount of scaling turns
|
||||
@@ -190,6 +183,16 @@ env:
|
||||
How long the owning source may go quiet before the arm is stopped. Keep it
|
||||
**below** the arm node's VELOCITY_COMMAND_TTL_S (0.5 s by default), which
|
||||
is the controller-side backstop behind this one. Defaults to 0.4.
|
||||
JOG_KEEPALIVE_S:
|
||||
type: float
|
||||
default: 0.2
|
||||
description: >-
|
||||
How often to repeat a velocity that has not changed. Must be **above 0 and
|
||||
below JOG_TTL_S**, which in turn sits below the arm's own
|
||||
VELOCITY_COMMAND_TTL_S — so the repeat outpaces the expiry it exists to
|
||||
beat. Raising it sends fewer messages and asks the controller to re-plan
|
||||
less; too high and the arm stops between repeats. A change is never
|
||||
delayed by it. Defaults to 0.2.
|
||||
JOY_TTL_S:
|
||||
type: float
|
||||
default: 0.2
|
||||
@@ -210,11 +213,26 @@ env:
|
||||
description: >-
|
||||
How often to call `get_state` for what telemetry does not carry: whether
|
||||
the tool is engaged and whether freedrive is on. Defaults to 0.5.
|
||||
WEB_HOST:
|
||||
type: string
|
||||
default: "0.0.0.0"
|
||||
description: >-
|
||||
The address the built-in web console binds to. Defaults to `0.0.0.0`,
|
||||
because the operator is rarely sitting at the machine wired to the arm.
|
||||
Set `127.0.0.1` where they are, or where the network is not yours.
|
||||
WEB_PORT:
|
||||
type: int
|
||||
default: 7000
|
||||
description: >-
|
||||
The port the console is served on — the page and the browsers' WebSocket
|
||||
both, so there is one URL and nothing to keep in step. **Set it to 0 to
|
||||
open no port at all**, which is what a pad-only dataflow wants. Defaults
|
||||
to 7000.
|
||||
|
||||
example: |
|
||||
- id: teleop
|
||||
git: ssh://git@gitea.liria.cl:5001/liria/dora-teleop.git
|
||||
tag: v0.1.0
|
||||
tag: v0.3.0
|
||||
build: pip install .
|
||||
path: dora-teleop
|
||||
inputs:
|
||||
@@ -223,8 +241,6 @@ example: |
|
||||
source: joy/joy
|
||||
queue_size: 1
|
||||
queue_policy: drop_oldest
|
||||
ui_command: ws-transceiver/data_out
|
||||
clients: ws-transceiver/clients
|
||||
response: ulite6/response
|
||||
motion_state: ulite6/motion_state
|
||||
robot_state: ulite6/robot_state
|
||||
|
||||
Reference in New Issue
Block a user