5.4.5. Multi-instance writable object with dynamic number of instances
Note
This section describes in details the implementation of custom Objects in Anjay, either defined in OMA LwM2M Object and Resource Registry or designed by user.
Although most of the Object’s code can be generated using Anjay Object stub generator if you have Object’s definition in XML, it is recommended to read this section to have a clear understanding on what various parts of the LwM2M Object code are for.
In this tutorial you will learn:
how to implement
instance_create
handler to create Instances,how to implement
instance_remove
handler to remove Instances,how to assign instance identifiers to a newly created Instances,
basics of the
AVS_LIST
utility library.
Implemented object will be roughly based on Multi-instance writable object with fixed number of instances.
Name |
Object ID |
Instances |
---|---|---|
Test object |
1234 |
Multiple |
As before, each Object Instance has two Resources:
Name |
Resource ID |
Operations |
Instances |
Mandatory |
Type |
---|---|---|---|---|---|
Label |
0 |
Read/Write |
Single |
Mandatory |
String |
Value |
1 |
Read/Write |
Single |
Mandatory |
Integer |
The code is based on previous tutorial, yet in this chapter all Test object related code was moved to separate files to keep everything clean.
5.4.5.1. Updating the object structure
First of all, we have to update our test_object_t
structure to support
storing multiple object instances. For that, we need some kind of dynamically
sized container. We could choose plain-old, manually managed C arrays, but
that comes with unnecessary distraction, and as a matter of fact is a bit
error-prone. Therefore, in this tutorial, we are going to use AVS_LIST
,
which is an abstraction over singly linked list, and has been used in Anjay
with success since the beginning of the project.
typedef struct test_object {
// handlers
const anjay_dm_object_def_t *obj_def;
// object state
AVS_LIST(test_instance_t) instances;
AVS_LIST(test_instance_t) backup_instances;
} test_object_t;
Note
AVS_LIST(x)
is actually a macro that expands to a pointer of type x
.
It therefore has pointer semantics and can be treated like that (standard
dereferencing and dereferencing with assignment will work as expected). This
is however, not everything you need to know about them, as they are more
complicated than that. We recommend you to refer to the documentation.
In the previous tutorial, our Instances had hardcoded Instance IDs. We no
longer have such comfort, and have to be able to uniquely identify Object
Instances. As a consequence, we will add anjay_iid_t iid
field to
test_instance_t
:
typedef struct test_instance {
anjay_iid_t iid;
bool has_label;
char label[32];
bool has_value;
int32_t value;
} test_instance_t;
5.4.5.2. Initialization and cleanup
We must reconsider the way our Test object is being initialized. Up to this point it was allocated on stack and required no cleanup. Again, times have changed, and we won’t be able to proceed further without allocating memory on demand.
To achieve proper control over object lifetime and initialization, we are
going to introduce two functions, namely create_test_object
:
const anjay_dm_object_def_t **create_test_object(void) {
test_object_t *repr =
(test_object_t *) avs_calloc(1, sizeof(test_object_t));
if (repr) {
repr->obj_def = &OBJECT_DEF;
return &repr->obj_def;
}
return NULL;
}
and delete_test_object
:
void delete_test_object(const anjay_dm_object_def_t **obj) {
if (!obj) {
return;
}
test_object_t *repr = get_test_object(obj);
AVS_LIST_CLEAR(&repr->instances);
AVS_LIST_CLEAR(&repr->backup_instances);
avs_free(repr);
}
As you can see, dynamic memory management is semi-automatically handled by AVS_LIST
.
Note
AVS_LIST_CLEAR
iterates over the list, in the process frees memory allocated for
each element, and in the end sets list handle to NULL
.
Using functions defined above to create, register and free the Test object is similar as in previous tutorials.
Note
Before calling delete_test_object()
the object must be unregistered. You
can do this by calling anjay_unregister_object()
. Also anjay_delete()
will deregister (but not delete) all of registered objects before destruction
of Anjay instance.
5.4.5.3. Updating old, already implemented handlers to use AVS_LIST
To simplify matters, we have to agree upon one contract:
We establish a natural Instance ordering on their Instance IDs, exploiting the fact they MUST be unique.
We store Instances of the Test object in an ordered (as above) list.
OK, now that we made our assumptions, we are ready to implement utility function
get_instance
which retrieves Test object instance with specified Instance ID.
It will happen to be very useful in the next couple of subsections:
static AVS_LIST(test_instance_t) get_instance(test_object_t *repr,
anjay_iid_t iid) {
AVS_LIST(test_instance_t) it;
AVS_LIST_FOREACH(it, repr->instances) {
if (it->iid == iid) {
return it;
} else if (it->iid > iid) {
// Since list of instances is sorted by Instance ID,
// Instance with given iid does not exist on that list
break;
}
}
// Instance was not found.
return NULL;
}
And one more method, presenting another functionality of AVS_LISTs
before going into details of instance_create
and instance_remove
and leaving the rest of the work of this kind as an exercise for the reader
(or, if they are lazy, they can always look at the code):
static int test_list_instances(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_dm_list_ctx_t *ctx) {
(void) anjay; // unused
// iterate over all instances and return their IDs
AVS_LIST(test_instance_t) it;
AVS_LIST_FOREACH(it, get_test_object(obj_ptr)->instances) {
anjay_dm_emit(ctx, it->iid);
}
return 0;
}
Note that as we keep the Instances in sorted order, this implementation satisfies the contract for this handler.
That’s all for this section. As noted above, implementation of other methods
is as always available in the source code provided with the tutorial. We
do however strongly recommend you to port the methods to use AVS_LISTs
on your own, especially remember about updating transaction handlers.
5.4.5.4. instance_create
handler
Let’s have a look on anjay_dm_instance_create_t
handler type signature:
typedef int
anjay_dm_instance_create_t(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid);
The iid
parameter is the most important for us at the moment, as this is the
ID of the Instance we need to create.
LwM2M Create requests do not necessarily have to contain preferred Instance ID.
However, Anjay makes it transparent to the application - if the Instance ID is
not specified by the server, it will iterate over existing instances using the
anjay_dm_list_instances_t
handler, and find the lowest ID that is not
already occupied. If the Instance ID is specified by the server, Anjay will call
the anjay_dm_list_instances_t
handler and ensure that there is no such
Instance ID already existing.
static int test_instance_create(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid) {
(void) anjay; // unused
test_object_t *repr = get_test_object(obj_ptr);
AVS_LIST(test_instance_t) new_instance =
AVS_LIST_NEW_ELEMENT(test_instance_t);
if (!new_instance) {
// out of memory
return ANJAY_ERR_INTERNAL;
}
new_instance->iid = iid;
// find a place where instance should be inserted,
// insert it and claim a victory
AVS_LIST(test_instance_t) *insert_ptr;
AVS_LIST_FOREACH_PTR(insert_ptr, &repr->instances) {
if ((*insert_ptr)->iid > new_instance->iid) {
break;
}
}
AVS_LIST_INSERT(insert_ptr, new_instance);
return 0;
}
Note
There is a lot going on in this function, also new concepts regarding
AVS_LIST
are being used. We advise you to look at AVS_LIST_FOREACH_PTR
,
AVS_LIST_NEW_ELEMENT
and AVS_LIST_INSERT
documentation for more details.
5.4.5.5. instance_remove
handler
instance_remove
handler does not have to perform anything other than
removing the instance from our list.
static int test_instance_remove(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid) {
(void) anjay; // unused
test_object_t *repr = get_test_object(obj_ptr);
AVS_LIST(test_instance_t) *it;
AVS_LIST_FOREACH_PTR(it, &repr->instances) {
if ((*it)->iid == iid) {
AVS_LIST_DELETE(it);
return 0;
}
}
// should never happen as Anjay checks whether instance is present
// prior to issuing instance_remove
return ANJAY_ERR_INTERNAL;
}
Note
Complete code of this example can be found in examples/tutorial/AT-CustomObjects/multi-instance-dynamic subdirectory of main Anjay project repository.