Interprocess communication (IPC) client APIs
External components on the managed integrations hub can communicate with the managed integrations Hub SDK using its Agent component and interprocess communications (IPC). An example external component on the hub is a daemon (a continuously running background process) that manages local routines. During communication, the IPC client is the external component that publishes commands or other requests, and subscribes to events. The IPC server is the Agent component in the managed integrations Hub SDK. For more information, see Setting up the IPC client.
To build the IPC client, an IPC client library IotmiLocalControllerClient
is provided. This library provides client-side APIs for communicating with the IPC server in Agent, including sending command requests, querying device states, subscribing to events (like the device state event), and handling event-based interactions.
Setting up the IPC client
The IotmiLocalControllerClient
library is a wrapper around basic IPC APIs, which simplify and streamline the process of implementing IPC in your applications. The following sections describe the APIs it provides.
Note
This topic is specifically for an external component as an IPC client and not the implementations of an IPC server.
-
Create an IPC client
You must first initialize the IPC client before it can be used to process requests. You can use a constructor in the
IotmiLocalControllerClient
library, which takes the subscriber contextchar *subscriberCtx
as a parameter, and creates an IPC client manager based on it. The following is an example of creating an IPC client:// Context for subscriber char subscriberCtx[] = "example_ctx"; // Instantiate the client IotmiLocalControllerClient lcc(subscriberCtx);
-
Subscribe to an event
You can subscribe the IPC client to events of the targeting IPC server. When the IPC server publishes an event that the client is subscribed to, the client will receive that event. To subscribe, use the
registerSubscriber
function and provide the event IDs to subscribe to, as well as the customized callback.The following is a definition of the
registerSubscriber
function and its example usage:iotmi_statusCode_t registerSubscriber( std::vector<iotmiIpc_eventId_t> eventIds, SubscribeCallbackFunction cb);
// A basic example of customized subscribe callback, which prints the event ID, data, and length received void customizedSubscribeCallback(iotmiIpc_eventId_t event_id, uint32_t length, const uint8_t *data, void *ctx) { IOTMI_IPC_LOGI("Received subscribed event id: %d\n" "length: %d\n" "data: %s\n", event_id, length, data); } iotmi_statusCode_t status; status = lcc.registerSubscriber({IOTMI_IPC_EVENT_DEVICE_UPDATE_TO_RE}, customerProvidedSubscribeCallback);
The
status
is defined to check if the operation (like subscribe or send request) is successful. If the operation is successful, the returned status isIOTMI_STATUS_OK (= 0)
.Note
The IPC library has the following service quotas for the maximum number of subscriber and events in a subscription:
-
Maximum number of subscribers per process: 5
Defined as
IOTMI_IPC_MAX_SUBSCRIBER
in the IPC library. -
Maximum number of events defined: 32
Defined as
IOTMI_IPC_EVENT_PUBLIC_END
in the IPC library. -
Each subscriber has a 32-bit events field, where each bit corresponds to an event defined.
-
-
Connect the IPC client to the server
The connect function in the
IotmiLocalControllerClient
library does jobs like initializing the IPC client, registering subscribers, and subscribing to events that were provided in theregisterSubscriber
function. You can call the connect function on the IPC client.status = lcc.connect();
Confirm that the returned status is
IOTMI_STATUS_OK
before you send requests or make other operations. -
Send command request and device state query
The IPC server in Agent can process command requests and device state requests.
-
Command request
Form a command request payload string, and then call the
sendCommandRequest
function to send it. For example:status = lcc.sendCommandRequest(payloadData, iotmiIpcMgr_commandRequestCb, nullptr);
/** * @brief method to send local control command * @param payloadString A pre-defined data format for local command request. * @param callback a callback function with typedef as PublishCallbackFunction * @param client_ctx client provided context * @return */ iotmi_statusCode_t sendCommandRequest(std::string payloadString, PublishCallbackFunction callback, void *client_ctx);
For more information about the command request format, see command requests.
Example callback function
The IPC server first sends a message acknowledgement
Command received, will send command response back
to the IPC client. After receiving this acknowledgment, the IPC client can expect a command response event.void iotmiIpcMgr_commandRequestCb(iotmi_statusCode_t ret_status, void *ret_data, void *ret_client_ctx) { char* data = NULL; char *ctx = NULL; if (ret_status != IOTMI_STATUS_OK) return; if (ret_data == NULL) { IOTMI_IPC_LOGE("error, event data NULL"); return; } if (ret_client_ctx == NULL) { IOTMI_IPC_LOGE("error, event client ctx NULL"); return; } data = (char *)ret_data; ctx = (char *)ret_client_ctx; IOTMI_IPC_LOGI("response received: %s \n", data); }
-
Device state request
Similarly to the
sendCommandRequest
function, thissendDeviceStateQuery
function also takes a payload string, the corresponding callback, and the client context.status = lcc.sendDeviceStateQuery(payloadData, iotmiIpcMgr_deviceStateQueryCb, nullptr);
-
IPC interface definitions and payloads
This section focuses on IPC interfaces specifically for communication between the Agent and external components, and provides example implementations of IPC APIs between those two components. In the following examples, the external component manages local routines.
In the IoTManagedIntegrationsDevice-IPC
library, the following commands and events are defined for communication between the Agent and the external component.
typedef enum { // The async cmd used to send commands from the external component to Agent IOTMI_IPC_SVC_SEND_REQ_FROM_RE = 32, // The async cmd used to send device state query from the external component to Agent IOTMI_IPC_SVC_SEND_QUERY_FROM_RE = 33, // ... } iotmiIpcSvc_cmd_t;
typedef enum { // Event about device state update from Agent to the component IOTMI_IPC_EVENT_DEVICE_UPDATE_TO_RE = 3, // ... } iotmiIpc_eventId_t;
Command request
Command request format
-
Example command request
{ "payload": { "traceId": "LIGHT_DIMMING_UPDATE", "nodeId": "1", "managedThingId": <ManagedThingId of the device>, "endpoints": [{ "id": "1", "capabilities": [ { "id": "matter.LevelControl@1.4", "name": "Level Control", "version": "1.0", "actions":[ { "name": "UpdateState", "parameters": { "OnLevel": 5, "DefaultMoveRate": 30 } } ] } ] }] } }
Command response format
-
If the command request from the external component is valid, the Agent sends it to the CDMB (Common Data Model Bridge). The actual command response that contains the command execution time and other information isn't sent back to the external component immediately, since processing commands takes time. This command response is an instant response from the Agent (like an acknowledgement). The response tells the external component that managed integrations received the command, and will either process it or discard it if there isn't a valid local token. The command response is sent in a string format.
std::string errorResponse = "No valid token for local command, cannot process."; *ret_buf_len = static_cast<uint32_t>(errorResponse.size()); *ret_buf = new uint8_t[*ret_buf_len]; std::memcpy(*ret_buf, errorResponse.data(), *ret_buf_len);
Device state request
The external component sends a device state request to the Agent. The request provides the managedThingId
of a device, and then the Agent replies with the state of that device.
Device state request format
-
Your device state request must have the
managedThingId
of the queried device.{ "payload": { "managedThingId": "testManagedThingId" } }
Device state response format
-
If the device state request is valid, the Agent sends the state back in the string format.
Example device state response for a valid request
{ "payload": { "currentState": "exampleState" } }
If the device state request is not valid (such as if there isn't a valid token, the payload can't be processed, or another error case), the Agent sends the response back. The response includes the error code and error message.
Example device state response for an invalid request
{ "payload": { "response":{ "code": 111, "message": "errorMessage" } } }
Command response
Example command response format
{ "payload": { "traceId": "LIGHT_DIMMING_UPDATE", "commandReceivedAt": "1684911358.533", "commandExecutedAt": "1684911360.123", "managedThingId": <ManagedThingId of the device>, "nodeId": "1", "endpoints": [{ "id": "1", "capabilities": [ { "id": "matter.OnOff@1.4", "name": "On/Off", "version": "1.0", "actions":[ {} ] } ] }] } }
Notification event
Example notification event format
{ "payload": { "hasState": "true" "nodeId": "1", "managedThingId": <ManagedThingId of the device>, "endpoints": [{ "id": "1", "capabilities": [ { "id": "matter.OnOff@1.4", "name": "On/Off", "version": "1.0", "properties":[ { "name": "OnOff", "value": true } ] } ] }] } }