Mbed Cloud example program for workshop in W27 2018.

Dependencies:   MMA7660 LM75B

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ServiceClient.cpp Source File

ServiceClient.cpp

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 // Note: this macro is needed on armcc to get the the PRI*32 macros
00020 // from inttypes.h in a C++ code.
00021 #ifndef __STDC_FORMAT_MACROS
00022 #define __STDC_FORMAT_MACROS
00023 #endif
00024 #include <inttypes.h>
00025 
00026 #include <string>
00027 #include "include/ServiceClient.h"
00028 #include "include/CloudClientStorage.h"
00029 #include "include/UpdateClientResources.h"
00030 #include "include/UpdateClient.h"
00031 #include "factory_configurator_client.h"
00032 #include "mbed-trace/mbed_trace.h"
00033 #include <assert.h>
00034 
00035 #define TRACE_GROUP "mClt"
00036 
00037 #define CONNECT 0
00038 #define ERROR_UPDATE "Update has failed, check MbedCloudClient::Error"
00039 
00040 /* lookup table for printing hexadecimal values */
00041 const uint8_t ServiceClient::hex_table[16] = {
00042     '0', '1', '2', '3', '4', '5', '6', '7',
00043     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
00044 };
00045 
00046 ServiceClient::ServiceClient(ServiceClientCallback& callback)
00047 : _service_callback(callback),
00048   _service_uri(NULL),
00049   _stack(NULL),
00050   _client_objs(NULL),
00051   _current_state(State_Init),
00052   _event_generated(false),
00053   _state_engine_running(false),
00054 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00055   _setup_update_client(false),
00056 #endif
00057   _connector_client(this)
00058 {
00059 }
00060 
00061 ServiceClient::~ServiceClient()
00062 {
00063 }
00064 
00065 void ServiceClient::initialize_and_register(M2MObjectList& client_objs)
00066 {
00067     tr_debug("ServiceClient::initialize_and_register");
00068     if(_current_state == State_Init ||
00069        _current_state == State_Unregister ||
00070        _current_state == State_Failure) {
00071         _client_objs = &client_objs;
00072 
00073 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00074         tr_debug("ServiceClient::initialize_and_register: update client supported");
00075 
00076         if(!_setup_update_client) {
00077             _setup_update_client = true;
00078 
00079 #ifdef MBED_CLOUD_DEV_UPDATE_ID
00080             /* Overwrite values stored in KCM. This is for development only
00081                since these IDs should be provisioned in the factory.
00082             */
00083             tr_debug("ServiceClient::initialize_and_register: update IDs defined");
00084 
00085             /* Delete VendorId */
00086             delete_config_parameter("mbed.VendorId");
00087             /* Store Vendor Id to mbed.VendorId. No conversion is performed. */
00088             set_device_resource_value(M2MDevice::Manufacturer,
00089                                       (const char*) arm_uc_vendor_id,
00090                                       arm_uc_vendor_id_size);
00091 
00092             /* Delete ClassId */
00093             delete_config_parameter("mbed.ClassId");
00094             /* Store Class Id to mbed.ClassId. No conversion is performed. */
00095             set_device_resource_value(M2MDevice::ModelNumber,
00096                                       (const char*) arm_uc_class_id,
00097                                       arm_uc_class_id_size);
00098 #endif /* MBED_CLOUD_DEV_UPDATE_ID */
00099 
00100 #ifdef ARM_UPDATE_CLIENT_VERSION
00101             /* Inject Update Client version number if no other software
00102                version is present in the KCM.
00103             */
00104             tr_debug("ServiceClient::initialize_and_register: update version defined");
00105 ;
00106             const size_t buffer_size = 16;
00107             uint8_t buffer[buffer_size];
00108             size_t size = 0;
00109 
00110             /* check if software version is already set */
00111             ccs_status_e status = get_config_parameter(KEY_DEVICE_SOFTWAREVERSION,
00112                                           buffer, buffer_size, &size);
00113 
00114             if (status == CCS_STATUS_KEY_DOESNT_EXIST) {
00115                 tr_debug("ServiceClient::initialize_and_register: insert update version");
00116 
00117                 /* insert value from Update Client Common */
00118                 set_config_parameter(KEY_DEVICE_SOFTWAREVERSION,
00119                                      (const uint8_t*) ARM_UPDATE_CLIENT_VERSION,
00120                                      sizeof(ARM_UPDATE_CLIENT_VERSION));
00121             }
00122 #endif /* ARM_UPDATE_CLIENT_VERSION */
00123 
00124             /* Update Client adds the OMA LWM2M Firmware Update object */
00125             UpdateClient::populate_object_list(*_client_objs);
00126 
00127             /* Initialize Update Client */
00128             FP1<void, int32_t> callback(this, &ServiceClient::update_error_callback);
00129             UpdateClient::UpdateClient(callback);
00130         }
00131 #endif /* MBED_CLOUD_CLIENT_SUPPORT_UPDATE */
00132 
00133         /* Device Object is mandatory.
00134            Get instance and add it to object list
00135         */
00136         M2MDevice *device_object = device_object_from_storage();
00137 
00138         if (device_object) {
00139             /* Publish device object resource to mds */
00140             M2MResourceList list = device_object->object_instance()->resources();
00141             if(!list.empty()) {
00142                 M2MResourceList::const_iterator it;
00143                 it = list.begin();
00144                 for ( ; it != list.end(); it++ ) {
00145                     (*it)->set_register_uri(true);
00146                 }
00147             }
00148 
00149             /* Add Device Object to object list. */
00150             _client_objs->push_back(device_object);
00151         }
00152 
00153         internal_event(State_Bootstrap);
00154     } else if (_current_state == State_Success) {
00155         state_success();
00156     }
00157 }
00158 
00159 ConnectorClient &ServiceClient::connector_client()
00160 {
00161     return _connector_client;
00162 }
00163 
00164 const ConnectorClient &ServiceClient::connector_client() const
00165 {
00166     return _connector_client;
00167 }
00168 
00169 // generates an internal event. called from within a state
00170 // function to transition to a new state
00171 void ServiceClient::internal_event(StartupMainState new_state)
00172 {
00173     tr_debug("ServiceClient::internal_event: state: %d -> %d", _current_state, new_state);
00174 
00175     _event_generated = true;
00176     _current_state = new_state;
00177 
00178     if (!_state_engine_running) {
00179         state_engine();
00180     }
00181 }
00182 
00183 // the state engine executes the state machine states
00184 void ServiceClient::state_engine(void)
00185 {
00186     tr_debug("ServiceClient::state_engine");
00187 
00188     // this simple flagging gets rid of recursive calls to this method
00189     _state_engine_running = true;
00190 
00191     // while events are being generated keep executing states
00192     while (_event_generated) {
00193         _event_generated = false;  // event used up, reset flag
00194 
00195         state_function(_current_state);
00196     }
00197 
00198     _state_engine_running = false;
00199 }
00200 
00201 void ServiceClient::state_function(StartupMainState current_state)
00202 {
00203     switch (current_state) {
00204         case State_Init:        // -> Goes to bootstrap state
00205         case State_Bootstrap:    // -> State_Register OR State_Failure
00206             state_bootstrap();
00207             break;
00208         case State_Register:     // -> State_Succes OR State_Failure
00209             state_register();
00210             break;
00211         case State_Success:      // return success to user
00212             state_success();
00213             break;
00214         case State_Failure:      // return error to user
00215             state_failure();
00216             break;
00217         case State_Unregister:   // return error to user
00218             state_unregister();
00219             break;
00220     }
00221 }
00222 
00223 void ServiceClient::state_bootstrap()
00224 {
00225     tr_info("ServiceClient::state_bootstrap()");
00226     bool credentials_ready = _connector_client.connector_credentials_available();
00227     bool bootstrap = _connector_client.use_bootstrap();
00228     tr_info("ServiceClient::state_bootstrap() - lwm2m credentials available: %d", credentials_ready);
00229     tr_info("ServiceClient::state_bootstrap() - use bootstrap: %d", bootstrap);
00230     if (credentials_ready || !bootstrap) {
00231         internal_event(State_Register);
00232     } else {
00233         _connector_client.start_bootstrap();
00234     }
00235 }
00236 
00237 void ServiceClient::state_register()
00238 {
00239     tr_info("ServiceClient::state_register()");
00240     _connector_client.start_registration(_client_objs);
00241 }
00242 
00243 void ServiceClient::registration_process_result(ConnectorClient::StartupSubStateRegistration status)
00244 {
00245     tr_debug("ServiceClient::registration_process_result(): status: %d", status);
00246     if (status == ConnectorClient::State_Registration_Success) {
00247         internal_event(State_Success);
00248     } else if(status == ConnectorClient::State_Registration_Failure ||
00249               status == ConnectorClient::State_Bootstrap_Failure){
00250         internal_event(State_Failure); // XXX: the status should be saved to eg. event object
00251     }
00252     if(status == ConnectorClient::State_Bootstrap_Success) {
00253         internal_event(State_Register);
00254     }
00255     if(status == ConnectorClient::State_Unregistered) {
00256         internal_event(State_Unregister);
00257     }
00258     if (status == ConnectorClient::State_Registration_Updated) {
00259         _service_callback.complete(ServiceClientCallback::Service_Client_Status_Register_Updated);
00260     }
00261 }
00262 
00263 void ServiceClient::connector_error(M2MInterface::Error error, const char *reason)
00264 {
00265     tr_error("ServiceClient::connector_error() error %d", (int)error);
00266     if (_current_state == State_Register) {
00267         registration_process_result(ConnectorClient::State_Registration_Failure);
00268     }
00269     else if (_current_state == State_Bootstrap) {
00270         registration_process_result(ConnectorClient::State_Bootstrap_Failure);
00271     }
00272     _service_callback.error(int(error),reason);
00273     internal_event(State_Failure);
00274 }
00275 
00276 void ServiceClient::value_updated(M2MBase *base, M2MBase::BaseType type)
00277 {
00278     tr_debug("ServiceClient::value_updated()");
00279     _service_callback.value_updated(base, type);
00280 }
00281 
00282 void ServiceClient::state_success()
00283 {
00284     tr_info("ServiceClient::state_success()");
00285     // this is verified already at client API level, but this might still catch some logic failures
00286     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Registered);
00287 }
00288 
00289 void ServiceClient::state_failure()
00290 {
00291     tr_error("ServiceClient::state_failure()");
00292     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Failure);
00293 }
00294 
00295 void ServiceClient::state_unregister()
00296 {
00297     tr_debug("ServiceClient::state_unregister()");
00298     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Unregistered);
00299 }
00300 
00301 M2MDevice* ServiceClient::device_object_from_storage()
00302 {
00303     M2MDevice *device_object = M2MInterfaceFactory::create_device();
00304     if (device_object == NULL) {
00305         return NULL;
00306     }
00307 
00308     const size_t buffer_size = 128;
00309     uint8_t buffer[buffer_size];
00310     size_t size = 0;
00311 
00312 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00313     uint8_t guid[sizeof(arm_uc_guid_t)] = {0};
00314     // Read out the binary Vendor UUID
00315     ccs_status_e status = (ccs_status_e)UpdateClient::getVendorId(guid, sizeof(arm_uc_guid_t), &size);
00316 
00317     // Format the binary Vendor UUID into a hex string
00318     if (status == CCS_STATUS_SUCCESS) {
00319         size_t j = 0;
00320         for(size_t i = 0; i < size; i++)
00321         {
00322             buffer[j++] = hex_table[(guid[i] >> 4) & 0xF];
00323             buffer[j++] = hex_table[(guid[i] >> 0) & 0xF];
00324         }
00325         buffer[j] = '\0';
00326         device_object->create_resource(M2MDevice::Manufacturer, String((char*)buffer, size * 2));
00327     }
00328 
00329     // Read out the binary Class UUID
00330     status = (ccs_status_e)UpdateClient::getClassId(guid, sizeof(arm_uc_guid_t), &size);
00331 
00332     // Format the binary Class UUID into a hex string
00333     if (status == CCS_STATUS_SUCCESS) {
00334         size_t j = 0;
00335         for(size_t i = 0; i < size; i++)
00336         {
00337             buffer[j++] = hex_table[(guid[i] >> 4) & 0xF];
00338             buffer[j++] = hex_table[(guid[i] >> 0) & 0xF];
00339         }
00340         buffer[j] = '\0';
00341         device_object->create_resource(M2MDevice::ModelNumber, String((char*)buffer, size * 2));
00342     }
00343 #else
00344     ccs_status_e status = get_config_parameter(g_fcc_manufacturer_parameter_name, buffer, buffer_size, &size);
00345     if (status == CCS_STATUS_SUCCESS) {
00346         device_object->create_resource(M2MDevice::Manufacturer, String((char*)buffer, size));
00347     }
00348     status = get_config_parameter(g_fcc_model_number_parameter_name, buffer, buffer_size, &size);
00349     if (status == CCS_STATUS_SUCCESS) {
00350         device_object->create_resource(M2MDevice::ModelNumber, String((char*)buffer, size));
00351     }
00352 #endif
00353     status = get_config_parameter(g_fcc_device_serial_number_parameter_name, buffer, buffer_size, &size);
00354     if (status == CCS_STATUS_SUCCESS) {
00355         device_object->create_resource(M2MDevice::SerialNumber, String((char*)buffer, size));
00356     }
00357     status = get_config_parameter(g_fcc_device_type_parameter_name, buffer, buffer_size, &size);
00358     if (status == CCS_STATUS_SUCCESS) {
00359         device_object->create_resource(M2MDevice::DeviceType, String((char*)buffer, size));
00360     }
00361     status = get_config_parameter(g_fcc_hardware_version_parameter_name, buffer, buffer_size, &size);
00362     if (status == CCS_STATUS_SUCCESS) {
00363         device_object->create_resource(M2MDevice::HardwareVersion, String((char*)buffer, size));
00364     }
00365     status = get_config_parameter(KEY_DEVICE_SOFTWAREVERSION, buffer, buffer_size, &size);
00366     if (status == CCS_STATUS_SUCCESS) {
00367         device_object->create_resource(M2MDevice::SoftwareVersion, String((char*)buffer, size));
00368     }
00369     uint8_t data[4] = {0};
00370     uint32_t value;
00371     status = get_config_parameter(g_fcc_memory_size_parameter_name, data, 4, &size);
00372     if (status == CCS_STATUS_SUCCESS) {
00373         memcpy(&value, data, 4);
00374         device_object->create_resource(M2MDevice::MemoryTotal, value);
00375         tr_debug("ServiceClient::device_object_from_storage() - setting memory total value %" PRIu32 " (%s)", value, tr_array(data, 4));
00376     }
00377     status = get_config_parameter(g_fcc_current_time_parameter_name, data, 4, &size);
00378     if (status == CCS_STATUS_SUCCESS) {
00379         memcpy(&value, data, 4);
00380         device_object->create_resource(M2MDevice::CurrentTime, value);
00381         tr_debug("ServiceClient::device_object_from_storage() - setting current time value %" PRIu32 " (%s)", value, tr_array(data, 4));
00382     }
00383     status = get_config_parameter(g_fcc_device_time_zone_parameter_name, buffer, buffer_size, &size);
00384     if (status == CCS_STATUS_SUCCESS) {
00385         device_object->create_resource(M2MDevice::Timezone, String((char*)buffer, size));
00386     }
00387     status = get_config_parameter(g_fcc_offset_from_utc_parameter_name, buffer, buffer_size, &size);
00388     if (status == CCS_STATUS_SUCCESS) {
00389         device_object->create_resource(M2MDevice::UTCOffset, String((char*)buffer, size));
00390     }
00391     return device_object;
00392 }
00393 
00394 /**
00395  * \brief Set resource value in the Device Object
00396  *
00397  * \param resource Device enum to have value set.
00398  * \param value String object.
00399  * \return True if successful, false otherwise.
00400  */
00401 bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
00402                                               const std::string& value)
00403 {
00404     return set_device_resource_value(resource,
00405                                      value.c_str(),
00406                                      value.size() - 1);
00407 }
00408 
00409 /**
00410  * \brief Set resource value in the Device Object
00411  *
00412  * \param resource Device enum to have value set.
00413  * \param value Byte buffer.
00414  * \param length Buffer length.
00415  * \return True if successful, false otherwise.
00416  */
00417 bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
00418                                               const char* value,
00419                                               uint32_t length)
00420 {
00421     bool retval = false;
00422 
00423     /* sanity check */
00424     if (value && (length < 256) && (length > 0))
00425     {
00426 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00427         /* Pass resource value to Update Client.
00428            Used for validating the manifest.
00429         */
00430         switch (resource) {
00431             case M2MDevice::Manufacturer:
00432                 ARM_UC_SetVendorId((const uint8_t*) value, length);
00433                 break;
00434             case M2MDevice::ModelNumber:
00435                 ARM_UC_SetClassId((const uint8_t*) value, length);
00436                 break;
00437             default:
00438                 break;
00439         }
00440 #endif
00441 
00442         /* Convert resource to printable string if necessary */
00443 
00444         /* Getting object instance from factory */
00445         M2MDevice *device_object = M2MInterfaceFactory::create_device();
00446 
00447         /* Check device object and resource both are present */
00448         if (device_object && device_object->is_resource_present(resource)) {
00449             /* set counter to not-zero */
00450             uint8_t printable_length = 0xFF;
00451 
00452             /* set printable_length to 0 if the buffer is not printable */
00453             for (uint8_t index = 0; index < length; index++) {
00454                 /* break if character is not printable */
00455                 if ((value[index] < ' ') || (value[index] > '~')) {
00456                     printable_length = 0;
00457                     break;
00458                 }
00459             }
00460 
00461             /* resource is a string */
00462             if (printable_length != 0) {
00463                 /* reset counter */
00464                 printable_length = 0;
00465 
00466                 /* find actual printable length */
00467                 for ( ; printable_length < length; printable_length++) {
00468                     /* break prematurely if end-of-string character is found */
00469                     if (value[printable_length] == '\0') {
00470                         break;
00471                     }
00472                 }
00473 
00474                 /* convert to string and set value in object */
00475                 String string_value(value, printable_length);
00476                 retval = device_object->set_resource_value(resource, string_value);
00477             }
00478             else
00479             {
00480                 /* resource is a byte array */
00481                 char value_buffer[0xFF] = { 0 };
00482 
00483                 /* count length */
00484                 uint8_t index = 0;
00485 
00486                 /* convert byte array to string */
00487                 for ( ;
00488                     (index < length) && ((2*index +1) < 0xFF);
00489                     index++) {
00490 
00491                     uint8_t byte = value[index];
00492 
00493                     value_buffer[2 * index]     = hex_table[byte >> 4];
00494                     value_buffer[2 * index + 1] = hex_table[byte & 0x0F];
00495                 }
00496 
00497                 /* convert to string and set value in object */
00498                 String string_value(value_buffer, 2 * (index - 1));
00499                 retval = device_object->set_resource_value(resource, string_value);
00500             }
00501         }
00502     }
00503 
00504     return retval;
00505 }
00506 
00507 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00508 void ServiceClient::set_update_authorize_handler(void (*handler)(int32_t request))
00509 {
00510     UpdateClient::set_update_authorize_handler(handler);
00511 }
00512 
00513 void ServiceClient::update_authorize(int32_t request)
00514 {
00515     UpdateClient::update_authorize(request);
00516 }
00517 
00518 void ServiceClient::set_update_progress_handler(void (*handler)(uint32_t progress, uint32_t total))
00519 {
00520     UpdateClient::set_update_progress_handler(handler);
00521 }
00522 
00523 void ServiceClient::update_error_callback(int32_t error)
00524 {
00525     _service_callback.error(error, ERROR_UPDATE);
00526 }
00527 #endif