man/coap_observe.txt.in: New man page covering Observe usage

This commit is contained in:
Jon Shallow committed 2018-04-07 11:34:44 +01:00
1 parent 20cd610c03
commit 747205fb93
1 file changed
+372
+372
View File
@@ -0,0 +1,372 @@
// -*- mode:doc; -*-
// vim: set syntax=asciidoc,tw=0:
coap_observe(3)
=================
:doctype: manpage
:man source: coap_observe
:man version: @PACKAGE_VERSION@
:man manual: libcoap Manual
NAME
----
coap_observe, coap_resource_set_get_observable, coap_resource_notify_observers,
coap_resource_get_uri_path, coap_find_observer - work with CoAP observe
SYNOPSIS
--------
*#include <coap/coap.h>*
*void coap_resource_set_get_observable(coap_resource_t *_resource_,
int _mode_);*
*int coap_resource_notify_observers(coap_resource_t *_resource_,
const str *_query_);*
*const str *coap_resource_get_uri_path(coap_resource_t *_resource_);*
*coap_subscription_t *coap_find_observer(coap_resource_t *_resource_,
coap_session_t *_session_, const str *_token_);*
Link with -lcoap-@LIBCOAP_API_VERSION@.
DESCRIPTION
-----------
RFC 7641 extends the CoAP protocol to be able to monitor the state of a
resource over time.
This enables clients to "observe" resources with a defined query, i.e., to
retrieve a representation of a resource and keep this representation updated
by the server over a period of time.
The server has to flag a resource as "observable", and then the client has
to request in a GET request that it wants to observe this resource by the use
of the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_ESTABLISH.
Optionally, the client can specify query options for the resource.
The underlying library adds in and removes "subscribers" to the resource as
appropriate in the server side logic.
Within the server application, it needs to determine that there is a change of
state of the resource under observation, and then cause the CoAP library
layer to initiate a "fake GET request" so that an unsolicited GET response
gets sent back to all the clients that are observing the resource. The
appropriate GET handler within the server application is called to fill in the
response packet with the appropriate information.
The call to coap_write() in the main server application i/o loop will do all
the necessary processing of sending any outstanding "fake GET requests".
Whenever the server sends a copy of the state of the "observed" resource to
the client, it will use the same token used by the client when the client
requested the "observe". The client will receive this unsolicited response
in the handler defined by coap_register_response_handler(). It is the
responsibility of the client application to match the supplied token and
update the appropriate internal information.
The *coap_resource_set_get_observable*() function enables or disables the
observable status of the _resource_ by the setting of _mode_. If _mode_ is
1, then the _resource_ is observable. If _mode_ is 0, then the
_resource_ is no longer observable.
The *coap_resource_notify_observers*() function needs to be called whenever the
server application determines that there has been a change to the state of
_resource_, possibly only matching a specific _query_ if _query_ is not NULL.
The *coap_resource_get_uri_path*() function is used to obtain the UriPath of
the _resource_ definion.
The *coap_find_observer*() function is used to check whether the current GET
request as determined from _resource_, _session_ and _token_ is currently
being observed or not.
RETURN VALUES
-------------
The *coap_resource_get_uri_path*() function
returns the uri_path or NULL if there was a failure.
The *coap_find_observer*() function
returns the subscription or NULL if there was a failure.
The *coap_resource_set_get_observable*() function return 0 on failure, 1 on
success.
EXAMPLES
--------
*Server Initialize Resources*
[source, c]
--
#include <coap/coap.h>
external coap_resource_t *time_resource;
static void
init_resources(coap_context_t *ctx)
{
coap_resource_t *r;
/* Create a resource to return return or update time */
r = coap_resource_init((unsigned char *)"time", 4,
COAP_RESOURCE_FLAGS_NOTIFY_CON);
/* We are using a generic GET handler here */
coap_register_handler(r, COAP_REQUEST_GET, hnd_get_generic);
coap_resource_set_get_observable(r, 1);
coap_add_resource(ctx, r);
time_resource = 1;
}
--
*Server GET Callback Handlers*
[source, c]
--
#include <coap/coap.h>
/* specific GET "time" handler, called from hnd_get_generic() */
static void
hnd_get_time(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, str *token, str *query,
coap_pdu_t *response) {
unsigned char buf[40];
size_t len;
time_t now;
coap_tick_t t;
/* Define what the response code is going to be */
response->code = COAP_RESPONSE_CODE(205);
/* If the resource is being Observed, return with Observe set */
if (coap_find_observer(resource, session, token)) {
coap_add_option(response,
COAP_OPTION_OBSERVE,
coap_encode_var_bytes(buf, resource->observe), buf);
}
/* Response is in plain text format */
coap_add_option(response,
COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);
/* Report on how long this response is valid for */
coap_add_option(response,
COAP_OPTION_MAXAGE,
coap_encode_var_bytes(buf, 0x01), buf);
now = time(NULL);
if (query != NULL && memcmp(query->s, "secs", min(4, query->length)) == 0) {
/* Output secs since Jan 1 1970 */
len = snprintf((char *)buf, sizeof(buf), "%lu", now);
coap_add_data(response, len, buf);
} else {
/* Output human-readable time */
struct tm *tmp;
tmp = gmtime(&now);
len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
coap_add_data(response, len, buf);
}
}
static void
hnd_get_generic(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, str *token, str *query,
coap_pdu_t *response) {
const str *uri_path = coap_resource_get_uri_path(resource);
if (!uri_path) {
/* Unexpected Failure */
response->code = COAP_RESPONSE_CODE(400);
return;
}
/* Is this the "time" resource" ? */
if (!memcmp((char*)uri_path->s, "time", sizeof("time")) {
return hnd_get_time(ctx, resource, session, request, token, query,
response);
}
/* Other resources code */
/* Failure response */
response->code = COAP_RESPONSE_CODE(400);
}
--
*Server Main Loop*
[source, c]
--
#include <coap/coap.h>
coap_resource_t *time_resource = NULL;
int main(int argc, char *argv[]){
coap_contest_t *ctx = NULL;
unsigned wait_ms;
/* Other Set up Code */
init_resources(ctx);
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
while (1) {
int result = coap_run_once(ctx, wait_ms);
if ( result < 0 ) {
break;
} else if ((unsigned)result < wait_ms) {
wait_ms -= result;
} else {
if (check_if_time_resource_has_changed(time_resource)) {
coap_resource_notify_observers(time_resource, NULL);
}
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
}
}
}
--
*Client Request Setup*
[source, c]
--
#include <coap/coap.h>
unsigned char msgtype = COAP_MESSAGE_CON; /* usually, requests are sent confirmable */
typedef struct coap_list_t {
struct coap_list_t *next;
char data[];
} coap_list_t;
static int coap_insert(coap_list_t **head, coap_list_t *node) {
if (!node) {
coap_log(LOG_WARNING, "node not specified\n");
} else {
/* must append at the list end to avoid re-ordering of
* options during sort */
LL_APPEND((*head), node);
}
return node != NULL;
}
static coap_list_t * new_option_node(uint16_t key, size_t length,
unsigned char *data) {
coap_list_t *node;
node = coap_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length);
if (node) {
coap_option *option;
option = (coap_option *)(node->data);
COAP_OPTION_KEY(*option) = key;
COAP_OPTION_LENGTH(*option) = length;
memcpy(COAP_OPTION_DATA(*option), data, length);
} else {
coap_log(LOG_DEBUG, "new_option_node: malloc\n");
}
return node;
}
static int order_opts(void *a, void *b) {
coap_option *o1, *o2;
if (!a || !b)
return a < b ? -1 : 1;
o1 = (coap_option *)(((coap_list_t *)a)->data);
o2 = (coap_option *)(((coap_list_t *)b)->data);
return (COAP_OPTION_KEY(*o1) < COAP_OPTION_KEY(*o2))
? -1
: (COAP_OPTION_KEY(*o1) != COAP_OPTION_KEY(*o2));
}
static coap_pdu_t *
coap_new_request(coap_context_t *ctx, coap_session_t *session, char m,
coap_list_t **options, unsigned char *data, size_t length, int observe) {
static unsigned int token = 0;
coap_pdu_t *pdu;
coap_list_t *opt;
(void)ctx;
if (!(pdu = coap_new_pdu(session)))
return NULL;
pdu->type = msgtype;
pdu->tid = coap_new_message_id(session);
pdu->code = m;
/* Create uniqueness for this request */
token++;
if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) {
coap_log(LOG_DEBUG, "cannot add token to request\n");
}
if (m == COAP_REQUEST_GET && observe) {
/* Indicate that we want to observe this resource */
coap_insert(options, new_option_node(COAP_OPTION_OBSERVE,
COAP_OBSERVE_ESTABLISH, NULL));
}
if (options) {
/* sort options for delta encoding */
LL_SORT((*options), order_opts);
LL_FOREACH((*options), opt) {
coap_option *o = (coap_option *)(opt->data);
coap_add_option(pdu,
COAP_OPTION_KEY(*o),
COAP_OPTION_LENGTH(*o),
COAP_OPTION_DATA(*o));
}
}
if (length) {
coap_add_data(pdu, length, data);
}
return pdu;
}
--
SEE ALSO
--------
*coap_attribute*(3), *coap_context*(3), *coap_handler*(3) and
*coap_resource*(3)
FURTHER INFORMATION
-------------------
"RFC7252: The Constrained Application Protocol (CoAP)"
"RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)"
BUGS
----
Please report bugs on the mailing list for libcoap:
libcoap-developers@lists.sourceforge.net
AUTHORS
-------
The libcoap project <libcoap-developers@lists.sourceforge.net>