Interprocess communication (IPC) client APIs - Managed integrations for AWS IoT Device Management

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.

  1. 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 context char *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);
  2. 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 is IOTMI_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.

  3. 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 the registerSubscriber 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.

  4. 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, this sendDeviceStateQuery 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 } ] } ] }] } }