No edit summary |
|||
| Line 89: | Line 89: | ||
== Example Machine == | == Example Machine == | ||
This is an excerpt from the B machine used to communicate with the Crazyflie drone. | This is an excerpt from the B machine used to communicate with the Crazyflie drone. | ||
Parallel to this a Python script is running, which implements all the JSON-RPC methods and uses [https://pypi.org/project/cflib/ <tt>cflib</tt>] to communicate with the drone over radio. | |||
<pre> | <pre> | ||
MACHINE DroneCommunicator USES LibraryJSON, LibraryZMQ_RPC | MACHINE DroneCommunicator USES LibraryJSON, LibraryZMQ_RPC | ||
ProB contains external functions to read and write JSON data and to communicate via sockets using JSON-RPC:
We have used this library to control Crazyflie drones via ProB:
LibraryJSON.mch defines a FREETYPE to represent JSON data in a type-safe way:
JsonValue = JsonNull,
JsonBoolean(BOOL),
JsonNumber(FLOAT),
JsonString(STRING),
JsonArray(seq(JsonValue)),
JsonObject(STRING +-> JsonValue)
Free Types are inductive data types. They are not standard B, but stem from Z and Rodin's theory plug-in.
JsonValue maps quite naturally to the specification of JSON itself.
LibraryJSON.def contains the definitions for the the external functions to interact with JSON text data:
You need to include both LibraryJson.def and LibraryJson.mch to interact with JSON. One contains the freetype definition, the other one the external function definitions.
Implements the JSON RPC protocol.
Despite their names the LibraryZMQ_RPC libraries can use both ZeroMQ library and native sockets.
LibraryZMQ_RPC.mch defines a FREETYPE to represent JSON-RPC response object in a type-safe way:
RpcResult = RpcSuccess(JsonValue), RpcError(STRING)
LibraryZMQ_RPC.def contains the definitions for the the external functions to interact with JSON RPC as a client or server:
LibraryZMQ_RPC.def will transitively include LibraryJson.def.
You need to include all of LibraryJson.mch, LibraryZMQ_RPC.def and LibraryZMQ_RPC.mch to interact with JSON-RPC, because the freetype definitions have to be separate from the external function definitions.
This is an excerpt from the B machine used to communicate with the Crazyflie drone. Parallel to this a Python script is running, which implements all the JSON-RPC methods and uses cflib to communicate with the drone over radio.
MACHINE DroneCommunicator USES LibraryJSON, LibraryZMQ_RPC
DEFINITIONS
"LibraryZMQ_RPC.def";
"LibraryReals.def";
SET_PREF_MAX_OPERATIONS == 0;
CONSTANTS DRONE_URL
PROPERTIES DRONE_URL:STRING & DRONE_URL = "radio://0/80/2M/5700B500F7"
VARIABLES init, socket, cycle
INVARIANT
init:BOOL &
socket:SOCKET &
cycle:INTEGER
INITIALISATION
init := FALSE;
socket := 0;
cycle := 1
OPERATIONS
Init =
SELECT init = FALSE THEN
socket := ZMQ_RPC_INIT("tcp://localhost:22272");
init := TRUE
END;
Destroy =
SELECT init = TRUE THEN
ZMQ_RPC_DESTROY(socket);
init := FALSE
END;
register_sensors =
SELECT init = TRUE THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "register_log", {
("url" |-> JsonString(DRONE_URL)),
("name" |-> JsonString("Telemetry")),
("variables" |-> JsonArray([
JsonString("stateEstimate.x"),
JsonString("stateEstimate.y"),
JsonString("stateEstimate.z"),
JsonString("stateEstimate.roll"),
JsonString("stateEstimate.pitch"),
JsonString("stateEstimate.yaw")
]))}));
END
END;
Drone_Takeoff =
SELECT init = TRUE THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "takeoff", {("url" |-> JsonString(DRONE_URL))}))
END
END;
Drone_Land =
SELECT init = TRUE THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "land", {("url" |-> JsonString(DRONE_URL))}))
END
END;
Drone_Left(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "left", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
Drone_Right(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "right", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
Drone_Up(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "up", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
Drone_Down(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "down", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
Drone_Forward(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "forward", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
Drone_Backward(dist) =
SELECT
init = TRUE &
dist : 0..2000
THEN
VAR res IN
res := RpcSuccess~(ZMQ_RPC_SEND(socket, "backward", {("url" |-> JsonString(DRONE_URL)), ("distance" |-> JsonNumber(RDIV(real(dist), real(1000))))}))
END
END;
out <-- Drone_GetX =
SELECT init = TRUE THEN
out := floor(1000.0 * JsonNumber~(RpcSuccess~(ZMQ_RPC_SEND(socket, "get_log_var", {("url" |-> JsonString(DRONE_URL)), ("name" |-> JsonString("stateEstimate.x"))}))));
cycle := cycle + 1
END
END