Skip to content

Commit

Permalink
lightdb: rework golioth_lightdb_observe*() on top of coap_req
Browse files Browse the repository at this point in the history
This commit is phasing out direct CoAP dependency and low-level CoAP
handling requirement when using lightdb observe API.

Remove golioth_lightdb_observe() API, which would be reserved for
synchronous equivalent of observations. It might be useful at some point to
implement such variant, where first value from lightdb is fetched
synchronously, but subsequent updates are handled with registered callback.
There are no use-cases for such API now, so leaving it as possible future
enhancement.

Add new golioth_lightdb_observe_cb() callback based API, which is replacing
existing golioth_lightdb_observe() API. This works similar to
golioth_lightdb_get_cb() API, with the difference that update notifications
are enabled for observed resource, hence registered callback will be
executed after each notification is received.

Update samples/lightdb/observe and samples/lightdb_led to new API.

Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
  • Loading branch information
mniestroj committed Oct 7, 2022
1 parent 10dc6f1 commit 8f9a2fe
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 187 deletions.
34 changes: 18 additions & 16 deletions include/net/golioth/lightdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,36 +106,38 @@ int golioth_lightdb_set(struct golioth_client *client, const uint8_t *path,
const uint8_t *data, size_t data_len);

/**
* @brief Delete value in Golioth's LightDB
* @brief Observe value in Golioth's LightDB (callback based)
*
* Delete value in LightDB.
* Asynchronously request to observe value in Golioth's LightDB and let @p cb be invoked when such
* value is retrieved (for the first time or after an update) or some error condition happens.
*
* @param client Client instance
* @param path LightDB resource path
* @warning Experimental API
*
* @param[in] client Client instance
* @param[in] path LightDB resource path
* @param[in] format Requested format of payload
* @param[in] cb Callback executed on response received, timeout or error
* @param[in] user_data User data passed to @p cb
*
* @retval 0 On success
* @retval <0 On failure
*/
int golioth_lightdb_delete(struct golioth_client *client, const uint8_t *path);
int golioth_lightdb_observe_cb(struct golioth_client *client, const uint8_t *path,
enum golioth_content_format format,
golioth_req_cb_t cb, void *user_data);

/**
* @brief Observe value in Golioth's LightDB
* @brief Delete value in Golioth's LightDB
*
* Observe value in LightDB and initialize passed CoAP reply handler.
* Delete value in LightDB.
*
* @param client Client instance
* @param path LightDB resource path to be monitored
* @param format Requested format of payload
* @param reply CoAP reply handler object used for notifying about updated
* value
* @param reply_cb Reply handler callback
* @param[in] client Client instance
* @param[in] path LightDB resource path
*
* @retval 0 On success
* @retval <0 On failure
*/
int golioth_lightdb_observe(struct golioth_client *client, const uint8_t *path,
enum golioth_content_format format,
struct coap_reply *reply, coap_reply_t reply_cb);
int golioth_lightdb_delete(struct golioth_client *client, const uint8_t *path);

/** @} */

Expand Down
63 changes: 7 additions & 56 deletions net/golioth/lightdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,63 +131,14 @@ int golioth_lightdb_set(struct golioth_client *client, const uint8_t *path,
GOLIOTH_COAP_REQ_NO_RESP_BODY);
}

static int golioth_coap_observe_init(struct coap_packet *packet,
uint8_t *buffer, size_t buffer_len,
const char *path)
int golioth_lightdb_observe_cb(struct golioth_client *client, const uint8_t *path,
enum golioth_content_format format,
golioth_req_cb_t cb, void *user_data)
{
int err;

err = coap_packet_init(packet, buffer, buffer_len,
COAP_VERSION_1, COAP_TYPE_CON,
COAP_TOKEN_MAX_LEN, coap_next_token(),
COAP_METHOD_GET, coap_next_id());
if (err) {
return err;
}

err = coap_append_option_int(packet, COAP_OPTION_OBSERVE, 0);
if (err) {
LOG_ERR("Unable to add observe option");
return err;
}

err = coap_packet_append_uri_path_from_stringz(packet, path);
if (err) {
LOG_ERR("Unable add uri path to packet");
return err;
}

return 0;
}

