Helium / helium

Files at this revision

API Documentation at this revision

Comitter:
Marc Nijdam
Date:
Tue Sep 05 13:56:03 2017 -0700
Parent:
22:062758492f31
Commit message:
Add configuration API support

Changed in this revision

mbed_lib.json Show annotated file Show diff for this revision Revisions of this file
src/Helium.cpp Show annotated file Show diff for this revision Revisions of this file
src/Helium.h Show annotated file Show diff for this revision Revisions of this file
src/HeliumUtil.cpp Show annotated file Show diff for this revision Revisions of this file
src/HeliumUtil.h Show annotated file Show diff for this revision Revisions of this file
src/helium-client.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_lib.json	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,14 @@
+{
+  "name" : "Helium",
+  "config" : {
+    "debug" : {
+      "help" : "Print debug output",
+      "macro_name" : "DEBUG",
+      "value": 1
+    },
+    "macros" : [],
+    "target_overrides":
+    {
+    }
+  }
+}
--- a/src/Helium.cpp	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/Helium.cpp	Tue Sep 05 13:56:03 2017 -0700
@@ -110,15 +110,15 @@
 // Channel
 //
 
-Channel::Channel(Helium * helium)
+Channel::Channel(Helium * h)
 {
-    _helium = helium;
+    helium = h;
 }
 
 int
 Channel::begin(const char * name, uint16_t * token)
 {
-    return helium_channel_create(&_helium->_ctx, name, strlen(name), token);
+    return helium_channel_create(&helium->_ctx, name, strlen(name), token);
 }
 
 int
@@ -143,7 +143,7 @@
 int
 Channel::send(void const * data, size_t len, uint16_t * token)
 {
-    return helium_channel_send(&_helium->_ctx, _channel_id, data, len, token);
+    return helium_channel_send(&helium->_ctx, _channel_id, data, len, token);
 }
 
 
@@ -162,7 +162,7 @@
 int
 Channel::poll_result(uint16_t token, int8_t * result, uint32_t retries)
 {
-    return helium_channel_poll_result(&_helium->_ctx, token, result, retries);
+    return helium_channel_poll_result(&helium->_ctx, token, result, retries);
 }
 
 int
