Skip to content

Commit

Permalink
Merge branch 'smartuni:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
TristanImkn authored May 30, 2022
2 parents 43661fd + 2f6fd2c commit 25fa791
Show file tree
Hide file tree
Showing 15 changed files with 1,204 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Nodes/RIOT
1 change: 1 addition & 0 deletions Nodes/modules/puzzle_coap/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base
7 changes: 7 additions & 0 deletions Nodes/modules/puzzle_coap/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# add the module dependencies here
# USEMODULE += random

USEMODULE += gcoap
USEMODULE += cord_ep_standalone
USEMODULE += uri_parser
USEPKG += tinycbor
3 changes: 3 additions & 0 deletions Nodes/modules/puzzle_coap/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Use an immediate variable to evaluate `MAKEFILE_LIST` now
USEMODULE_INCLUDES_puzzle_coap_module := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_puzzle_coap_module)
49 changes: 49 additions & 0 deletions Nodes/modules/puzzle_coap/include/puzzle_coap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

#ifndef EXTERNAL_PUZZLE_COAP_H
#define EXTERNAL_PUZZLE_COAP_H

#include <stdio.h>

#include "uri_parser.h"
#include "net/gcoap.h"
#include "net/cord/common.h"
#include "net/cord/ep.h"
#include "net/sock/util.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PUZZLE_RESOURCE_TYPE ";rt=\"puzzle\";obs"

/* 119 is the max size without touching the gcoap.h */
/* the default CONFIG_GCOAP_PDU_BUF_SIZE is 128 */
#define CBOR_BUF_SIZE 100

/**
* Puzzle info structure
*/
typedef struct {
bool (*get_solved_handler)(void); /**< bool handler thats get called to see if a puzzle is solved */
bool (*get_ready_handler)(void); /**< bool handler thats get called to see if a puzzle is ready or in maintainance */
void (*set_ready_handler)(bool maintainance);/**< void handler thats get called to set a puzzle in ready or maintainance mode*/
const char *resource_dir_uri; /**< char* URI to a CoRE RD */
const char *name; /**< char* name of the puzzle */
} puzzle_t;

void puzzle_init(const puzzle_t *puzzle);
void puzzle_update(void);

#ifdef __cplusplus
}
#endif

/** @} */
#endif /* EXTERNAL_PUZZLE_COAP_H */
280 changes: 280 additions & 0 deletions Nodes/modules/puzzle_coap/puzzle_coap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/*
* Copyright (C) 2022 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @{
*
* @file
* @brief The puzzle_coap module, provides automated resource & RD handling
*
* @author Bennet Blischke <bennet.blischke@haw-hamburg.de>
*
* @}
*/

#include "puzzle_coap.h"
#include "cbor.h"

static const puzzle_t *_puzzle_info;

static ssize_t _puzzle_info_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx);
static ssize_t _puzzle_ready_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx);
static ssize_t _puzzle_maintainance_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx);
static ssize_t _puzzle_encoder(const coap_resource_t *resource, char *buf, size_t maxlen, coap_link_encoder_ctx_t *context);


/* CoAP resources. Must be sorted by path (ASCII order). */
static coap_resource_t _resources[] = {
{ "/node/info", COAP_GET, _puzzle_info_handler, NULL },
{ "/node/maintainance", COAP_PUT, _puzzle_maintainance_handler, NULL },
{ "/node/ready", COAP_PUT, _puzzle_ready_handler, NULL },
};


static gcoap_listener_t _default_listener = {
_resources,
ARRAY_SIZE(_resources),
GCOAP_SOCKET_TYPE_UDP,
_puzzle_encoder,
NULL,
NULL
};

static int _make_sock_ep(sock_udp_ep_t *ep, uri_parser_result_t *uri)
{
ep->port = 0;
if (sock_udp_name2ep(ep, uri->host) < 0) {
return -1;
}

/* if netif not specified in addr */
if ((ep->netif == SOCK_ADDR_ANY_NETIF) && (gnrc_netif_numof() == 1)) {
/* assign the single interface found in gnrc_netif_numof() */
ep->netif = (uint16_t)gnrc_netif_iter(NULL)->pid;
}
ep->family = AF_INET6;
if (uri->port_len == 0) {
ep->port = COAP_PORT;
} else {
ep->port = atoi(uri->port); // Fix me!
}
return 0;
}

static size_t _puzzle_build_cbor_buffer(uint8_t *cborbuf, size_t len)
{
/*
* Encode the puzzle info in cbor:
* {
* "name": <string>,
* "nodeState": <'provisioned'|'standby'>,
* "puzzleState": <'solved'|'ready'|'maintainance'>,
* }
*/
assert(_puzzle_info);

CborEncoder encoder, mapEncoder;
cbor_encoder_init(&encoder, cborbuf, len, 0);
cbor_encoder_create_map(&encoder, &mapEncoder, 3);
cbor_encode_text_stringz(&mapEncoder, "name");
/* Potentially dangerous! The string might not be null terminated */
cbor_encode_text_stringz(&mapEncoder, _puzzle_info->name);

cbor_encode_text_stringz(&mapEncoder, "puzzleState");
/* are we ready or are we in maintainance mode? */
if (_puzzle_info->get_ready_handler()){
if (_puzzle_info->get_solved_handler()){
cbor_encode_text_stringz(&mapEncoder, "solved");
} else {
cbor_encode_text_stringz(&mapEncoder, "ready");
}
} else {
cbor_encode_text_stringz(&mapEncoder, "maintainance");
}

cbor_encode_text_stringz(&mapEncoder, "nodeState");
cbor_encode_text_stringz(&mapEncoder, "standby");

cbor_encoder_close_container(&encoder, &mapEncoder);
return cbor_encoder_get_buffer_size(&encoder, cborbuf);
}


