Embedded Systems C Bridge API

Aus expecco Wiki (Version 2.x)
Zur Navigation springen Zur Suche springen

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 (unless otherwise configured), 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.

Handshake[Bearbeiten]

The bridge client (cLibrary) usually opens a TCP socket and listens for a connection. After accept, as single hello message is sent to the expecco side. By default, this initial hello message is ALWAYS encoded as a textFrame. (however, depending on the hello message's payload, the protocol may be switched to another format for further messages).

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
    "part":        partNr                        // optional
    "more":        boolean                       // optional
}

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.
  • part if present, this must be a running number, sequencing the parts of a single message. Numbers start with 1.
  • more if present, informs that more parts are to be expected for the current message. If missing and this is a part-package, it is assumed that this was the last part and the message is now complete (i.e. defaults to false)

Long packets may be split into and sent as a sequence of smaller packets, if a partner has limited resources in memory) or to reserve bandwidth for other communication. It should then add both a "part" field (containing a art-sequence number, starting at 1) and a "more" boolean flag, which tells the other side, that more packets are to be received for the same message. Currently, the messages with huge amounts of data will possibly split this way (eg. screenDump). The details on how that bulk data is split among the individual parts is to be documented in the concrete message.

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)
    "part":        partNr                        // optional
    "more":        boolean                       // optional
}

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
  • part and more as described above

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
    "limits":    [ "<limit1>", ... ]           // optional
}

Used to announce version information and available protocols.

  • version: a string of the form "<major>.<minor>". For same major numbers, any minor less or equal to a partner's own minor version must be supported. The major version MUST be incremented for incompatible (format) changes. The minor version should be incremented if new protocol messages or new fields are added.
  • protocols: list of supported protocols, in order of preference
  • limits: list of limits

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.

The optional <limit> elements can be used to order the partner to not exceed certain limits. Either side will refuse and ignore packets (or respond with an error) if a requested limit is exceeded. Currently known limits are:

  • { "payloadSize": maxSize }
    tells the partner to not send packets longer than that size. If required, packets must then be split when into smaller continuation packets. This can be used to limit the amount of memory needed inside a partner (i.e. when running in an embedded system).
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.

Requests (from cBridge to expecco)[Bearbeiten]



Copyright © 2014-2024 eXept Software AG