BB.Actuator.Server refuses commands in three cases today — unsupported payload, disarmed robot, and (once limit validation lands) a rate or duration outside the joint's limits. refuse/5 emits a [:bb, :actuator, :rejected] telemetry event and then calls command_reply/4:
defp command_reply(:call, reply, state, nil), do: {:reply, reply, state}
defp command_reply(_transport, _reply, state, nil), do: {:noreply, state}
So only set_position_sync/5 ever sees the error. set_position/4 (pubsub) and set_position!/4 (cast) discard it, and the caller carries on believing the joint is moving.
That's tolerable while refusals mean "disarmed" or "unsupported", since both are loud elsewhere. It gets worse once ordinary motion can be refused for violating a velocity or duration limit: the common path becomes one that fails invisibly.
Worth considering whether set_position/4 should be a call rather than a cast, so the caller learns the command was accepted before continuing. That trades latency for certainty, and the pubsub delivery exists precisely so orchestration and logging can observe commands — so it may want to stay a cast and gain a different mechanism instead.
Options, roughly:
- make
set_position/4 synchronous, accepting the round trip
- keep it asynchronous but publish a rejection message the caller can subscribe to
- log a warning in
refuse/5, so a refusal is at least visible in the logs
The last is worth doing regardless of which way the first is decided.
Context: came out of adding joint-limit validation to BB.Actuator.Server, where the concern is that refusing a Position command with an unmeetable duration would be less visible than the silent clamping it replaces.
BB.Actuator.Serverrefuses commands in three cases today — unsupported payload, disarmed robot, and (once limit validation lands) a rate or duration outside the joint's limits.refuse/5emits a[:bb, :actuator, :rejected]telemetry event and then callscommand_reply/4:So only
set_position_sync/5ever sees the error.set_position/4(pubsub) andset_position!/4(cast) discard it, and the caller carries on believing the joint is moving.That's tolerable while refusals mean "disarmed" or "unsupported", since both are loud elsewhere. It gets worse once ordinary motion can be refused for violating a velocity or duration limit: the common path becomes one that fails invisibly.
Worth considering whether
set_position/4should be a call rather than a cast, so the caller learns the command was accepted before continuing. That trades latency for certainty, and the pubsub delivery exists precisely so orchestration and logging can observe commands — so it may want to stay a cast and gain a different mechanism instead.Options, roughly:
set_position/4synchronous, accepting the round triprefuse/5, so a refusal is at least visible in the logsThe last is worth doing regardless of which way the first is decided.
Context: came out of adding joint-limit validation to
BB.Actuator.Server, where the concern is that refusing aPositioncommand with an unmeetabledurationwould be less visible than the silent clamping it replaces.