mirror of
https://github.com/obgm/libcoap.git
synced 2026-11-08 18:32:53 +03:00
man/coap_observe.txt.in: New man page covering Observe usage
This commit is contained in:
1 parent
20cd610c03
commit
747205fb93
1 file changed
+372
@@ -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>
|
||||
Reference in new issue
Block a user