openvibers/openvibe.link Commits

Node: speak OpenVibe.Bot's real device protocol (plan T15 follow-up B) (#2)

OP OpenVibers committed 2 Oct 2026 · 2a28cc3406 · 2 files, +7 −7

local checks ✗ 0/2 · CI ✓ 9/9 · review SHIP by DeepSeek harness

https://github.com/OpenVibers/OpenVibe.Node/pull/2

Changes

ddiff --git a/internal/node/node.go b/internal/node/node.go
iindex 2ed526b..3bce9ae 100644
---- a/internal/node/node.go
++++ b/internal/node/node.go
@@@ -76,7 +76,8 @@ type Node struct {
// configured is set by the first config on a connection; until then only halt runs, so a latch the owner set
// while the link was down is applied before any motion.
configured bool
-- robotIDs []string // from this connection's hello
++
++ robotIDs []string // the robots this device serves, from the connection's hello
}
// New builds a Node. Run starts it.
@@@ -443,19 +444,18 @@ func (n *Node) pollLatch(ctx context.Context) {
// sendEstopState reports the device's latch. Bot reads latched:true as "this device is stopped" and latches the robot
// (so the local kill switch shows on the panel and the gate stops sending motion); latched:false is a report only
--// and never clears the owner's latch.
++// and never clears the owner's latch. robot_id names the robot when the device serves exactly one.
func (n *Node) sendEstopState() {
if !n.reporting() {
return
}
-- st := n.latch.State()
-- // robot_id names the robot only when the device serves exactly one; a bridge for several leaves it out.
-- var robot string
n.mu.Lock()
++ var robot string
if len(n.robotIDs) == 1 {
robot = n.robotIDs[0]
}
n.mu.Unlock()
++ st := n.latch.State()
n.send(protocol.EstopState{Latched: st.Stopped(), By: "device", At: time.Now().UTC().Format(time.RFC3339Nano),
RobotID: robot, LocalStop: st.Local, Reason: st.RemoteReason})
}
ddiff --git a/internal/node/node_test.go b/internal/node/node_test.go
iindex f6ee8e2..7023dc2 100644
---- a/internal/node/node_test.go
++++ b/internal/node/node_test.go
@@@ -227,8 +227,8 @@ func TestPairConnectAndStatus(t *testing.T) {
for _, f := range e.conn.Frames() {
if f.Type == protocol.TypeEstopState {
sawEstopState = true
-- if f.Msg.(protocol.EstopState).Latched {
-- t.Fatal("latched on a fresh device")
++ if m := f.Msg.(protocol.EstopState); m.Latched || m.RobotID != fakebot.RobotID {
++ t.Fatalf("estop_state on a fresh device: %+v", m)
}
if r := f.Msg.(protocol.EstopState).RobotID; r != fakebot.RobotID {
t.Fatalf("estop_state robot_id %q on a one-robot device", r)