4.6. Multi-Instance Object Dynamic
This section describes implementation of a multi-instance Object defined in OMA LwM2M Registry with dynamic set of Instances.
As an example, we are going to implement Temperature Object with ID 3303 in version 1.1 by expanding the code developed in Multi-instance Object Implementation. Only the differences that need to be applied are described in this article.
4.6.1. Implementing the Object
Note
Code related to this tutorial can be found under examples/tutorial/AT-MultiInstanceObjectDynamic in the Anjay Lite source directory and is based on examples/tutorial/AT-MultiInstanceObject example.
Besides the fact that the set of Object Instances is dynamic, the maximum number TEMP_OBJ_NUMBER_OF_INSTANCES must still be set.
4.6.1.1. Object and Object Instances State
Since the number of instances is now dynamic, and we want to handle transactional operations properly, let’s change the caching method from Resource level to the whole Instance level.
typedef struct {
anj_iid_t iid;
double sensor_value;
double min_sensor_value;
double max_sensor_value;
char application_type[TEMP_OBJ_APPL_TYPE_MAX_SIZE];
} temp_obj_inst_t;
typedef struct {
anj_dm_obj_t obj;
anj_dm_obj_inst_t insts[TEMP_OBJ_NUMBER_OF_INSTANCES];
anj_dm_obj_inst_t insts_cached[TEMP_OBJ_NUMBER_OF_INSTANCES];
temp_obj_inst_t temp_insts[TEMP_OBJ_NUMBER_OF_INSTANCES];
temp_obj_inst_t temp_insts_cached[TEMP_OBJ_NUMBER_OF_INSTANCES];
} temp_obj_ctx_t;
4.6.1.2. Create and Delete Handlers
To support creating and deleting Object Instances by the Server, we need to
implement appropriate handlers: inst_create
and inst_delete
.
static const anj_dm_handlers_t TEMP_OBJ_HANDLERS = {
.inst_create = inst_create,
.inst_delete = inst_delete,
.res_read = res_read,
.res_write = res_write,
.res_execute = res_execute,
.transaction_begin = transaction_begin,
.transaction_validate = transaction_validate,
.transaction_end = transaction_end,
};
Anjay Lite requires the instances to be sorted in ascending iid
order in the
anj_dm_obj_inst_t::insts
// classic bubble sort for keeping the IID in the ascending order
static void sort_instances(temp_obj_ctx_t *ctx) {
for (uint16_t i = 0; i < TEMP_OBJ_NUMBER_OF_INSTANCES - 1; i++) {
for (uint16_t j = i + 1; j < TEMP_OBJ_NUMBER_OF_INSTANCES; j++) {
if (ctx->temp_insts[i].iid > ctx->temp_insts[j].iid) {
// swap temp_insts
temp_obj_inst_t tmp_temp = ctx->temp_insts[i];
ctx->temp_insts[i] = ctx->temp_insts[j];
ctx->temp_insts[j] = tmp_temp;
// swap insts
anj_dm_obj_inst_t tmp_inst = ctx->insts[i];
ctx->insts[i] = ctx->insts[j];
ctx->insts[j] = tmp_inst;
}
}
}
}
static int inst_create(anj_t *anj, const anj_dm_obj_t *obj, anj_iid_t iid) {
(void) anj;
assert(iid != ANJ_ID_INVALID);
temp_obj_ctx_t *ctx = ANJ_CONTAINER_OF(obj, temp_obj_ctx_t, obj);
// find an unitialized instance and use it
bool found = false;
for (uint16_t idx = 0; idx < TEMP_OBJ_NUMBER_OF_INSTANCES; idx++) {
if (ctx->temp_insts[idx].iid == ANJ_ID_INVALID) {
ctx->temp_insts[idx].iid = iid;
ctx->insts[idx].iid = iid;
found = true;
break;
}
}
if (!found) {
// no free instance found
return -1;
}
sort_instances(ctx);
return 0;
}
static int inst_delete(anj_t *anj, const anj_dm_obj_t *obj, anj_iid_t iid) {
(void) anj;
temp_obj_ctx_t *ctx = ANJ_CONTAINER_OF(obj, temp_obj_ctx_t, obj);
for (uint16_t idx = 0; idx < TEMP_OBJ_NUMBER_OF_INSTANCES; idx++) {
if (ctx->temp_insts[idx].iid == iid) {
ctx->insts[idx].iid = ANJ_ID_INVALID;
ctx->temp_insts[idx].iid = ANJ_ID_INVALID;
sort_instances(ctx);
return 0;
}
}
return ANJ_DM_ERR_NOT_FOUND;
}
For Create operation that does not indicate the new iid, Anjay Lite
assigns a new iid
and passes it down to the inst_create
handler from
Object definition.
4.6.1.3. Object definition and Initialization
First, let’s create a static structure of temperature_obj with the basic information:
static temp_obj_ctx_t temperature_obj = {
.obj = {
.oid = 3303,
.version = "1.1",
.handlers = &TEMP_OBJ_HANDLERS,
.max_inst_count = TEMP_OBJ_NUMBER_OF_INSTANCES
}
};
And then we can add an initialization function that fills the insts and temp_insts arrays contents:
void temperature_obj_init(void) {
// initialize the object with 0 instances
for (int i = 0; i < TEMP_OBJ_NUMBER_OF_INSTANCES; i++) {
temperature_obj.insts[i].res_count = TEMPERATURE_RESOURCES_COUNT;
temperature_obj.insts[i].resources = RES;
temperature_obj.insts[i].iid = ANJ_ID_INVALID;
temperature_obj.temp_insts[i].iid = ANJ_ID_INVALID;
}
temperature_obj.obj.insts = temperature_obj.insts;
temp_obj_inst_t *inst;
// initilize 1st instance
inst = &temperature_obj.temp_insts[0];
temperature_obj.insts[0].iid = 1;
inst->iid = 1;
snprintf(inst->application_type, sizeof(inst->application_type),
"Sensor_1");
inst->sensor_value = 10.0;
inst->min_sensor_value = 10.0;
inst->max_sensor_value = 10.0;
// initialize 2nd instance
inst = &temperature_obj.temp_insts[1];
temperature_obj.insts[1].iid = 2;
inst->iid = 2;
snprintf(inst->application_type, sizeof(inst->application_type),
"Sensor_2");
inst->sensor_value = 20.0;
inst->min_sensor_value = 20.0;
inst->max_sensor_value = 20.0;
}
This initialization must be then called in main function before installing the Object in Anjay Lite Data Model.
int main(int argc, char *argv[]) {
// ...
temperature_obj_init();
if (anj_dm_add_obj(&anj, get_temperature_obj())) {
log(L_ERROR, "install_temperature_object error");
return -1;
}
// ...
}
4.6.2. Supporting transactional Operations
In this case, to achieve proper transactions support, we need to cache not only the writeable resources, but rather the whole Object context. This can be done by creating copies of both insts and temp_insts arrays:
static int transaction_begin(anj_t *anj, const anj_dm_obj_t *obj) {
(void) anj;
temp_obj_ctx_t *ctx = ANJ_CONTAINER_OF(obj, temp_obj_ctx_t, obj);
memcpy(ctx->insts_cached, ctx->insts, sizeof(ctx->insts));
memcpy(ctx->temp_insts_cached, ctx->temp_insts, sizeof(ctx->temp_insts));
return 0;
}
static int transaction_validate(anj_t *anj, const anj_dm_obj_t *obj) {
(void) anj;
(void) obj;
// Perform validation of the object
return 0;
}
static void transaction_end(anj_t *anj, const anj_dm_obj_t *obj, int result) {
(void) anj;
if (result) {
// restore cached data
temp_obj_ctx_t *ctx = ANJ_CONTAINER_OF(obj, temp_obj_ctx_t, obj);
memcpy(ctx->insts, ctx->insts_cached, sizeof(ctx->insts));
memcpy(ctx->temp_insts, ctx->temp_insts_cached,
sizeof(ctx->temp_insts));
}
}