utility/msgpack/read_write_stream.c

/*******************************************************************************
* # License
* Copyright 2019 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "common.h"
#define ADC_READINGS 5
#define TCP_ECHO_SERVER_HOST "test.zentri.com"
#define TCP_ECHO_SERVER_PORT 50007
typedef struct
{
gos_handle_t handle;
uint32_t bytes_written;
} write_context_t;
/*************************************************************************************************
* This demonstrates how to serialize/de-serialize a MessagePack data stream.
*
* The stream is written to a TCP echo server. i.e. everything that is written to the
* server is echoed back to the device.
*
* The format of the data this example uses is as follows:
* (Note: While described in JSON format, the data is actually MessagePack formatted)
*
* {
* "time-ms" : <uint64:time in ms>,
* "time-utc" : "<string: time as UTC string>",
* "tick" : <uint32: RTOS tick count>,
* "mac" : "<binary string: MAC address as binary string>",
* "gpio-list": [ <array:bool: gpio values> ],
* "adc-readings" : {
* "adc-X" :
* {
* "format" : "<string:mV/raw>",
* "value" : <uint32:value>
* }
* ...
* }
* }
*/
gos_result_t read_write_stream(void)
{
gos_result_t result;
uint32_t serialized_data_length = 0;
GOS_LOG("\r\n\r\nSerialize/deserialize data stream ...");
if (GOS_FAILED(result, gos_tcp_connect(gos_network_get_default_interface(), TCP_ECHO_SERVER_HOST, TCP_ECHO_SERVER_PORT, &handle)))
{
GOS_LOG("Failed to connect to TCP echo server. Err code: %d", result);
}
if (GOS_FAILED(result, serialize_data(handle, &serialized_data_length)))
{
GOS_LOG("Failed to serialize data. Err code: %d", result);
}
else if (GOS_FAILED(result, deserialize_and_print_data(handle, serialized_data_length)))
{
GOS_LOG("Failed to deserialize data. Err code: %d", result);
}
return result;
}
/*************************************************************************************************
* Serial data using MessagePack format
*/
static gos_result_t serialize_data(gos_handle_t handle, uint32_t *buffer_length_ptr)
{
write_context_t write_context =
{
.handle = handle,
.bytes_written = 0
};
MSGPACK_INIT_WITH_WRITER(serialize_context, tcp_stream_writer, &write_context);
// The root dictionary has 6 key/value pairs
GOS_VERIFY(gos_msgpack_write_dict_marker(&serialize_context, 6));
// Write the current time
{
gos_utc_ms_t current_time_ms;
gos_time_get_current_utc_ms(&current_time_ms, false);
// Note: 'long' = 64bit
GOS_VERIFY(gos_msgpack_write_dict_ulong(&serialize_context, "time-ms", current_time_ms));
}
// Write UTC time
{
gos_iso8601_str_t current_time_utc;
gos_time_get_current_utc_as_iso8601_str(&current_time_utc, false);
GOS_VERIFY(gos_msgpack_write_dict_str(&serialize_context, "time-utc", (const char*)&current_time_utc));
}
// Write current RTOS tick
{
const uint32_t now = gos_rtos_get_time();
// Note: 'int' = 32bit
GOS_VERIFY(gos_msgpack_write_dict_uint(&serialize_context, "tick", now));
}
// Write MAC address as binary string
{
char mac_str[24];
gos_mac_t mac_address;
str_to_mac(mac_str, &mac_address);
GOS_VERIFY(gos_msgpack_write_dict_bin(&serialize_context, "mac", mac_address.octet, sizeof(mac_address.octet)));
}
// Write the current value of all GPIOs as an array
{
// Write the array marker
GOS_VERIFY(gos_msgpack_write_dict_array(&serialize_context, "gpio-list", GOS_GPIO_MAX));
// Write the array entries
for (gos_gpio_t gpio = 0; gpio < GOS_GPIO_MAX; ++gpio)
{
GOS_VERIFY(gos_msgpack_write_bool(&serialize_context, gos_gpio_get(gpio)));
}
}
// Write the ADCs object entries
{
// The sub-dictionary will have ADC_READINGS key/value pairs
GOS_VERIFY(gos_msgpack_write_dict_dict(&serialize_context, "adc-readings", ADC_READINGS));
for (int adc_reading_num = 0; adc_reading_num < ADC_READINGS; ++adc_reading_num)
{
uint16_t reading_value = 0;
char key_str[16];
//gos_adc_sample_type_t sample_type = ((adc_reading_num & 0x1) == 0) ? GOS_ADC_SAMPLE_TYPE_RAW : GOS_ADC_SAMPLE_TYPE_VOLTAGE;
const char *sample_type_str = ((adc_reading_num & 0x1) == 0) ? "raw" : "mV";
// Get the current value of the ADC
//gos_adc_sample(PLATFORM_STD_ADC, &reading_value, sample_type);
// Generate the entry key string
sprintf(key_str, "adc-%d", adc_reading_num);
// Write the adc sub-sub-dictionary which contains 2 entries
GOS_VERIFY(gos_msgpack_write_dict_dict(&serialize_context, key_str, 2));
// Write the 'format' entry
GOS_VERIFY(gos_msgpack_write_dict_str(&serialize_context, "format", sample_type_str));
// Write the value entry
GOS_VERIFY(gos_msgpack_write_dict_uint(&serialize_context, "value", reading_value));
}
}
// Write one more time to flush the buffer
gos_tcp_write(handle, NULL, 0, false);
// Return the total length of the serialized buffer
*buffer_length_ptr = write_context.bytes_written;
return GOS_SUCCESS;
}
/*************************************************************************************************
* Deserialize the msgpack formatted buffer and print the contents
*/
static gos_result_t deserialize_and_print_data(gos_handle_t handle, uint32_t length)
{
gos_result_t result;
gos_msgpack_object_t *root_ptr = NULL;
uint8_t *buffer = NULL;
if (GOS_FAILED(result, gos_malloc("msgpack", &buffer, length)))
{
GOS_LOG("Failed to alloc RX buffer");
goto exit;
}
else if (GOS_FAILED(result, read_rx_data(handle, buffer, length)))
{
GOS_LOG("Failed to read RX data. Err code: %d", result);
goto exit;
}
// Dump the 'packed' binary data
gos_dump_buffer(buffer, length, "'Packed' binary dump:", GOS_DUMP_FLAGS(16, 1, LITTLE, ADD_SPACE, PRINT_ADDRESSES, PRINT_ASCII));
if (GOS_FAILED(result, gos_msgpack_deserialize_with_buffer(&root_ptr, buffer, length, 0)))
{
GOS_LOG("Failed to deserialize msgpack buffer. Err code: %d", result);
goto exit;
}
// Retrieve and print the 'time-ms' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "time-ms");
if(obj == NULL)
{
GOS_LOG("WARN: 'time-ms' entry not found");
}
else
{
char str_buffer[32];
uint64_t time_ms;
MSGPACK_ULONG(obj, &time_ms);
GOS_LOG("'time-ms' : %s", uint64_to_str(time_ms, str_buffer));
}
}
// Retrieve and print the 'time-utc' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "time-utc");
if (obj == NULL)
{
GOS_LOG("WARN: 'time-utc' entry not found");
}
else if (MSGPACK_IS_STR(obj) == 0)
{
GOS_LOG("WARN: 'time-utc' entry is NOT a string object");
}
else
{
char str_buffer[32];
GOS_LOG("'time-utc' : %s", MSGPACK_STR(obj, str_buffer, sizeof(str_buffer)));
}
}
// Retrieve and print the 'tick' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "tick");
if (obj == NULL)
{
GOS_LOG("WARN: 'tick' entry not found");
}
else
{
GOS_LOG("'tick' : %u", MSGPACK_UINT(obj));
}
}
// Retrieve and print the 'mac' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "mac");
if (obj == NULL)
{
GOS_LOG("WARN: 'mac' entry not found");
}
else if (MSGPACK_IS_BIN(obj) == 0)
{
GOS_LOG("WARN: 'mac' entry is NOT a binary string object");
}
else
{
gos_mac_t mac_address;
char mac_str[20];
memcpy(mac_address.octet, MSGPACK_BIN_VALUE(obj), MSGPACK_BIN_LENGTH(obj));
GOS_LOG("'mac' : %s", mac_to_str(&mac_address, mac_str));
}
}
// Retrieve and print the 'gpio-list' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "gpio-list");
if (obj == NULL)
{
GOS_LOG("WARN: 'gpio-list' entry not found");
}
else if (MSGPACK_IS_ARRAY(obj) == 0)
{
GOS_LOG("WARN: 'gpio-list' entry is NOT an array object");
}
else
{
for (gos_gpio_t gpio = 0; gpio < array_obj->count; ++gpio)
{
const gos_msgpack_object_t *entry = MSGPACK_ARRAY_GET_OBJECT(array_obj, gpio);
if (entry == NULL)
{
GOS_LOG("WARN: 'gpio-list' entry not found");
}
else if(MSGPACK_IS_BOOL(entry) == 0)
{
GOS_LOG("WARN: 'gpio-list' entry is NOT a boolean object");
}
else
{
GOS_LOG("GPIO-%d : %d", gpio, MSGPACK_BOOL(entry));
}
}
}
}
// Retrieve and print the 'adc-readings' entry
{
const gos_msgpack_object_t *obj = MSGPACK_DICT_GET_OBJECT(root_ptr, "adc-readings");
if (obj == NULL)
{
GOS_LOG("WARN: 'adc-readings' entry not found");
}
else if (MSGPACK_IS_DICT(obj) == 0)
{
GOS_LOG("WARN: 'adc-readings' entry is NOT a dict object");
}
else
{
for (gos_gpio_t reading_num = 0; reading_num < dict_obj->count; ++reading_num)
{
char key_str[16];
sprintf(key_str, "adc-%d", reading_num);
const gos_msgpack_object_t *entry = MSGPACK_DICT_GET_OBJECT(dict_obj, key_str);
if (entry == NULL)
{
GOS_LOG("WARN: 'adc-readings' entry not found");
}
else if (MSGPACK_IS_DICT(entry) == 0)
{
GOS_LOG("WARN: 'adc-readings' entry is NOT a dict object");
}
else
{
char format_str[8];
const gos_msgpack_object_t *format_entry = MSGPACK_DICT_GET_OBJECT(entry, "format");
const gos_msgpack_object_t *value_entry = MSGPACK_DICT_GET_OBJECT(entry, "value");
GOS_LOG("%s : %u (%s)", key_str, MSGPACK_UINT(value_entry), MSGPACK_STR(format_entry, format_str, sizeof(format_str)));
}
}
}
}
exit:
if (buffer != NULL)
{
gos_free(buffer);
}
return result;
}
/*************************************************************************************************/
static gos_result_t tcp_stream_writer(void *user, const void *data, uint32_t length)
{
write_context_t *write_context = user;
write_context->bytes_written += length;
return gos_tcp_write(write_context->handle, data, length, true);
}
/*************************************************************************************************/
static gos_result_t read_rx_data(gos_handle_t handle, uint8_t *buffer, int32_t length)
{
uint8_t *ptr = buffer;
while (length > 0)
{
uint32_t bytes_read;
if (GOS_FAILED(result, gos_tcp_read(handle, ptr, length, &bytes_read)))
{
if (result == GOS_TIMEOUT)
{
continue;
}
else
{
break;
}
}
length -= bytes_read;
ptr += bytes_read;
}
return result;
}