Identifying Success and Failure
Imagine writing a software package that sends a single rpc_request
node once every five minutes. You could easily track the status of your RPC request by simply waiting for the next rpc_ok
or rpc_error
node from the device.
However, what if we are required to send 100 requests in a one-minute timeframe? Identifying success or failure is no longer a trivial task. This is especially true for operations that result in partial failure, where only a portion of the requests succeed.
When using the HTTP protocol, the solution is simple - open a connection, start a request, and then close the connection when finished. The situation is more complicated over MQTT, which is a persistent, session-based protocol where many requests are sent over the same TCP socket.
CeleryScript authors can solve this problem by tagging each outbound request with a unique label
arg. You can think of this as a sort of request ID.
Multiplexing requests with the label arg
FarmBot has the ability to send many commands over a single MQTT channel. Messages do not follow a call/response cycle seen with HTTP servers. Unlike HTTP, MQTT connections are persistent, full duplex connections. If you send three commands to a FarmBot, they might not come back in the order that they were received.
Request order != response order:
| BROWSER Request "ABC" DEVICE
| ------------------------->
| Request "DEF"
Time | ------------------------->
| Request "GHI"
| ------------------------->
| Response to "DEF"
| <-------------------------
v Response to "ABC"
<-------------------------
Response to "GHI"
<-------------------------
To make sense of which messages have been acknowledged, the rpc_request
, rpc_ok
, and rpc_error
nodes all contain a label
argument. The label
serves as a unique identifier for a message. Assigning unique IDs to each RPC message allows re-assembly of message order on arrival.
In the case of an rpc_error
, it allows us to specify not only that an error has occurred, but also which command caused the error to occur.
Here is an example of a move_relative
command sent to a device over MQTT:
move_relative RPC:
// Message published to MQTT channel:
// '/bot/device_123/from_clients'
{
kind: "rpc_request",
// Any unique identifier can be used as a label.
// UUIDs are highly recommended.
args: { label: "adslkjhfalskdha" },
body: [
{ kind: "move_relative", args: { x: 0, y: 0, z: 0, speed: 100 } }
]
}
// ... after some time the bot will respond on MQTT channel `/bot/device_123/from_device`
{
kind: "rpc_ok",
// The "label" here matches the "label" of the move_relative request (shown above).
args: { label: "adslkjhfalskdha" }
}