Embedded Systems C Bridge API: Unterschied zwischen den Versionen
Cg (Diskussion | Beiträge) |
Cg (Diskussion | Beiträge) |
||
Zeile 123: | Zeile 123: | ||
==== Frame Format ==== |
==== Frame Format ==== |
||
Messages are interchanged in frames which contain the length of a payload and the payload. |
|||
⚫ | |||
Frames can be encoded different formats. |
|||
⚫ | |||
⚫ | |||
⚫ | |||
* HTTP/REST frame<br>the payload is the POST data of an HTTP packet; the payload length is provided by the HTTP header. |
* HTTP/REST frame<br>the payload is the POST data of an HTTP packet; the payload length is provided by the HTTP header. |
||
* a binary frame, consisting of 4 bytes of payload length, followed by the payload. |
* a binary frame, consisting of 4 bytes of payload length, followed by the payload. |
||
* a textual frame, consisting of the payload length as ascii string, a space as separator, followed by the payload. |
* a textual frame, consisting of the payload length as ascii string, a space as separator, followed by the payload. |
||
Both the expecco side and the cLibrary support both the binary and textual frame formats. |
|||
⚫ | |||
By default (and initially), both sides use the textual frame format initially. |
|||
Future extensions will be backward compatible. |
|||
⚫ | |||
==== Requests (General Format) ==== |
==== Requests (General Format) ==== |
Version vom 1. September 2019, 15:06 Uhr
Inhaltsverzeichnis
- 1 Embedded Systems C Bridge API
- 1.1 Architecture
- 1.2 CBridge Message Interchange Sequence Diagram
- 1.3 Different Threading Models
- 1.4 Different Memory Allocation Models
- 1.5 CBridge Library
- 1.6 JSON Library
- 1.7 Bridge Wire Protocol
Embedded Systems C Bridge API[Bearbeiten]
We provide the CBridge code both as a DLL (shared object) and as a static link library upon request. If linked against the application, or dynamically loaded into the application, expecco can interact with - even if the target is an embedded system.
Architecture[Bearbeiten]
The CBridge opens a socket and listens for an incoming connection. Only one such connection may be active at any time. Once connected, expecco will send requests and receive responses, which are exchanged in human readable JSON format, which is very similar to the ChromeV8 debug interface message format.
The set of supported messages is defined by the target system - therefore customers have full control over which messages are allowed to be interchanged, and how they are handled. This is done before the bridge is activated by so called message registration calls.
The bridge code as provided by exept includes handlers for various common tasks, which can be registered as option. Except also provides a ready-to-run cBridge executable, which is configured to register handlers for remote code execution (i.e. for Bridged C Actions).
As an alternative to using the library, customers can also implement the wire protocol, which is described below.
CBridge Message Interchange Sequence Diagram[Bearbeiten]
In the following swim lane diagram,
- function calls are marked as "=====>"
- socket messages as "------>"
- parallel execution as "||"
- thread blocking (suspend / wait) as "***"
- normal thread execution as "..."
expecco side target cBridge library cBridge thread --------------------+-------------------------------------+------------------------+---------------------------- ... | ... | | |initialization: | | | cBrige_registerHandler(...) =======> register handler | | | immediate return | | <======= | | ... | | | cBrige_registerHandler(...) =======> register handler | | | immediate return | | <======= | | ... | | | cBrige_startAsync(...) =======> starts cBridge thread | thread started | | immediate return | opens listen socket | ... <======= | await connection | target runs as usual | | *** | ... | | await incoming message | | | *** ... | | | send request -------------------------------------------------------------------> receive request (JSON) await response | | | decode request *** | | | prepare response object | | | call handler(req, resp) | ... || handler in target <========================= | ... || fills response | | | ... || =========================> send response | | | encode response send request <------------------------------------------------------------------- send response (JSON) ... | ... | | await incoming message | | | ***
Different Threading Models[Bearbeiten]
The bridge library can be used in various different configurations:
Threaded with separate Socket handling code[Bearbeiten]
This is the easiest mode to use. The socket handler is started as a separate thread, which blocks in the listen() and read() calls to the socket. The target code must be changed to perform a single call into the cLibrary (to start the asynchronous bridge thread), in addition to the handler registration, which is required in all configurations (and of course, the handlers code).
This mode should be used if the target is a Windows program or if the target's operating system supports Posix threads.
Non-Threaded with Socket Polling[Bearbeiten]
In this mode, the main application should poll (or select on) the socket itself, and call into the cLibrary when either a connection is established, or an incoming message is received.
In addition to the handler registration calls and handler code implementation, the target needs to ne changed to call the library's incomingMessage handler.
This mode can be used when no thread support is provided by the operating system, or if there already exists an event handling loop which selects on input streams (eg. Qt, X11 or OSX main event loops), and sockets are to be used for bridge communication.
Non Threaded with Packet Delivery (non-Socket Communication)[Bearbeiten]
In this mode, the main application should use any mechanism available to exchange messages via any external port (USB, serial, socket) or via shared memory, and call into the cLibrary when an incoming message arrived. It must also register a packet delivery function, for the cLibrary to send back responses.
This mode must be used, if no socket communication is possible.
Different Memory Allocation Models[Bearbeiten]
As JSON objects vary in size and structure, these objects must use dynamically allocated memory. because many embedded systems will not allow or support the use of "malloc/free", the library can be configured to:
- either use malloc / free dynamically (i.e. for every object) (dynamic malloc scheme)
- or to use a single malloc'd area, which is allocated once at startup (single malloc scheme)
- or to not use malloc/free at all, but instead use a static memory area for temporary data (static data scheme)
Memory needs to be dynamically allocated whenever an incoming message is parsed from JSON into a jsonObject, when additional fields are added to a jsonObject (i.e. when a requestHandler constructs a response object), or when a jsonObject response is finally encoded into JSON and set back to expecco.
All memory which is allocated for the request and response objects will be released when the message handler's response object has been sent to expecco. Pointers into it and field references are no longer valid, as the memory will be reused for the next request/response.
Dynamic Malloc Scheme[Bearbeiten]
This is the standard behavior when the target runs on Windows or Unix systems.
Allocations will be done by calling "malloc" multiple times, once for each individual jsonObject field and possibly for field values.
Single Malloc Scheme[Bearbeiten]
This should be used if the target system does provide malloc, but dynamic malloc is not allowed or not wanted due to other constraints (thread safety, predictability, real time constraints etc.).
A single memory area is allocated using "malloc" at startup time. All jsonObject fields will be allocated inside this arena, which will never be freed. The size of the arena defines the maximum JSON message size which can be interchanged, and an operation may fail due to that constraint.
Static Data Scheme[Bearbeiten]
This mode should be used, if no malloc is available or allowed at all inside the target system.
Similar to the previous scheme, but static memory (a data buffer provided by the target) is used, and malloc/free is never called.
CBridge Library[Bearbeiten]
JSON Library[Bearbeiten]
Messages are interchanged as JSON encoded packages. For this, a JSON library is part of the cLibrary, to parse and generate JSON.
Bridge Wire Protocol[Bearbeiten]
Frame Format[Bearbeiten]
Messages are interchanged in frames which contain the length of a payload and the payload. Frames can be encoded different formats.
A packet's payload is always encoded as a JSON object. This payload can be (currently) delivered in one of the following frames:
- HTTP/REST frame
the payload is the POST data of an HTTP packet; the payload length is provided by the HTTP header. - a binary frame, consisting of 4 bytes of payload length, followed by the payload.
- a textual frame, consisting of the payload length as ascii string, a space as separator, followed by the payload.
Both the expecco side and the cLibrary support both the binary and textual frame formats. By default (and initially), both sides use the textual frame format initially.
Future extensions will be backward compatible.
Currently, the HTML/REST format is not yet supported directly by the cBridge library. However, it can be used if the target app already includes an HTTP/REST service and passes incoming payload to the cLibrary.
Requests (General Format)[Bearbeiten]
Requests can be sent from either side. On the expecco side, a request call will block the calling thread, until a response is received. On the cBridge side, a callback will be called, when the response is received. In theory, the protocol allows for multiple request to be sent and outstanding until corresponding responses are received. However this is actually not needed and therefore currently not supported by either expecco, not the cBridge library.
All requests must contain the following fields:
{ "type": "request", // required "command": "<nameOfCommand>", // required "seq": sequenceNr // required }
where:
- command the name as given to the "
cBridge_registerHandler()
function previously, - seq a unique number assigned to every request/event by the sender side (usually simply a running number, or UUID-string). The value of "seq" will later be found in the corresponding response object.
Response (General Format)[Bearbeiten]
Responses are only sent as an answer to a previous incoming request. Every request MUST be answered by a response.
All responses must contain the following fields:
{ "type": "response", // required "requestSeq": sequenceNr // required "success": boolean, // required "command": "<nameOfCommand>", // optional "errorMessage":"...", // optional (if success == false) }
where:
- command the command from the corresponding request, for which this is the response.
- requestSeq the seqNr of the corresponding request, for which this is the response.
- success boolean true or false
- errorMessage if success is false
Event (General Format)[Bearbeiten]
Events can be sent at any time (i.e. while waiting for a response, while executing a request action, or at any other time). The bridge ensures, that an ongoing message is not disturbed (i.e. the actual packet sending is atomic). Events are not answered by a response.
All responses must contain the following fields:
{ "type": "event", // required "seq": sequenceNr // required "event": "<nameOfEvent>", // required }
where:
- seq same as in requests. Event sequence numbers may not be the same as request sequence numbers (i.e. you can use one single sequence-nr generator, which simply increments on every packet)
Requests (from expecco to cBridge)[Bearbeiten]
The following lists well known messages only. The format of user-defined messages (i.e. for which a handler was registered) is variable and opaque to the bridge.
PROTOCOL Request[Bearbeiten]
Sent from expecco to change the protocol. This will only be sent iff the bridge announced the availability of another protocol (via a HELLO event), AND expecco decides to another. Expecco will choose the first within the list of available protocols, which is also supported by the expecco side.
{ "type": "request", // required "message": "protocol", // required "seq": sequenceNr // required "protocol": "protocolToUse", // required }
when this message arrives, the response MUST be sent in the current protocol mode. Then, any further communication MUST be performed in the new protocol.
DEFINEACTION Request[Bearbeiten]
This is an internal request for Bridged C actions. Its format is not part of the stable API.
CALLACTION Request[Bearbeiten]
This is an internal request for Bridged C actions. Its format is not part of the stable API.
Events (from cBridge to expecco)[Bearbeiten]
HELLO Event[Bearbeiten]
Sent from the bridge to expecco to provide version and protocol formats.
{ "type": "event", // required "message": "hello", // required "seq": sequenceNr // required "version": "<version string>", // optional "protocols": [ "<supported1>", ... ] // optional }
Used to announce version information and available protocols.
Each <supported> element describes a protocol which is supported by the target/cLibrary,
in order of preference (the targets preference).
The <supported> strings are of the form: "frame/payload[/version]
,
where frame describes the frame format, payload the encoding of the payload and the optional version further details on the used payload format. A missing version part defaults to "1".
Currently known protocols are:
- text/json/1 - JSON encoded payload, in a text frame (i.e. prefixed by payload size in ascii and space)
- binary/json/1 - JSON encoded payload, in a binary frame (i.e. prefixed by 4 bytes of payload size)
- http/json/1 - JSON encoded payload, in an HTTP frame
The partner (expecco) may decide to switch the protocol, based on the returned information.
EXECUTING Event[Bearbeiten]
This is an internal event for Bridged C actions. Its format is not part of the stable API.
OUT Event[Bearbeiten]
This is an internal event for Bridged C actions. Its format is not part of the stable API.
OUTPUT Event[Bearbeiten]
Sent from the bridge to expecco to output a string to the Transcript (console), to stdout or stderr.
{ "type": "event", // required "event": "output", // required "message": "<how>", // required "seq": sequenceNr // required "target": "<stream>", // required "args": ["<fmt>",...] // required for show or showCR }
- messageone of "cr", "show" or "showCR"
- target one of "Transcript", "Stdout" or "Stderr"
- args the arguments for the string to be sent. If the first argument ("<fmt>")contains placeholders like "%1", these are replaced by corresponding values from the remaining args (i.e "some error in %1"). Otherwise, the arguments are concatenated to form a single string.