static ssize_t _build_cbor_coap_packet(coap_pkt_t *pdu, uint8_t *buf, size_t len)
{
uint8_t cborbuf[CBOR_BUF_SIZE];

/* set the format option to CBOR */
coap_opt_add_format(pdu, COAP_FORMAT_CBOR);

/* finish the options sections */
/* it is important to keep track of the amount of used bytes (resp_len) */
size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD);
size_t cbor_len = _puzzle_build_cbor_buffer(cborbuf, sizeof(cborbuf));


if (pdu->payload_len >= cbor_len && cbor_len < sizeof(cborbuf)){
memcpy(pdu->payload, cborbuf, cbor_len);
return resp_len + cbor_len;
} else {
/* in this case we use a simple convenience function to create the
* response, it only allows to set a payload and a response code. */
puts("puzzle_coap: msg buffer too small for given puzzle cbor object");
return gcoap_response(pdu, buf, len, COAP_CODE_INTERNAL_SERVER_ERROR);
}
}


void puzzle_init(const puzzle_t *puzzle)
{
assert(puzzle);
assert(puzzle->name);
assert(puzzle->resource_dir_uri);
assert(puzzle->get_solved_handler);
assert(puzzle->get_ready_handler);
assert(puzzle->set_ready_handler);
_puzzle_info = puzzle;

sock_udp_ep_t remote;
uri_parser_result_t uri_result;
assert(uri_parser_is_absolute_string(puzzle->resource_dir_uri));
assert(uri_parser_process_string(&uri_result, puzzle->resource_dir_uri) == 0);
assert(_make_sock_ep(&remote, &uri_result) == 0);

gcoap_register_listener(&_default_listener);

puts("Registering with RD now, this may take a short while...");
if (cord_ep_register(&remote, NULL) != CORD_EP_OK) {
puts("error: registration failed");
return;
} else {
puts("registration successfull");
}
}


void puzzle_update(void)
{
size_t len = 0;
uint8_t buf[CONFIG_GCOAP_PDU_BUF_SIZE];
coap_pkt_t pdu;

switch (gcoap_obs_init(&pdu, buf, sizeof(buf), &_resources[0])) {
case GCOAP_OBS_INIT_OK:
len = _build_cbor_coap_packet(&pdu, buf, sizeof(buf));
if(gcoap_obs_send(buf, len, &_resources[0]) == 0) {
puts("Notification failed to send");
}
break;
case GCOAP_OBS_INIT_ERR:
puts("Notification of the observers failed");
break;
case GCOAP_OBS_INIT_UNUSED:
break;
}
}

/*
* Server callback for /node/info. Accepts only GET.
*
* GET: Returns the puzzle status in CBOR
*/
static ssize_t _puzzle_info_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx)
{
(void)ctx;
/* since the init functions asserts all other members,
* its safe to just assert the base pointer */
assert(_puzzle_info);

/* initialize a new coap response */
gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT);

return _build_cbor_coap_packet(pdu, buf, len);

}

/*
* Server callback for /node/ready. Accepts only PUT.
* Sets the puzzle into the ready state
* PUT: Returns the puzzle status in CBOR
*/
static ssize_t _puzzle_ready_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx)
{
(void)ctx;
/* since the init functions asserts all other members,
* its safe to just assert the base pointer */
assert(_puzzle_info);

unsigned method = coap_method2flag(coap_get_code_detail(pdu));

switch (method) {
case COAP_PUT:
_puzzle_info->set_ready_handler(false);
break;
default:
/* we don't care about anything else */
return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST);
}


/* initialize a new coap response */
gcoap_resp_init(pdu, buf, len, COAP_CODE_CHANGED);

return _build_cbor_coap_packet(pdu, buf, len);

}


/*
* Server callback for /node/maintainance. Accepts only PUT.
* Sets the puzzle into the ready state
* PUT: Returns the puzzle status in CBOR
*/
static ssize_t _puzzle_maintainance_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx)
{
(void)ctx;
/* since the init functions asserts all other members,
* its safe to just assert the base pointer */
assert(_puzzle_info);

unsigned method = coap_method2flag(coap_get_code_detail(pdu));

switch (method) {
case COAP_PUT:
_puzzle_info->set_ready_handler(true);
break;
default:
/* we don't care about anything else */
return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST);
}


/* initialize a new coap response */
gcoap_resp_init(pdu, buf, len, COAP_CODE_CHANGED);

return _build_cbor_coap_packet(pdu, buf, len);

}


/* Adds link format params to resource list */
static ssize_t _puzzle_encoder(const coap_resource_t *resource, char *buf,
size_t maxlen, coap_link_encoder_ctx_t *context) {
ssize_t res = gcoap_encode_link(resource, buf, maxlen, context);

if (res > 0) {
if (strlen(PUZZLE_RESOURCE_TYPE) < (maxlen - res)){
if (buf) {
memcpy(buf+res, PUZZLE_RESOURCE_TYPE, strlen(PUZZLE_RESOURCE_TYPE));
}
res += strlen(PUZZLE_RESOURCE_TYPE);
}
}

return res;
}
35 changes: 35 additions & 0 deletions Nodes/puzzle_example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# name of the application
APPLICATION = example

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= ../RIOT

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

# For the coap_client
USEMODULE += od

USEMODULE += netdev_default
USEMODULE += auto_init_gnrc_netif
USEMODULE += gnrc_ipv6_default
USEMODULE += gnrc_icmpv6_echo
USEMODULE += netutils

USEMODULE += fmt
USEMODULE += xtimer

USEMODULE += puzzle_coap
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

include ../common.mk
Loading

0 comments on commit 25fa791

Please sign in to comment.