@@ -171,10 +171,224 @@
                    size_t * used,
                    uint32_t retries)
 {
-    return helium_channel_poll_data(&_helium->_ctx,
+    return helium_channel_poll_data(&helium->_ctx,
                                     _channel_id,
                                     data,
                                     len,
                                     used,
                                     retries);
 }
+
+//
+// Config
+//
+
+Config::Config(Channel * channel)
+{
+    _channel = channel;
+}
+
+int
+Config::get(const char * key, uint16_t * token)
+{
+    return helium_channel_config_get(&_channel->helium->_ctx,
+                                     _channel->_channel_id,
+                                     key,
+                                     token);
+}
+
+
+int
+Config::_get(const char *            config_key,
+             enum helium_config_type config_type,
+             void *                  value,
+             size_t                  value_len,
+             void *                  default_value,
+             size_t                  default_value_len,
+             uint32_t                retries)
+{
+    uint16_t token;
+    int      status = get(config_key, &token);
+    int8_t   result = 0;
+    if (helium_status_OK == status)
+    {
+        status = poll_get_result(token,
+                                 config_key,
+                                 config_type,
+                                 value,
+                                 value_len,
+                                 default_value,
+                                 default_value_len,
+                                 &result,
+                                 retries);
+    }
+    if (helium_status_OK == status && result != 0)
+    {
+        status = helium_status_ERR_COMMUNICATION;
+    }
+    return status;
+}
+
+
+/** Context for the poll_get handlder
+ *
+ * An instance of this if filled in the #Config::poll_get_result
+ * implementation and the result of the poll handler is returned in
+ * the status field
+ */
+struct _poll_get_context
+{
+    //! The config key to look for
+    const char * filter_key;
+    //! The config type to check for
+    enum helium_config_type filter_type;
+    //! The destination buffer
+    void * dest;
+    //! The length of the destination buffer
+    size_t dest_len;
+    //! The default value. Assumed to be the same type as filter_type
+    void * default_value;
+    //! The length of the default_value
+    size_t default_value_len;
+    //! The result status of the result handler
+    enum config_poll_get_status status;
+};
+
+
+static bool
+_poll_get_result_handler(void *                  handler_ctx,
+                         const char *            key,
+                         enum helium_config_type value_type,
+                         void *                  value)
+{
+    struct _poll_get_context * ctx = (struct _poll_get_context *)handler_ctx;
+    size_t                     len = ctx->dest_len;
+    void *                     src = value;
+
+    if (strcmp(ctx->filter_key, key) != 0)
+    {
+        // Not the right key, keep going
+        return true;
+    }
+    if (value_type == ctx->filter_type)
+    {
+        // Found and the right type
+        // Use the given value and the destination buffer size
+        ctx->status = config_status_POLL_FOUND;
+    }
+    else
+    {
+        // Found but not the right type, return an error
+        ctx->status = config_status_POLL_ERR_TYPE;
+        // And use the context's default
+        value_type = ctx->filter_type;
+        // Us the default value as the source
+        src = ctx->default_value;
+        // Check for a shorter default value length (only really valid for
+        // strings)
+        len = len > ctx->default_value_len ? ctx->default_value_len : len;
+    }
+
+    switch (value_type)
+    {
+    case helium_config_bool:
+    case helium_config_f32:
+    case helium_config_i32:
+    case helium_config_str:
+        break;
+    case helium_config_null:
+        ctx->status = config_status_POLL_FOUND_NULL;
+        len         = 0;
+        break;
+    }
+    memcpy(ctx->dest, src, len);
+    return false;
+}
+
+int
+Config::poll_get_result(uint16_t           token,
+                        const char *       config_key,
+                        helium_config_type config_type,
+                        void *             value,
+                        size_t             value_len,
+                        void *             default_value,
+                        size_t             default_value_len,
+                        int8_t *           result,
+                        uint32_t           retries)
+{
+    struct _poll_get_context handler_ctx = {
+        .filter_key        = config_key,
+        .filter_type       = config_type,
+        .dest              = value,
+        .dest_len          = value_len,
+        .default_value     = default_value,
+        .default_value_len = default_value_len,
+        .status            = config_status_POLL_FOUND_NULL,
+    };
+    int status = helium_channel_config_get_poll_result(&_channel->helium->_ctx,
+                                                       token,
+                                                       _poll_get_result_handler,
+                                                       &handler_ctx,
+                                                       result,
+                                                       retries);
+    if (helium_status_OK == status)
+    {
+        status = handler_ctx.status;
+    }
+
+    return status;
+}
+
+int
+Config::set(const char *       config_key,
+            helium_config_type config_type,
+            void *             value,
+            uint16_t *         token)
+{
+    return helium_channel_config_set(&_channel->helium->_ctx,
+                                     _channel->_channel_id,
+                                     config_key,
+                                     config_type,
+                                     value,
+                                     token);
+}
+
+int
+Config::poll_set_result(uint16_t token, int8_t * result, uint32_t retries)
+{
+    return helium_channel_config_set_poll_result(&_channel->helium->_ctx,
+                                                 token,
+                                                 result,
+                                                 retries);
+}
+
+int
+Config::_set(const char *            config_key,
+             enum helium_config_type value_type,
+             void *                  value,
+             uint32_t                retries)
+{
+    uint16_t token;
+    int      status = set(config_key, value_type, value, &token);
+    int8_t   result = 0;
+    if (helium_status_OK == status)
+    {
+        status = poll_set_result(token, &result, retries);
+    }
+    if (helium_status_OK == status && result != 0)
+    {
+        status = helium_status_ERR_COMMUNICATION;
+    }
+    return status;
+}
+
+bool
+Config::is_stale()
+{
+    bool result = false;
+    helium_channel_config_poll_invalidate(&_channel->helium->_ctx,
+                                          _channel->_channel_id,
+                                          &result,
+                                          0);
+    return result;
+}
--- a/src/Helium.h	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/Helium.h	Tue Sep 05 13:56:03 2017 -0700
@@ -110,6 +110,7 @@
     BufferedSerial    serial;
 
     friend class Channel;
+    friend class Config;
 };
 
 /**
@@ -229,9 +230,342 @@
                   size_t * used,
                   uint32_t retries = HELIUM_POLL_RETRIES_5S);
 
+    /** The Helium Atom this channel uses to communicate. */
+    Helium * helium;
+
   private:
-    Helium * _helium;
     int8_t   _channel_id;
+
+    friend class Config;
+};
+
+
+enum config_poll_get_status
+{
+    config_status_POLL_FOUND      = 0,
+    config_status_POLL_FOUND_NULL = -1,
+    config_status_POLL_ERR_TYPE   = -2,
 };
 
