Simulated product dispenser

Dependencies:   HTS221

Fork of mbed-cloud-workshop-connect-HTS221 by Jim Carver

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