5.4.2. Single-instance read-only object with an executable resource
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 example you will learn:
what are LwM2M Execute arguments,
how to parse them using Anjay’s API,
how to implement
resource_execute
handler.
The implemented Object will be be based on the previous tutorial Single-instance read-only object, but with additional executable resource:
Name |
Object ID |
Instances |
---|---|---|
Test object |
1234 |
Multiple |
Each Object Instance has three Resources:
Name |
Resource ID |
Operations |
Instances |
Mandatory |
Type |
---|---|---|---|---|---|
Label |
0 |
Read |
Single |
Mandatory |
String |
Value |
1 |
Read |
Single |
Mandatory |
Integer |
Add |
2 |
Execute |
Single |
Mandatory |
Our new Add resource will be used to perform an addition of integers, storing the result in the Value resource. The integers are to be specified as arguments to the LwM2M Execute operation.
5.4.2.1. LwM2M Execute arguments
The LwM2M specification defines a syntax of the Execute argument list as a formal ABNF grammar.
Note
Just to give you an idea of how the syntax looks like (without getting into details of the grammar), here are few examples of valid argument list:
5
,
2='10.3'
,
7,0='https://www.avsystem.com/'
0,1,2,3,4
an empty string.
Also note that argument indices are limited to range 0-9 inclusively.
Note
It is up to the implementation on how to interpret argument lists. One
may, for example, think of each [0-9]='value'
as a mapping between
argument index and a corresponding value.
Then the value of the argument of index k could be applied as a k-th argument to some function for further processing. (we will be, in fact, using this interpretation in this tutorial)
While the grammar itself is rather simple, it could be tedious to implement a correct parser accepting it (with support for all corner-cases). In Anjay there are methods designed specifically to solve this problem, namely:
// ... One that returns the next argument from the Execute argument list
int anjay_execute_get_next_arg(anjay_execute_ctx_t *ctx,
int *out_arg,
bool *out_has_value);
// ... And the one that obtains its value (if any)
int anjay_execute_get_arg_value(anjay_execute_ctx_t *ctx,
size_t *out_bytes_read,
char *out_buf,
size_t buf_size);
They will greatly simplify parsing process, as you will see in the next section.
5.4.2.2. Implementation
We start with adding our Resource to the list of supported Resources:
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) {
// ...
anjay_dm_emit_res(ctx, 2, ANJAY_DM_RES_E, ANJAY_DM_RES_PRESENT);
return 0;
}
Note that the kind
argument is set to ANJAY_DM_RES_E
to signify an
executable resource.
We can now implement resource_execute
handler. Since our new resource will
be used to sum integers we have to store the addition result somewhere. For
simplicity we are going to use a static
variable:
static long addition_result;
And resource_execute
could be implemented as follows:
static int test_resource_execute(anjay_t *anjay,
const anjay_dm_object_def_t *const *obj_ptr,
anjay_iid_t iid,
anjay_rid_t rid,
anjay_execute_ctx_t *ctx) {
switch (rid) {
case 2: {
long sum = 0;
int result;
do {
int arg_value = 0;
if ((result = get_arg_value(ctx, &arg_value)) == 0) {
sum += arg_value;
}
} while (!result);
if (result != ANJAY_EXECUTE_GET_ARG_END) {
return result;
}
addition_result = sum;
return 0;
}
default:
// no other resource is executable
return ANJAY_ERR_METHOD_NOT_ALLOWED;
}
}
Where get_arg_value function is:
static int get_arg_value(anjay_execute_ctx_t *ctx, int *out_value) {
// we expect arguments of form <0-9>='<integer>'
int arg_number;
bool has_value;
int result = anjay_execute_get_next_arg(ctx, &arg_number, &has_value);
// note that we do not check against duplicated argument ids
(void) arg_number;
if (result < 0 || result == ANJAY_EXECUTE_GET_ARG_END) {
// an error occured or there is just nothing more to read
return result;
}
if (!has_value) {
// we expect arguments with values only
return ANJAY_ERR_BAD_REQUEST;
}
char value_buffer[10];
if (anjay_execute_get_arg_value(ctx, NULL, value_buffer,
sizeof(value_buffer))
!= 0) {
// the value must have been malformed or it is too long - either way, we
// don't like it
return ANJAY_ERR_BAD_REQUEST;
}
char *endptr = NULL;
long value = strtol(value_buffer, &endptr, 10);
if (!endptr || *endptr != '\0' || value < INT_MIN || value > INT_MAX) {
// either not an integer or the number is too small / too big
return ANJAY_ERR_BAD_REQUEST;
}
*out_value = (int) value;
return 0;
}
- Now, we need to update
resource_read
handler, so that: it returns 4.05 Method Not Allowed when an attempt to read Executable resource is made,
it returns an addition result, when a Read is performed on a Value resource.
Warning
It is worth to mention that the LwM2M specification explicitly forbids existence of executable resources that could be read at the same time.
So, here is how it could look like:
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) {
// ...
switch (rid) {
// ...
case 1:
return anjay_ret_i64(ctx, addition_result);
case 2:
return ANJAY_ERR_METHOD_NOT_ALLOWED;
default:
// control will never reach this part due to test_list_resources
return 0;
}
}
Finally, we need to revisit an object definition to make sure our execute
handler is set as a resource_execute
implementation:
static const anjay_dm_object_def_t OBJECT_DEF = {
// ...
.handlers = {
// ...
.resource_read = test_resource_read,
.resource_execute = test_resource_execute
// ...
}
// ...
};
And that’s it.
Note
As before, you can find full source-code of this example in examples/tutorial/AT-CustomObjects/read-only-with-executable subdirectory of the Anjay root source dir.
More examples of the Execute handlers can be found in object implementations of the demo client (in the demo subdirectory). Just grep for the resource_execute keyword.