+
+/**
+ * \class Config
+ *
+ * \brief A Channel Configuration
+ *
+ * Channels can have configuration data for the Helium Atom
+ * available. Depending on the IoT platform the configuration data is
+ * a representation of device configuration. Other terms used are
+ * device "twins" or "shadows".
+ *
+ * To use a channel configuration construct it with a channel that
+ * supports configuration and use the get methods to retrieve
+ * configuration values from the IoT platform's device
+ * representation. Use the set methods to set values in the IoT
+ * platform's representation of the device.
+ *
+ * Note that most IoT platforms represent sets and gets in different
+ * namespaces. The get methods represent the IoT platform's desired or
+ * expected namespace of the device, while the set methods are
+ * reflected in the actual, or current namespace of the device.
+ *
+ */
+class Config
+{
+  public:
+    /** Construct a Configuration
+     *
+     * @param channel The channel to get/set configuration with
+     */
+    Config(Channel * channel);
+
+    /** Get an integer configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            int32_t *    value,
+            int32_t      default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_i32,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+    /** Get a float configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            float *      value,
+            float        default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_f32,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+
+    /** Get a boolean configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            bool *       value,
+            bool         default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_bool,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+    /** Get a string configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target buffer for the received string
+     * @param value_len The length of the available buffer space
+     * @param default_value The default value to use if not found
+     * @param default_value_len The length of the default_value buffer.
+     *     Note: Ensure to include the trailing NULL in this length
+     *     parameter
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            char *       value,
+            size_t       value_len,
+            char *       default_value,
+            size_t       default_value_len,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_str,
+                    value,
+                    value_len,
+                    default_value,
+                    default_value_len,
+                    retries);
+    }
+
+    /** Send a request for a configuration value.
+     *
+     * Getting a configuration value requires sending a request with
+     * the configuration key and then using the resulting token in a
+     * #poll_get_result() call to wait for a response.
+     *
+     * @param key The configuration key to get
+     * @param[out] token The token representing the response.
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int get(const char * key, uint16_t * token);
+
+    /** Poll the response of a configuration request.
+     *
+     * Polls the given token and validates any response against the
+     * given configuration key and expected type. If these match the
+     * value is copied into the given value buffer.
+     *
+     * Note: The short form methods for getting config values hide
+     * most of the complexity required to make a configuration get
+     * work.
+     *
+     * @param token The token returned from a previous #get() request
+     * @param config_key The configuration key to check for
+     * @param config_type The configuration type to check for
+     * @param[out] value The destination buffer to copy the result into
+     * @param value_len The size of the given destination buffer
+     * @param default_value The default value to use if not found
+     * @param default_value_len The length of the default_value buffer.
+     * @param retries The number of times to retry (optional)
+     * @param[out] result The channel response code.
+     *     0 for no errors, non-0 otherwise
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int poll_get_result(uint16_t                token,
+                        const char *            config_key,
+                        enum helium_config_type config_type,
+                        void *                  value,
+                        size_t                  value_len,
+                        void *                  default_value,
+                        size_t                  default_value_len,
+                        int8_t *                result,
+                        uint32_t retries = HELIUM_POLL_RETRIES_5S);
+
+    /** Set a float configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key, float value, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_f32, &value, retries);
+    }
+
+    /** Set an integer configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key,
+            int32_t      value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_i32, &value, retries);
+    }
+
+    /** Set a boolean configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key, bool value, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_bool, &value, retries);
+    }
+
+    /** Set a string configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key,
+            const char * value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_str, &value, retries);
+    }
+
+    /** Set a null configuration value
+     *
+     * @param key The configuration key to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set_null(const char * key, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_null, NULL, retries);
+    }
+
+    /** Send a request for setting a configuration value.
+     *
+     * Setting a configuration value requires sending a request with
+     * the configuration key, the value type and the value and then
+     * using the resulting token in a #poll_set_result() call to wait
+     * for a response.
+     *
+     * @param key The configuration key to set
+     * @param value_type The type of the configuration value to set
+     * @param value A pointer to the value that needs to be st
+     * @param[out] token The token representing the response.
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char *       key,
+            helium_config_type value_type,
+            void *             value,
+            uint16_t *         token);
+
+    /** Poll the response of a configuration set request.
+     *
+     * Polls the given token and returns the result code of the set
+     * request.
+     *
+     * Note: The short form methods for getting config values hide
+     * most of the complexity required to make a configuration get
+     * work.
+     *
+     * @param token The token returned from a previous #set() request
+     * @param[out] result A pointer to storage for the response code
+     * @param retries The number of times to retry (optional)
+     * @returns A helium_status result code for the actual
+     *     communication part. If the result is helium_status_OK, the
+     *     result code can be used to check if the set was
+     *     successful. The result code will be 0 on success, and
+     *     non-zero otherwise.
+     */
+    int poll_set_result(uint16_t token,
+                        int8_t * result,
+                        uint32_t retries = HELIUM_POLL_RETRIES_5S);
+
+    /** Check whether configuration values are stale.
+     *
+     * Checks whether there has been a system indication that
+     * configuration attributes may have gone stale.
+     *
+     * When this returns true you should assume that any configuration
+     * values you have previously retrieved are no longer valid.
+     *
+     * @returns true if previous configuration values are stale, false
+     *     if not
+     */
+    bool is_stale();
+
+
+  private:
+    int       _get(const char *            config_key,
+                   enum helium_config_type config_type,
+                   void *                  value,
+                   size_t                  value_len,
+                   void *                  default_value,
+                   size_t                  default_value_len,
+                   uint32_t                retries);
+    int       _set(const char *            config_key,
+                   enum helium_config_type value_type,
+                   void *                  value,
+                   uint32_t                retries);
+    Channel * _channel;
+};
+
+
 #endif // HELIUM_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HeliumUtil.cpp	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017, Helium Systems, Inc.