int golioth_lightdb_observe(struct golioth_client *client, const uint8_t *path,
enum golioth_content_format format,
struct coap_reply *reply, coap_reply_t reply_cb)
{
struct coap_packet packet;
uint8_t buffer[GOLIOTH_COAP_MAX_NON_PAYLOAD_LEN];
int err;

if (!reply || !reply_cb) {
return -EINVAL;
}

err = golioth_coap_observe_init(&packet, buffer, sizeof(buffer), path);
if (err) {
return err;
}

err = coap_append_option_int(&packet, COAP_OPTION_ACCEPT, format);
if (err) {
LOG_ERR("Unable add content format to packet");
return err;
}

coap_reply_clear(reply);
coap_reply_init(reply, &packet);
reply->reply = reply_cb;

return golioth_send_coap(client, &packet);
return golioth_coap_req_lightdb_cb(client, COAP_METHOD_GET, path, format,
NULL, 0,
cb, user_data,
GOLIOTH_COAP_REQ_OBSERVE);
}

int golioth_lightdb_delete(struct golioth_client *client, const uint8_t *path)
Expand Down
37 changes: 18 additions & 19 deletions samples/lightdb/observe/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,24 +145,20 @@ This is the output from the serial console:

.. code-block:: console
[00:00:01.079,000] <inf> golioth_system: Initializing
[00:00:01.080,000] <inf> net_config: Initializing network
[00:00:01.080,000] <inf> net_config: Waiting interface 1 (0x3ffb01d8) to be up...
[00:00:01.080,000] <inf> esp_event: WIFI_EVENT_STA_START
[00:00:01.080,000] <inf> net_config: Interface 1 (0x3ffb01d8) coming up
[00:00:01.080,000] <inf> net_config: Running dhcpv4 client...
[00:00:01.977,000] <inf> esp_event: WIFI_EVENT_STA_DISCONNECTED
[00:00:04.026,000] <inf> esp_event: WIFI_EVENT_STA_DISCONNECTED
[00:00:09.097,000] <inf> net_dhcpv4: Received: 192.168.0.180
[00:00:09.097,000] <inf> net_config: IPv4 address: 192.168.0.180
[00:00:09.097,000] <inf> net_config: Lease time: 7200 seconds
[00:00:09.097,000] <inf> net_config: Subnet: 255.255.255.0
[00:00:09.097,000] <inf> net_config: Router: 192.168.0.1
[00:00:09.097,000] <dbg> golioth_lightdb.main: Start LightDB observe sample
[00:00:09.097,000] <inf> golioth_system: Starting connect
[00:00:12.366,000] <inf> golioth_system: Client connected!
[00:00:12.422,000] <dbg> golioth_lightdb.on_update: payload: {"m":"original"}
[00:00:22.910,000] <dbg> golioth_lightdb.on_update: payload: {"m":"new"}
[00:00:00.000,000] <inf> golioth_system: Initializing
[00:00:00.000,000] <inf> net_config: Initializing network
[00:00:00.000,000] <inf> net_config: IPv4 address: 192.0.2.1
[00:00:00.000,000] <dbg> golioth_lightdb: main: Start LightDB observe sample
[00:00:00.000,000] <inf> golioth_system: Starting connect
[00:00:00.030,000] <inf> golioth_system: Client connected!
[00:00:00.030,000] <inf> golioth_lightdb: Counter
35 |5
[00:00:04.370,000] <inf> golioth_lightdb: Counter
31 30 |10
[00:00:06.120,000] <inf> golioth_lightdb: Counter
31 31 |11
[00:00:07.620,000] <inf> golioth_lightdb: Counter
31 32 |12
Set the observed value
======================
Expand All @@ -172,7 +168,10 @@ retrieves it every time that it's updated. The value can be updates as such:

.. code-block:: console
goliothctl lightdb set <device-name> /counter -b "{\"m\":\"new\"}"
goliothctl lightdb set <device-name> /counter -b 5
goliothctl lightdb set <device-name> /counter -b 10
goliothctl lightdb set <device-name> /counter -b 11
goliothctl lightdb set <device-name> /counter -b 12
.. _Networking with QEMU: https://docs.zephyrproject.org/3.0.0/guides/networking/qemu_setup.html#networking-with-qemu
Expand Down
2 changes: 1 addition & 1 deletion samples/lightdb/observe/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ CONFIG_MAIN_STACK_SIZE=4096
CONFIG_GOLIOTH=y
CONFIG_GOLIOTH_SYSTEM_CLIENT=y

CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=256
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=2048
8 changes: 5 additions & 3 deletions samples/lightdb/observe/pytest/test_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0

