The ARF Applications page describes the ARF communicator and the replicator objects and the basic steps for implementing ARF (IoT) applications. This page describes the eXtremeDB Active Replication Fabric C API for implementing automatic or on-demand data exchange between collection points and servers.
ARF runtime support includes two libraries: the communication library
mcoiotcomm
and the protocol librarymcoiotrepl
. The communication library deals with network connections (IP sockets) and provides a set of functions and structures to the protocol library. The architecture is similar to the architecture of eXtremeDB Cluster (mcocltcp
+mcocluster
) and eXtremeDB High Availability (mconwtcp
+mcoha
). ARF applications can use the APIs exposed by both the communication and protocol libraries.The ARF API is practically identical for device and server databases and all functions can be used by the both device and server applications.
As explained in the ARF Applications page, the communicator and the replicator objects belong to the process that creates them and are never shared between multiple processes. By default both objects are allocated by the dynamic allocator through
mco_sys_malloc()
. The other possible allocator is a static allocator which has a limited number of connections and a limited dictionary size.Example
To illustrate usage of the ARF C API, the following code snippets illustrate a simple ARF server and device application.
IoT Device Application
Consider the following simple schema definition for an edge device:
declare database iotdevice; declare auto_oid[100]; iot device 1000; uptable class Sensor { datetime ts; unsigned<4> sensorId; double value; hash<ts, sensorId> idx[100]; }; downtable class Config { unsigned<4> param_id; double value; tree<param_id> idx; };The following code snippet would initialize the ARF runtime, and create the communicator and replicator for the device:
int main(int argc, char *argv[]) { ... mco_iot_replicator_params_t repl_params; mco_iot_comm_params_t comm_params; mco_iot_replicator_h repl; mco_iot_comm_h comm; const char *conn_string "127.0.0.1:15000"; ... mco_iot_comm_params_init(&comm_params); CHECK(mco_iot_comm_create(&comm_params, &comm)); mco_iot_replicator_params_init(&repl_params); CHECK(mco_iot_replicator_create(db, comm, &repl_params, &repl)); CHECK(mco_iot_replicator_connect(repl, conn_string, 2*1000, 0)); ...Then, after inserting and/or modifying Sensor data, the device could be synchronized with the server database and then shut down with code like the following:
CHECK(mco_iot_replicator_sync(repl, MCO_IOT_SERVER_AGENT_ID, MCO_IOT_SYNC_PULL | MCO_IOT_SYNC_PUSH | MCO_IOT_SYNC_WAIT)); PrintConfig(db); CHECK(mco_iot_replicator_stop(repl)); CHECK(mco_iot_comm_stop(comm)); CHECK(mco_iot_replicator_destroy(repl)); CHECK(mco_iot_comm_destroy(comm)); CHECK(mco_db_disconnect(db)); CHECK(mco_db_close(db_name)); free(dev.dev.conv.ptr); mco_runtime_stop(); return 0; }Note that the function
PrintConfig()
simply prints out the configuration parameters downloaded from the server database bymco_iot_replicator_sync()
.IoT Server Application
Consider the following simple schema definition for the server database interfacing with the above device application:
declare database iotserver; declare auto_oid[100]; iot server 100; uptable class Sensor { datetime ts; unsigned<4> sensorId; double value; tree<ts, sensorId> idx; }; downtable class Config { unsigned<4> param_id; double value; hash<param_id> idx[100]; };The following code snippet would initialize the ARF runtime, create the communicator and replicator, and begin listening for the device connections:
int main(int argc, char *argv[]) { ... mco_iot_replicator_params_t repl_params; mco_iot_comm_params_t comm_params; mco_iot_replicator_h repl; mco_iot_comm_h comm; ... mco_iot_comm_params_init(&comm_params); CHECK(mco_iot_comm_create(&comm_params, &comm)); mco_iot_replicator_params_init(&repl_params); CHECK(mco_iot_replicator_create(db, comm, &repl_params, &repl)); CHECK(mco_iot_comm_listen(comm, "15000", 0)); ...The following code could be used to modify the Config data and propagate the changes to devices:
void InsertConfig(mco_db_h db) { Config obj; mco_trans_h t; CHECK(mco_trans_start(db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &t)); CHECK(Config_new(t, &obj)); CHECK(Config_agent_id_put(&obj, 1000)); /* This record will be replicated to device with ID 1000 */ CHECK(Config_param_id_put(&obj, 1)); CHECK(Config_value_put(&obj, 3.1415)); CHECK(Config_new(t, &obj)); CHECK(Config_agent_id_put(&obj, MCO_IOT_ALL_AGENTS)); /* This record will be replicated to all devices */ CHECK(Config_param_id_put(&obj, 2)); CHECK(Config_value_put(&obj, 2.7182)); CHECK(mco_trans_commit(t)); }Note the use of the constant
MCO_IOT_ALL_AGENTS
to cause this record to be replicated to all devices.Then, the server could update the configuration parameters, print out the current Sensor data, and shut down with code like the following:
InsertConfig(db); PrintSensors(db); CHECK(mco_iot_replicator_stop(repl)); CHECK(mco_iot_comm_stop(comm)); CHECK(mco_iot_replicator_destroy(repl)); CHECK(mco_iot_comm_destroy(comm)); CHECK(mco_db_disconnect(db)); CHECK(mco_db_close(db_name)); free(dev.dev.conv.ptr); mco_runtime_stop(); return 0; }Note that the function
PrintSensors()
simply prints out the current Sensor data from the active devices.