+ * All Rights Reserved. See LICENCE.txt for license information
+ */
+
+#include "mbed.h"
+#include "HeliumUtil.h"
+
+int
+report_status(int status, int result)
+{
+    if (helium_status_OK == status)
+    {
+        if (result == 0)
+        {
+            DBG_PRINTF("Succeeded\n");
+        }
+        else
+        {
+            DBG_PRINTF("Failed - %d\n", result);
+        }
+    }
+    else
+    {
+        DBG_PRINTF("Failed\n");
+    }
+    return status;
+}
+
+
+void
+helium_connect(Helium * helium)
+{
+    while (!helium->connected())
+    {
+        DBG_PRINTF("Connecting - ");
+        int status = helium->connect();
+        if (report_status(status) != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    }
+}
+
+void
+channel_create(Channel * channel, const char * channel_name)
+{
+    int8_t result;
+    int    status;
+    do
+    {
+        // Ensure we're connected
+        helium_connect(channel->helium);
+        DBG_PRINTF("Creating Channel - ");
+        status = channel->begin(channel_name, &result);
+        // Print status and result
+        if (report_status(status, result) != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    } while (helium_status_OK != status || result != 0);
+}
+
+void
+channel_send(Channel *    channel,
+             const char * channel_name,
+             void const * data,
+             size_t       len)
+{
+    int    status;
+    int8_t result;
+
+    do
+    {
+        // Try to send
+        DBG_PRINTF("Sending - ");
+        status = channel->send(data, len, &result);
+        report_status(status, result);
+        // Create the channel if any service errors are returned
+        if (status == helium_status_OK && result != 0)
+        {
+            channel_create(channel, channel_name);
+        }
+        else if (status != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    } while (helium_status_OK != status || result != 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HeliumUtil.h	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017, Helium Systems, Inc.
+ * All Rights Reserved. See LICENCE.txt for license information
+ */
+
+#ifndef HELIUMUTIL_H
+#define HELIUMUTIL_H
+
+#include "Helium.h"
+
+#if DEBUG
+#define DBG_PRINTF(...) printf(__VA_ARGS__)
+#else
+#define DBG_PRINTF(...)
+#endif
+
+int
+report_status(int status, int result = 0);
+
+void
+helium_connect(Helium * helium);
+
+void
+channel_create(Channel * channel, const char * channel_name);
+
+void
+channel_send(Channel *    channel,
+             const char * channel_name,
+             void const * data,
+             size_t       len);
+
+
+#endif /* HELIUMUTIL_H */
--- a/src/helium-client.lib	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/helium-client.lib	Tue Sep 05 13:56:03 2017 -0700
@@ -1,1 +1,1 @@
-https://github.com/helium/helium-client/#2cd344319cf6e704e9ce994a5f9d758e0386f66c
+https://github.com/helium/helium-client/#210d5d20083c65d53bf9286a26d3b7ae969157c0