import base64
from contextlib import suppress
import json
import logging
Expand Down Expand Up @@ -53,7 +54,7 @@ def goliothctl_readline(goliothctl, timeout):
def test_lightdb_counter_observe(initial_timeout):
magic_value = 8664100
expected_updates = 5
payload_pattern = re.compile(r"payload: (\d+)")
payload_pattern = re.compile(r"Counter.*|(\d+)")

try:
args = goliothctl_args() + ["logs", "listen", "--json", device_name()]
Expand Down Expand Up @@ -83,10 +84,11 @@ def test_lightdb_counter_observe(initial_timeout):
assert "message" in entry, f"No 'message' in {entry}"

payload = payload_pattern.match(entry["message"])
if not payload:
if "Counter" not in entry["message"]:
continue

counter_logged = int(payload[1])
binary_value = base64.decodebytes(entry["metadata"]["hexdump"].encode())
counter_logged = int(binary_value.decode())

logging.info("Logged %s (expected %s)", counter_logged, counter_expected)

Expand Down
56 changes: 9 additions & 47 deletions samples/lightdb/observe/src/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Golioth, Inc.
* Copyright (c) 2021-2022 Golioth, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -14,34 +14,19 @@ LOG_MODULE_REGISTER(golioth_lightdb, LOG_LEVEL_DBG);
#include <stdlib.h>

static struct golioth_client *client = GOLIOTH_SYSTEM_CLIENT_GET();
static struct coap_reply coap_replies[1];

/*
* This function is registered to be called when the data
* stored at `/counter` changes.
*/
static int on_update(const struct coap_packet *response,
struct coap_reply *reply,
const struct sockaddr *from)
static int counter_handler(struct golioth_req_rsp *rsp)
{
char str[64];
uint16_t payload_len;
const uint8_t *payload;

payload = coap_packet_get_payload(response, &payload_len);
if (!payload) {
LOG_WRN("packet did not contain data");
return -ENOMSG;
}

if (payload_len + 1 > ARRAY_SIZE(str)) {
payload_len = ARRAY_SIZE(str) - 1;
if (rsp->err) {
LOG_ERR("Failed to receive counter value: %d", rsp->err);
return rsp->err;
}

memcpy(str, payload, payload_len);
str[payload_len] = '\0';

LOG_DBG("payload: %s", str);
LOG_HEXDUMP_INF(rsp->data, rsp->len, "Counter");

return 0;
}
Expand All @@ -52,46 +37,24 @@ static int on_update(const struct coap_packet *response,
*/
static void golioth_on_connect(struct golioth_client *client)
{
struct coap_reply *observe_reply;
int err;

coap_replies_clear(coap_replies, ARRAY_SIZE(coap_replies));

observe_reply = coap_reply_next_unused(coap_replies,
ARRAY_SIZE(coap_replies));

/*
* Observe the data stored at `/counter` in LightDB.
* When that data is updated, the `on_update` callback
* will be called.
* This will get the value when first called, even if
* the value doesn't change.
*/
err = golioth_lightdb_observe(client,
GOLIOTH_LIGHTDB_PATH("counter"),
GOLIOTH_CONTENT_FORMAT_APP_JSON,
observe_reply, on_update);
err = golioth_lightdb_observe_cb(client, "counter",
GOLIOTH_CONTENT_FORMAT_APP_JSON,
counter_handler, NULL);

if (err) {
LOG_WRN("failed to observe lightdb path: %d", err);
}
}

/*
* In the `main` function, this function is registered to be
* called when the device receives a packet from the Golioth server.
*/
static void golioth_on_message(struct golioth_client *client,
struct coap_packet *rx)
{
/*
* In order for the observe callback to be called,
* we need to call this function.
*/
coap_response_received(rx, NULL, coap_replies,
ARRAY_SIZE(coap_replies));
}

void main(void)
{
LOG_DBG("Start LightDB observe sample");
Expand All @@ -101,7 +64,6 @@ void main(void)
}

client->on_connect = golioth_on_connect;
client->on_message = golioth_on_message;
golioth_system_client_start();

while (true) {
Expand Down
2 changes: 1 addition & 1 deletion samples/lightdb_led/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ CONFIG_GOLIOTH=y
CONFIG_GOLIOTH_SYSTEM_CLIENT=y

CONFIG_QCBOR=y
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=1024
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=2048

0 comments on commit 8f9a2fe

Please sign in to comment.