5.4.1. Single-instance read-only object
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.
This is the simplest possible case. Let us implement following custom Object:
Name |
Object ID |
Instances |
---|---|---|
Test object |
1234 |
Single |
With two simple Resources:
Name |
Resource ID |
Operations |
Instances |
Mandatory |
Type |
---|---|---|---|---|---|
Object name |
0 |
Read |
Single |
Mandatory |
String |
Timestamp |
1 |
Read |
Single |
Mandatory |
Integer |
In this case, the most interesting handler type is anjay_dm_resource_read_t
,
called whenever the library needs to get a value of a Resource. This might
happen if:
a LwM2M Server sends a Read request,
the Resource is being observed and the library needs to send a Notify message,
value of the Resource is required for the library to function correctly (mostly related to Objects 0 (Security), 1 (Server) and 2 (Access Control)).
The Read handler for our test object might be implemented as follows:
static int test_resource_read(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid,
anjay_rid_t rid,
anjay_riid_t riid,
anjay_output_ctx_t *ctx) {
// These arguments may seem superfluous now, but they will come in handy
// while defining more complex objects
(void) anjay; // unused
(void) obj_ptr; // unused: the object holds no state
(void) iid; // unused: will always be 0 for single-instance Objects
(void) riid; // unused: will always be ANJAY_ID_INVALID
switch (rid) {
case 0:
return anjay_ret_string(ctx, "Test object");
case 1:
return anjay_ret_i64(ctx, avs_time_real_now().since_real_epoch.seconds);
default:
// control will never reach this part due to test_list_resources
return 0;
}
}
What happens here?
rid
value is compared against all known Resource IDs to determine what value should be returned to the library.Resource value is passed to the library via one of
anjay_ret_*
functions, depending on the actual data type of a Resource. The value returned by an appropriate call is then forwarded up - this ensures correct error handling in case anything goes wrong.
The code above makes reference to a test_list_resources
function - this is
another handler, used by the library to determine which resources are supported
and present in a given Object Instance. A LwM2M Client may be able to handle a
Resource that has no default value - in that case, the Resource is always
supported, but becomes present only after a LwM2M Server sets its value
first. Before that, it can be treated as non-existent - it will not be reported
via the Discover operation, for example. Examples include Default Minimum Period
and Default Maximum Period Resources of the LwM2M Server object.
In our case, Resources 0 and 1 are always present in the only Instance we have,
so we can implement the test_resource_preset
handler simply as:
static int test_list_resources(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid,
anjay_dm_resource_list_ctx_t *ctx) {
(void) anjay; // unused
(void) obj_ptr; // unused
(void) iid; // unused
anjay_dm_emit_res(ctx, 0, ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT);
anjay_dm_emit_res(ctx, 1, ANJAY_DM_RES_R, ANJAY_DM_RES_PRESENT);
return 0;
}
The anjay_dm_emit_res()
function, used in the above snippet, is declared as:
void anjay_dm_emit_res(anjay_dm_resource_list_ctx_t *ctx,
anjay_rid_t rid,
anjay_dm_resource_kind_t kind,
anjay_dm_resource_presence_t presence);
It shall be called for all supported resources. The presence
argument
informs the library whether a given resource is present at the moment.
The kind
argument informs the library what kind of operations are legal to
perform on the resource. Valid values are:
ANJAY_DM_RES_R
- read-only single instance resource
ANJAY_DM_RES_W
- write-only single instance resource
ANJAY_DM_RES_RW
- read/write single instance resource
ANJAY_DM_RES_RM
- read-only multiple instance resource
ANJAY_DM_RES_WM
- write-only multiple instance resource
ANJAY_DM_RES_RWM
- read/write multiple instance resource
ANJAY_DM_RES_E
- executable resource
Note that when communicating with a Bootstrap Server may be able to ignore this information, see Bootstrap awareness for more information.
Note that the resources MUST be returned in a strictly ascending, sorted order.
Having the Read and List Resources handlers implemented, one can initialize the
anjay_dm_object_def_t
structure:
static const anjay_dm_object_def_t OBJECT_DEF = {
// Object ID
.oid = 1234,
.handlers = {
// single-instance Objects can use this pre-implemented handler:
.list_instances = anjay_dm_list_instances_SINGLE,
.list_resources = test_list_resources,
.resource_read = test_resource_read
// all other handlers can be left NULL if only Read operation is
// required
}
};
When the Object Definition is ready, the only thing left to do is registering it in the library:
int main(int argc, char *argv[]) {
// ... Anjay initialization
// note: in this simple case the object does not have any state,
// so it's fine to use a plain double pointer to its definition struct
const anjay_dm_object_def_t *test_object_def_ptr = &OBJECT_DEF;
anjay_register_object(anjay, &test_object_def_ptr);
// ... event loop
}
After registering the object, whenever a LwM2M Server issues a Read request on Object 1234 or any of its Resources, Anjay will take care of preparing a response containing the value of requested Resource.
Note
Complete code of this example can be found in examples/tutorial/AT-CustomObjects/read-only subdirectory of main Anjay project repository.