Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: mbed-cloud-client/source/ServiceClient.cpp
- Revision:
- 0:276e7a263c35
diff -r 000000000000 -r 276e7a263c35 mbed-cloud-client/source/ServiceClient.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-cloud-client/source/ServiceClient.cpp Mon Jul 02 06:30:39 2018 +0000
@@ -0,0 +1,527 @@
+// ----------------------------------------------------------------------------
+// Copyright 2016-2017 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+
+// Note: this macro is needed on armcc to get the the PRI*32 macros
+// from inttypes.h in a C++ code.
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <inttypes.h>
+
+#include <string>
+#include "include/ServiceClient.h"
+#include "include/CloudClientStorage.h"
+#include "include/UpdateClientResources.h"
+#include "include/UpdateClient.h"
+#include "factory_configurator_client.h"
+#include "mbed-trace/mbed_trace.h"
+#include <assert.h>
+
+#define TRACE_GROUP "mClt"
+
+#define CONNECT 0
+#define ERROR_UPDATE "Update has failed, check MbedCloudClient::Error"
+
+/* lookup table for printing hexadecimal values */
+const uint8_t ServiceClient::hex_table[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+ServiceClient::ServiceClient(ServiceClientCallback& callback)
+: _service_callback(callback),
+ _service_uri(NULL),
+ _stack(NULL),
+ _client_objs(NULL),
+ _current_state(State_Init),
+ _event_generated(false),
+ _state_engine_running(false),
+#ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+ _setup_update_client(false),
+#endif
+ _connector_client(this)
+{
+}
+
+ServiceClient::~ServiceClient()
+{
+}
+
+void ServiceClient::initialize_and_register(M2MObjectList& client_objs)
+{
+ tr_debug("ServiceClient::initialize_and_register");
+ if(_current_state == State_Init ||
+ _current_state == State_Unregister ||
+ _current_state == State_Failure) {
+ _client_objs = &client_objs;
+
+#ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+ tr_debug("ServiceClient::initialize_and_register: update client supported");
+
+ if(!_setup_update_client) {
+ _setup_update_client = true;
+
+#ifdef MBED_CLOUD_DEV_UPDATE_ID
+ /* Overwrite values stored in KCM. This is for development only
+ since these IDs should be provisioned in the factory.
+ */
+ tr_debug("ServiceClient::initialize_and_register: update IDs defined");
+
+ /* Delete VendorId */
+ delete_config_parameter("mbed.VendorId");
+ /* Store Vendor Id to mbed.VendorId. No conversion is performed. */
+ set_device_resource_value(M2MDevice::Manufacturer,
+ (const char*) arm_uc_vendor_id,
+ arm_uc_vendor_id_size);
+
+ /* Delete ClassId */
+ delete_config_parameter("mbed.ClassId");
+ /* Store Class Id to mbed.ClassId. No conversion is performed. */
+ set_device_resource_value(M2MDevice::ModelNumber,
+ (const char*) arm_uc_class_id,
+ arm_uc_class_id_size);
+#endif /* MBED_CLOUD_DEV_UPDATE_ID */
+
+#ifdef ARM_UPDATE_CLIENT_VERSION
+ /* Inject Update Client version number if no other software
+ version is present in the KCM.
+ */
+ tr_debug("ServiceClient::initialize_and_register: update version defined");
+;
+ const size_t buffer_size = 16;
+ uint8_t buffer[buffer_size];
+ size_t size = 0;
+
+ /* check if software version is already set */
+ ccs_status_e status = get_config_parameter(KEY_DEVICE_SOFTWAREVERSION,
+ buffer, buffer_size, &size);
+
+ if (status == CCS_STATUS_KEY_DOESNT_EXIST) {
+ tr_debug("ServiceClient::initialize_and_register: insert update version");
+
+ /* insert value from Update Client Common */
+ set_config_parameter(KEY_DEVICE_SOFTWAREVERSION,
+ (const uint8_t*) ARM_UPDATE_CLIENT_VERSION,
+ sizeof(ARM_UPDATE_CLIENT_VERSION));
+ }
+#endif /* ARM_UPDATE_CLIENT_VERSION */
+
+ /* Update Client adds the OMA LWM2M Firmware Update object */
+ UpdateClient::populate_object_list(*_client_objs);
+
+ /* Initialize Update Client */
+ FP1<void, int32_t> callback(this, &ServiceClient::update_error_callback);
+ UpdateClient::UpdateClient(callback);
+ }
+#endif /* MBED_CLOUD_CLIENT_SUPPORT_UPDATE */
+
+ /* Device Object is mandatory.
+ Get instance and add it to object list
+ */
+ M2MDevice *device_object = device_object_from_storage();
+
+ if (device_object) {
+ /* Publish device object resource to mds */
+ M2MResourceList list = device_object->object_instance()->resources();
+ if(!list.empty()) {
+ M2MResourceList::const_iterator it;
+ it = list.begin();
+ for ( ; it != list.end(); it++ ) {
+ (*it)->set_register_uri(true);
+ }
+ }
+
+ /* Add Device Object to object list. */
+ _client_objs->push_back(device_object);
+ }
+
+ internal_event(State_Bootstrap);
+ } else if (_current_state == State_Success) {
+ state_success();
+ }
+}
+
+ConnectorClient &ServiceClient::connector_client()
+{
+ return _connector_client;
+}
+
+const ConnectorClient &ServiceClient::connector_client() const
+{
+ return _connector_client;
+}
+
+// generates an internal event. called from within a state
+// function to transition to a new state
+void ServiceClient::internal_event(StartupMainState new_state)
+{
+ tr_debug("ServiceClient::internal_event: state: %d -> %d", _current_state, new_state);
+
+ _event_generated = true;
+ _current_state = new_state;
+
+ if (!_state_engine_running) {
+ state_engine();
+ }
+}
+
+// the state engine executes the state machine states
+void ServiceClient::state_engine(void)
+{
+ tr_debug("ServiceClient::state_engine");
+
+ // this simple flagging gets rid of recursive calls to this method
+ _state_engine_running = true;
+
+ // while events are being generated keep executing states
+ while (_event_generated) {
+ _event_generated = false; // event used up, reset flag
+
+ state_function(_current_state);
+ }
+
+ _state_engine_running = false;
+}
+
+void ServiceClient::state_function(StartupMainState current_state)
+{
+ switch (current_state) {
+ case State_Init: // -> Goes to bootstrap state
+ case State_Bootstrap: // -> State_Register OR State_Failure
+ state_bootstrap();
+ break;
+ case State_Register: // -> State_Succes OR State_Failure
+ state_register();
+ break;
+ case State_Success: // return success to user
+ state_success();
+ break;
+ case State_Failure: // return error to user
+ state_failure();
+ break;
+ case State_Unregister: // return error to user
+ state_unregister();
+ break;
+ }
+}
+
+void ServiceClient::state_bootstrap()
+{
+ tr_info("ServiceClient::state_bootstrap()");
+ bool credentials_ready = _connector_client.connector_credentials_available();
+ bool bootstrap = _connector_client.use_bootstrap();
+ tr_info("ServiceClient::state_bootstrap() - lwm2m credentials available: %d", credentials_ready);
+ tr_info("ServiceClient::state_bootstrap() - use bootstrap: %d", bootstrap);
+ if (credentials_ready || !bootstrap) {
+ internal_event(State_Register);
+ } else {
+ _connector_client.start_bootstrap();
+ }
+}
+
+void ServiceClient::state_register()
+{
+ tr_info("ServiceClient::state_register()");
+ _connector_client.start_registration(_client_objs);
+}
+
+void ServiceClient::registration_process_result(ConnectorClient::StartupSubStateRegistration status)
+{
+ tr_debug("ServiceClient::registration_process_result(): status: %d", status);
+ if (status == ConnectorClient::State_Registration_Success) {
+ internal_event(State_Success);
+ } else if(status == ConnectorClient::State_Registration_Failure ||
+ status == ConnectorClient::State_Bootstrap_Failure){
+ internal_event(State_Failure); // XXX: the status should be saved to eg. event object
+ }
+ if(status == ConnectorClient::State_Bootstrap_Success) {
+ internal_event(State_Register);
+ }
+ if(status == ConnectorClient::State_Unregistered) {
+ internal_event(State_Unregister);
+ }
+ if (status == ConnectorClient::State_Registration_Updated) {
+ _service_callback.complete(ServiceClientCallback::Service_Client_Status_Register_Updated);
+ }
+}
+
+void ServiceClient::connector_error(M2MInterface::Error error, const char *reason)
+{
+ tr_error("ServiceClient::connector_error() error %d", (int)error);
+ if (_current_state == State_Register) {
+ registration_process_result(ConnectorClient::State_Registration_Failure);
+ }
+ else if (_current_state == State_Bootstrap) {
+ registration_process_result(ConnectorClient::State_Bootstrap_Failure);
+ }
+ _service_callback.error(int(error),reason);
+ internal_event(State_Failure);
+}
+
+void ServiceClient::value_updated(M2MBase *base, M2MBase::BaseType type)
+{
+ tr_debug("ServiceClient::value_updated()");
+ _service_callback.value_updated(base, type);
+}
+
+void ServiceClient::state_success()
+{
+ tr_info("ServiceClient::state_success()");
+ // this is verified already at client API level, but this might still catch some logic failures
+ _service_callback.complete(ServiceClientCallback::Service_Client_Status_Registered);
+}
+
+void ServiceClient::state_failure()
+{
+ tr_error("ServiceClient::state_failure()");
+ _service_callback.complete(ServiceClientCallback::Service_Client_Status_Failure);
+}
+
+void ServiceClient::state_unregister()
+{
+ tr_debug("ServiceClient::state_unregister()");
+ _service_callback.complete(ServiceClientCallback::Service_Client_Status_Unregistered);
+}
+
+M2MDevice* ServiceClient::device_object_from_storage()
+{
+ M2MDevice *device_object = M2MInterfaceFactory::create_device();
+ if (device_object == NULL) {
+ return NULL;
+ }
+
+ const size_t buffer_size = 128;
+ uint8_t buffer[buffer_size];
+ size_t size = 0;
+
+#ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+ uint8_t guid[sizeof(arm_uc_guid_t)] = {0};
+ // Read out the binary Vendor UUID
+ ccs_status_e status = (ccs_status_e)UpdateClient::getVendorId(guid, sizeof(arm_uc_guid_t), &size);
+
+ // Format the binary Vendor UUID into a hex string
+ if (status == CCS_STATUS_SUCCESS) {
+ size_t j = 0;
+ for(size_t i = 0; i < size; i++)
+ {
+ buffer[j++] = hex_table[(guid[i] >> 4) & 0xF];
+ buffer[j++] = hex_table[(guid[i] >> 0) & 0xF];
+ }
+ buffer[j] = '\0';
+ device_object->create_resource(M2MDevice::Manufacturer, String((char*)buffer, size * 2));
+ }
+
+ // Read out the binary Class UUID
+ status = (ccs_status_e)UpdateClient::getClassId(guid, sizeof(arm_uc_guid_t), &size);
+
+ // Format the binary Class UUID into a hex string
+ if (status == CCS_STATUS_SUCCESS) {
+ size_t j = 0;
+ for(size_t i = 0; i < size; i++)
+ {
+ buffer[j++] = hex_table[(guid[i] >> 4) & 0xF];
+ buffer[j++] = hex_table[(guid[i] >> 0) & 0xF];
+ }
+ buffer[j] = '\0';
+ device_object->create_resource(M2MDevice::ModelNumber, String((char*)buffer, size * 2));
+ }
+#else
+ ccs_status_e status = get_config_parameter(g_fcc_manufacturer_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::Manufacturer, String((char*)buffer, size));
+ }
+ status = get_config_parameter(g_fcc_model_number_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::ModelNumber, String((char*)buffer, size));
+ }
+#endif
+ status = get_config_parameter(g_fcc_device_serial_number_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::SerialNumber, String((char*)buffer, size));
+ }
+ status = get_config_parameter(g_fcc_device_type_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::DeviceType, String((char*)buffer, size));
+ }
+ status = get_config_parameter(g_fcc_hardware_version_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::HardwareVersion, String((char*)buffer, size));
+ }
+ status = get_config_parameter(KEY_DEVICE_SOFTWAREVERSION, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::SoftwareVersion, String((char*)buffer, size));
+ }
+ uint8_t data[4] = {0};
+ uint32_t value;
+ status = get_config_parameter(g_fcc_memory_size_parameter_name, data, 4, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ memcpy(&value, data, 4);
+ device_object->create_resource(M2MDevice::MemoryTotal, value);
+ tr_debug("ServiceClient::device_object_from_storage() - setting memory total value %" PRIu32 " (%s)", value, tr_array(data, 4));
+ }
+ status = get_config_parameter(g_fcc_current_time_parameter_name, data, 4, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ memcpy(&value, data, 4);
+ device_object->create_resource(M2MDevice::CurrentTime, value);
+ tr_debug("ServiceClient::device_object_from_storage() - setting current time value %" PRIu32 " (%s)", value, tr_array(data, 4));
+ }
+ status = get_config_parameter(g_fcc_device_time_zone_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::Timezone, String((char*)buffer, size));
+ }
+ status = get_config_parameter(g_fcc_offset_from_utc_parameter_name, buffer, buffer_size, &size);
+ if (status == CCS_STATUS_SUCCESS) {
+ device_object->create_resource(M2MDevice::UTCOffset, String((char*)buffer, size));
+ }
+ return device_object;
+}
+
+/**
+ * \brief Set resource value in the Device Object
+ *
+ * \param resource Device enum to have value set.
+ * \param value String object.
+ * \return True if successful, false otherwise.
+ */
+bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
+ const std::string& value)
+{
+ return set_device_resource_value(resource,
+ value.c_str(),
+ value.size() - 1);
+}
+
+/**
+ * \brief Set resource value in the Device Object
+ *
+ * \param resource Device enum to have value set.
+ * \param value Byte buffer.
+ * \param length Buffer length.
+ * \return True if successful, false otherwise.
+ */
+bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
+ const char* value,
+ uint32_t length)
+{
+ bool retval = false;
+
+ /* sanity check */
+ if (value && (length < 256) && (length > 0))
+ {
+#ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+ /* Pass resource value to Update Client.
+ Used for validating the manifest.
+ */
+ switch (resource) {
+ case M2MDevice::Manufacturer:
+ ARM_UC_SetVendorId((const uint8_t*) value, length);
+ break;
+ case M2MDevice::ModelNumber:
+ ARM_UC_SetClassId((const uint8_t*) value, length);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /* Convert resource to printable string if necessary */
+
+ /* Getting object instance from factory */
+ M2MDevice *device_object = M2MInterfaceFactory::create_device();
+
+ /* Check device object and resource both are present */
+ if (device_object && device_object->is_resource_present(resource)) {
+ /* set counter to not-zero */
+ uint8_t printable_length = 0xFF;
+
+ /* set printable_length to 0 if the buffer is not printable */
+ for (uint8_t index = 0; index < length; index++) {
+ /* break if character is not printable */
+ if ((value[index] < ' ') || (value[index] > '~')) {
+ printable_length = 0;
+ break;
+ }
+ }
+
+ /* resource is a string */
+ if (printable_length != 0) {
+ /* reset counter */
+ printable_length = 0;
+
+ /* find actual printable length */
+ for ( ; printable_length < length; printable_length++) {
+ /* break prematurely if end-of-string character is found */
+ if (value[printable_length] == '\0') {
+ break;
+ }
+ }
+
+ /* convert to string and set value in object */
+ String string_value(value, printable_length);
+ retval = device_object->set_resource_value(resource, string_value);
+ }
+ else
+ {
+ /* resource is a byte array */
+ char value_buffer[0xFF] = { 0 };
+
+ /* count length */
+ uint8_t index = 0;
+
+ /* convert byte array to string */
+ for ( ;
+ (index < length) && ((2*index +1) < 0xFF);
+ index++) {
+
+ uint8_t byte = value[index];
+
+ value_buffer[2 * index] = hex_table[byte >> 4];
+ value_buffer[2 * index + 1] = hex_table[byte & 0x0F];
+ }
+
+ /* convert to string and set value in object */
+ String string_value(value_buffer, 2 * (index - 1));
+ retval = device_object->set_resource_value(resource, string_value);
+ }
+ }
+ }
+
+ return retval;
+}
+
+#ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+void ServiceClient::set_update_authorize_handler(void (*handler)(int32_t request))
+{
+ UpdateClient::set_update_authorize_handler(handler);
+}
+
+void ServiceClient::update_authorize(int32_t request)
+{
+ UpdateClient::update_authorize(request);
+}
+
+void ServiceClient::set_update_progress_handler(void (*handler)(uint32_t progress, uint32_t total))
+{
+ UpdateClient::set_update_progress_handler(handler);
+}
+
+void ServiceClient::update_error_callback(int32_t error)
+{
+ _service_callback.error(error, ERROR_UPDATE);
+}
+#endif