Simple interface for Mbed Cloud Client

Dependents:  

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers m2mobject.cpp Source File

m2mobject.cpp

00001 /*
00002  * Copyright (c) 2015 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 // Note: this macro is needed on armcc to get the the PRI*32 macros
00018 // from inttypes.h in a C++ code.
00019 #ifndef __STDC_FORMAT_MACROS
00020 #define __STDC_FORMAT_MACROS
00021 #endif
00022 #include <inttypes.h>
00023 
00024 #include "mbed-client/m2mobject.h"
00025 #include "mbed-client/m2mconstants.h"
00026 #include "include/m2mtlvserializer.h"
00027 #include "include/m2mtlvdeserializer.h"
00028 #include "include/m2mreporthandler.h"
00029 #include "mbed-trace/mbed_trace.h"
00030 #include "mbed-client/m2mstringbuffer.h"
00031 #include "include/m2mcallbackstorage.h"
00032 
00033 #include <stdlib.h>
00034 
00035 #define BUFFER_SIZE 10
00036 #define TRACE_GROUP "mClt"
00037 
00038 M2MObject::M2MObject(const String &object_name, char *path, bool external_blockwise_store)
00039 : M2MBase(object_name,
00040           M2MBase::Dynamic,
00041 #ifndef DISABLE_RESOURCE_TYPE
00042           "",
00043 #endif
00044           path,
00045           external_blockwise_store,
00046           false),
00047           _observation_handler(NULL)
00048 {
00049     M2MBase::set_base_type(M2MBase::Object);
00050     M2MBase::set_operation(M2MBase::GET_ALLOWED);
00051     if(M2MBase::name_id() != -1) {
00052         M2MBase::set_coap_content_type(COAP_CONTENT_OMA_TLV_TYPE_OLD);
00053     }
00054 }
00055 
00056 M2MObject::M2MObject(const M2MBase::lwm2m_parameters_s* static_res)
00057 : M2MBase(static_res),
00058 _observation_handler(NULL)
00059 {
00060     M2MBase::set_operation(M2MBase::GET_ALLOWED);
00061     if(M2MBase::name_id() != -1) {
00062         M2MBase::set_coap_content_type(COAP_CONTENT_OMA_TLV_TYPE_OLD);
00063     }
00064 }
00065 
00066 M2MObject::~M2MObject()
00067 {
00068     if(!_instance_list.empty()) {
00069 
00070         M2MObjectInstanceList::const_iterator it;
00071         it = _instance_list.begin();
00072         M2MObjectInstance* obj = NULL;
00073         uint16_t index = 0;
00074         for (; it!=_instance_list.end(); it++, index++ ) {
00075             //Free allocated memory for object instances.
00076             obj = *it;
00077             delete obj;
00078         }
00079 
00080         _instance_list.clear();
00081     }
00082 
00083     free_resources();
00084 }
00085 
00086 M2MObjectInstance* M2MObject::create_object_instance(uint16_t instance_id)
00087 {
00088     tr_debug("M2MObject::create_object_instance - id: %d", instance_id);
00089     M2MObjectInstance *instance = NULL;
00090     if(!object_instance(instance_id)) {
00091         char* path = create_path(*this, instance_id);
00092         if (path) {
00093             // Note: the object instance's name contains actually object's name.
00094             instance = new M2MObjectInstance(*this, "", path);
00095             if(instance) {
00096                 instance->add_observation_level(observation_level());
00097                 instance->set_instance_id(instance_id);
00098                 if(M2MBase::name_id() != -1) {
00099                     instance->set_coap_content_type(COAP_CONTENT_OMA_TLV_TYPE_OLD);
00100                 }
00101                 _instance_list.push_back(instance);
00102             }
00103         }
00104     }
00105     return instance;
00106 }
00107 
00108 
00109 M2MObjectInstance* M2MObject::create_object_instance(const lwm2m_parameters_s* s)
00110 {
00111     tr_debug("M2MObject::create_object_instance - id: %d", s->identifier.instance_id);
00112     M2MObjectInstance *instance = NULL;
00113     if(!object_instance(s->identifier.instance_id)) {
00114 
00115         instance = new M2MObjectInstance(*this, s);
00116         if(instance) {
00117             instance->add_observation_level(observation_level());
00118             //instance->set_instance_id(instance_id);
00119             //if(M2MBase::name_id() != -1) {
00120               //  instance->set_coap_content_type(COAP_CONTENT_OMA_TLV_TYPE_OLD);
00121             //}
00122             _instance_list.push_back(instance);
00123         }
00124     }
00125     return instance;
00126 }
00127 
00128 bool M2MObject::remove_object_instance(uint16_t inst_id)
00129 {
00130     tr_debug("M2MObject::remove_object_instance(inst_id %d)", inst_id);
00131     bool success = false;
00132     if(!_instance_list.empty()) {
00133         M2MObjectInstance* obj = NULL;
00134         M2MObjectInstanceList::const_iterator it;
00135         it = _instance_list.begin();
00136         int pos = 0;
00137         for ( ; it != _instance_list.end(); it++, pos++ ) {
00138             if((*it)->instance_id() == inst_id) {
00139                 // Instance found and deleted.
00140                 obj = *it;
00141 
00142                 _instance_list.erase(pos);
00143                 delete obj;
00144                 success = true;
00145                 break;
00146             }
00147         }
00148     }
00149     return success;
00150 }
00151 
00152 M2MObjectInstance* M2MObject::object_instance(uint16_t inst_id) const
00153 {
00154     tr_debug("M2MObject::object_instance(inst_id %d)", inst_id);
00155     M2MObjectInstance *obj = NULL;
00156     if(!_instance_list.empty()) {
00157         M2MObjectInstanceList::const_iterator it;
00158         it = _instance_list.begin();
00159         for ( ; it != _instance_list.end(); it++ ) {
00160             if((*it)->instance_id() == inst_id) {
00161                 // Instance found.
00162                 obj = *it;
00163                 break;
00164             }
00165         }
00166     }
00167     return obj;
00168 }
00169 
00170 const M2MObjectInstanceList& M2MObject::instances() const
00171 {
00172     return _instance_list;
00173 }
00174 
00175 uint16_t M2MObject::instance_count() const
00176 {
00177     return (uint16_t)_instance_list.size();
00178 }
00179 
00180 M2MObservationHandler* M2MObject::observation_handler() const
00181 {
00182     // XXX: need to check the flag too
00183     return _observation_handler;
00184 }
00185 
00186 void M2MObject::set_observation_handler(M2MObservationHandler *handler)
00187 {
00188     tr_debug("M2MObject::set_observation_handler - handler: 0x%p", (void*)handler);
00189     _observation_handler = handler;
00190 }
00191 
00192 void M2MObject::add_observation_level(M2MBase::Observation observation_level)
00193 {
00194     M2MBase::add_observation_level(observation_level);
00195     if(!_instance_list.empty()) {
00196          M2MObjectInstanceList::const_iterator it;
00197          it = _instance_list.begin();
00198          for ( ; it != _instance_list.end(); it++ ) {
00199              (*it)->add_observation_level(observation_level);
00200          }
00201     }
00202 }
00203 
00204 void M2MObject::remove_observation_level(M2MBase::Observation observation_level)
00205 {
00206     M2MBase::remove_observation_level(observation_level);
00207     if(!_instance_list.empty()) {
00208         M2MObjectInstanceList::const_iterator it;
00209         it = _instance_list.begin();
00210         for ( ; it != _instance_list.end(); it++ ) {
00211             (*it)->remove_observation_level(observation_level);
00212         }
00213     }
00214 }
00215 
00216 sn_coap_hdr_s* M2MObject::handle_get_request(nsdl_s *nsdl,
00217                                              sn_coap_hdr_s *received_coap_header,
00218                                              M2MObservationHandler *observation_handler)
00219 {
00220     tr_info("M2MObject::handle_get_request()");
00221     sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CONTENT;
00222     sn_coap_hdr_s * coap_response = sn_nsdl_build_response(nsdl,
00223                                                            received_coap_header,
00224                                                            msg_code);
00225     uint8_t *data = NULL;
00226     uint32_t data_length = 0;
00227     if(received_coap_header) {
00228         // process the GET if we have registered a callback for it
00229         if ((operation() & SN_GRS_GET_ALLOWED) != 0) {
00230             if(coap_response) {
00231                 bool content_type_present = false;
00232                 bool is_content_type_supported = true;
00233 
00234                 if (received_coap_header->options_list_ptr &&
00235                         received_coap_header->options_list_ptr->accept != COAP_CT_NONE) {
00236                     content_type_present = true;
00237                     coap_response->content_format = received_coap_header->options_list_ptr->accept;
00238 
00239                 }
00240 
00241                 // Check if preferred content type is supported
00242                 if (content_type_present) {
00243                     if (coap_response->content_format != COAP_CONTENT_OMA_TLV_TYPE_OLD &&
00244                         coap_response->content_format != COAP_CONTENT_OMA_TLV_TYPE) {
00245                         is_content_type_supported = false;
00246                     }
00247                 }
00248 
00249                 if (is_content_type_supported) {
00250                     if(!content_type_present &&
00251                        (M2MBase::coap_content_type() == COAP_CONTENT_OMA_TLV_TYPE ||
00252                         M2MBase::coap_content_type() == COAP_CONTENT_OMA_TLV_TYPE_OLD)) {
00253                         coap_response->content_format = sn_coap_content_format_e(M2MBase::coap_content_type());
00254                     }
00255 
00256                     // fill in the CoAP response payload
00257                     if(COAP_CONTENT_OMA_TLV_TYPE == coap_response->content_format ||
00258                        COAP_CONTENT_OMA_TLV_TYPE_OLD == coap_response->content_format) {
00259                         set_coap_content_type(coap_response->content_format);
00260                         data = M2MTLVSerializer::serialize(_instance_list, data_length);
00261                     }
00262 
00263                     coap_response->payload_len = data_length;
00264                     coap_response->payload_ptr = data;
00265                     if(data){
00266                         coap_response->options_list_ptr = sn_nsdl_alloc_options_list(nsdl, coap_response);
00267                         if(coap_response->options_list_ptr){
00268                             coap_response->options_list_ptr->max_age = max_age();
00269                         }
00270                         if(received_coap_header->options_list_ptr) {
00271                             if(received_coap_header->options_list_ptr->observe != -1) {
00272                                 if (is_observable()) {
00273                                     uint32_t number = 0;
00274                                     uint8_t observe_option = 0;
00275                                     observe_option = received_coap_header->options_list_ptr->observe;
00276 
00277                                     if(START_OBSERVATION == observe_option) {
00278                                         // If the observe length is 0 means register for observation.
00279                                         if(received_coap_header->options_list_ptr->observe != -1) {
00280                                             number = received_coap_header->options_list_ptr->observe;
00281                                         }
00282 
00283                                         // If the observe value is 0 means register for observation.
00284                                         if(number == 0) {
00285                                             tr_info("M2MObject::handle_get_request - put resource under observation");
00286                                             set_under_observation(true,observation_handler);
00287                                             add_observation_level(M2MBase::O_Attribute);
00288                                             send_notification_delivery_status(*this, NOTIFICATION_STATUS_SUBSCRIBED);
00289                                             if(coap_response->options_list_ptr){
00290                                                 coap_response->options_list_ptr->observe = observation_number();
00291                                             }
00292                                         }
00293 
00294                                         if(received_coap_header->token_ptr) {
00295                                             set_observation_token(received_coap_header->token_ptr,
00296                                                                   received_coap_header->token_len);
00297                                         }
00298                                     } else if (STOP_OBSERVATION == observe_option) {
00299                                         tr_info("M2MObject::handle_get_request - stops observation");
00300                                         // If the observe options_list_ptr->observe value is 1 means de-register from observation.
00301                                         set_under_observation(false,NULL);
00302                                         remove_observation_level(M2MBase::O_Attribute);
00303                                         send_notification_delivery_status(*this, NOTIFICATION_STATUS_UNSUBSCRIBED);
00304                                     }
00305                                     msg_code = COAP_MSG_CODE_RESPONSE_CONTENT;
00306                                 }
00307                                 else {
00308                                     msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00309                                 }
00310                             }
00311                         }
00312                     } else {
00313                         msg_code = COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT; // Content format not supported
00314                     }
00315                 } else {
00316                     tr_error("M2MObject::handle_get_request() - Content-Type %d not supported", coap_response->content_format);
00317                     msg_code = COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE;
00318                 }
00319             }
00320         }else {
00321             tr_error("M2MResource::handle_get_request - Return COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED");
00322             // Operation is not allowed.
00323             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00324         }
00325     } else {
00326         msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00327     }
00328     if(coap_response) {
00329         coap_response->msg_code = msg_code;
00330     }
00331     return coap_response;
00332 }
00333 
00334 sn_coap_hdr_s* M2MObject::handle_put_request(nsdl_s *nsdl,
00335                                              sn_coap_hdr_s *received_coap_header,
00336                                              M2MObservationHandler */*observation_handler*/,
00337                                              bool &/*execute_value_updated*/)
00338 {
00339     tr_info("M2MObject::handle_put_request()");
00340     sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; // 2.04
00341     sn_coap_hdr_s *coap_response = sn_nsdl_build_response(nsdl,
00342                                                           received_coap_header,
00343                                                           msg_code);
00344     if(received_coap_header) {
00345         if(received_coap_header->content_format != COAP_CT_NONE) {
00346             set_coap_content_type(received_coap_header->content_format);
00347         }
00348         if(received_coap_header->options_list_ptr &&
00349            received_coap_header->options_list_ptr->uri_query_ptr) {
00350             char *query = (char*)alloc_string_copy(received_coap_header->options_list_ptr->uri_query_ptr,
00351                                                     received_coap_header->options_list_ptr->uri_query_len);
00352 
00353             if (query){
00354                 tr_info("M2MObject::handle_put_request() - query %s", query);
00355                 // if anything was updated, re-initialize the stored notification attributes
00356                 if (!handle_observation_attribute(query)){
00357                     tr_debug("M2MObject::handle_put_request() - Invalid query");
00358                     msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; // 4.00
00359                 }
00360                 free(query);
00361             }
00362         } else {
00363             tr_error("M2MObject::handle_put_request() - COAP_MSG_CODE_RESPONSE_BAD_REQUEST - Empty URI_QUERY");
00364             msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST;
00365         }
00366     } else {
00367         msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00368     }
00369     if(coap_response) {
00370         coap_response->msg_code = msg_code;
00371     }
00372     return coap_response;
00373 }
00374 
00375 
00376 sn_coap_hdr_s* M2MObject::handle_post_request(nsdl_s *nsdl,
00377                                               sn_coap_hdr_s *received_coap_header,
00378                                               M2MObservationHandler *observation_handler,
00379                                               bool &execute_value_updated,
00380                                               sn_nsdl_addr_s *)
00381 {
00382     tr_info("M2MObject::handle_post_request()");
00383     sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; // 2.04
00384     // process the POST if we have registered a callback for it
00385     sn_coap_hdr_s *coap_response = sn_nsdl_build_response(nsdl,
00386                                       received_coap_header,
00387                                       msg_code);
00388 
00389     if(received_coap_header) {
00390         if ((operation() & SN_GRS_POST_ALLOWED) != 0) {
00391             if(received_coap_header->content_format != COAP_CT_NONE) {
00392                 set_coap_content_type(received_coap_header->content_format);
00393             }
00394             if(received_coap_header->payload_ptr) {
00395                 tr_debug("M2MObject::handle_post_request() - Update Object with new values");
00396                 uint16_t coap_content_type = 0;
00397                 bool content_type_present = false;
00398                 if(received_coap_header->content_format != COAP_CT_NONE) {
00399                     content_type_present = true;
00400                     if(coap_response) {
00401                         coap_content_type = received_coap_header->content_format;
00402                     }
00403                 } // if(received_coap_header->content_format)
00404                 if(!content_type_present &&
00405                    (M2MBase::coap_content_type() == COAP_CONTENT_OMA_TLV_TYPE ||
00406                     M2MBase::coap_content_type() == COAP_CONTENT_OMA_TLV_TYPE_OLD)) {
00407                     coap_content_type = M2MBase::coap_content_type();
00408                 }
00409 
00410                 tr_debug("M2MObject::handle_post_request() - Request Content-type: %d", coap_content_type);
00411 
00412                 if(COAP_CONTENT_OMA_TLV_TYPE == coap_content_type ||
00413                    COAP_CONTENT_OMA_TLV_TYPE_OLD == coap_content_type) {
00414                     set_coap_content_type(coap_content_type);
00415                     uint32_t instance_id = 0;
00416                     // Check next free instance id
00417                     for(instance_id = 0; instance_id <= MAX_UNINT_16_COUNT; instance_id++) {
00418                         if(NULL == object_instance(instance_id)) {
00419                             break;
00420                         }
00421                         if(instance_id == MAX_UNINT_16_COUNT) {
00422                             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00423                             break;
00424                         }
00425                     }
00426                     if(COAP_MSG_CODE_RESPONSE_CHANGED == msg_code) {
00427                         bool is_obj_instance = false;
00428                         bool obj_instance_exists = false;
00429                         is_obj_instance = M2MTLVDeserializer::is_object_instance(received_coap_header->payload_ptr);
00430                         if (is_obj_instance) {
00431                             instance_id = M2MTLVDeserializer::instance_id(received_coap_header->payload_ptr);
00432                             tr_debug("M2MObject::handle_post_request() - instance id in TLV: %" PRIu32, instance_id);
00433                             // Check if instance id already exists
00434                             if (object_instance(instance_id)){
00435                                 obj_instance_exists = true;
00436                             }
00437                         }
00438                         if (!obj_instance_exists && coap_response) {
00439                             M2MObjectInstance *obj_instance = create_object_instance(instance_id);
00440                             if(obj_instance) {
00441                                 obj_instance->set_operation(M2MBase::GET_PUT_ALLOWED);
00442                             }
00443 
00444                             M2MTLVDeserializer::Error error = M2MTLVDeserializer::None;
00445                             if(is_obj_instance) {
00446                                 tr_debug("M2MObject::handle_post_request() - TLV data contains ObjectInstance");
00447                                 error = M2MTLVDeserializer::deserialise_object_instances(received_coap_header->payload_ptr,
00448                                                                            received_coap_header->payload_len,
00449                                                                            *this,
00450                                                                            M2MTLVDeserializer::Post);
00451                             } else if(obj_instance &&
00452                                         (M2MTLVDeserializer::is_resource(received_coap_header->payload_ptr) ||
00453                                          M2MTLVDeserializer::is_multiple_resource(received_coap_header->payload_ptr))) {
00454                                 tr_debug("M2MObject::handle_post_request() - TLV data contains Resources");
00455                                 error = M2MTLVDeserializer::deserialize_resources(received_coap_header->payload_ptr,
00456                                                                             received_coap_header->payload_len,
00457                                                                             *obj_instance,
00458                                                                             M2MTLVDeserializer::Post);
00459                             } else {
00460                                 error = M2MTLVDeserializer::NotValid;
00461                             }
00462                             switch(error) {
00463                                 case M2MTLVDeserializer::None:
00464                                     if(observation_handler) {
00465                                         execute_value_updated = true;
00466                                     }
00467                                     coap_response->options_list_ptr = sn_nsdl_alloc_options_list(nsdl, coap_response);
00468 
00469                                     if (coap_response->options_list_ptr) {
00470 
00471                                         StringBuffer<MAX_OBJECT_PATH_NAME> obj_name;
00472 
00473                                         if (obj_name.ensure_space(M2MBase::resource_name_length() + (1 + 5 + 1))) {
00474                                             obj_name.append(M2MBase::name());
00475                                             obj_name.append('/');
00476                                             obj_name.append_int(instance_id);
00477 
00478                                             coap_response->options_list_ptr->location_path_len = obj_name.get_size();
00479                                             coap_response->options_list_ptr->location_path_ptr =
00480                                                     alloc_copy((uint8_t*)obj_name.c_str(), obj_name.get_size());
00481                                             // todo: else return error
00482                                         }
00483                                     }
00484                                     // todo: else return error
00485                                     msg_code = COAP_MSG_CODE_RESPONSE_CREATED;
00486                                     break;
00487                                 case M2MTLVDeserializer::NotAllowed:
00488                                     msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00489                                     break;
00490                                 case M2MTLVDeserializer::NotValid:
00491                                     msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST;
00492                                     break;
00493                                 case M2MTLVDeserializer::NotFound:
00494                                     msg_code = COAP_MSG_CODE_RESPONSE_NOT_FOUND;
00495                                     break;
00496                                 case M2MTLVDeserializer::OutOfMemory:
00497                                     msg_code = COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE;
00498                                     break;
00499                             }
00500 
00501                         } else {
00502                             tr_error("M2MObject::handle_post_request() - COAP_MSG_CODE_RESPONSE_BAD_REQUEST");
00503                             msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST;
00504                         }
00505                     }
00506                 } else {
00507                     msg_code =COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT;
00508                 } // if(COAP_CONTENT_OMA_TLV_TYPE == coap_content_type)
00509             } else {
00510                 tr_error("M2MObject::handle_post_request - COAP_MSG_CODE_RESPONSE_BAD_REQUEST - Missing Payload");
00511                 msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; //
00512             }
00513         } else { // if ((object->operation() & SN_GRS_POST_ALLOWED) != 0)
00514             tr_error("M2MObject::handle_post_request - COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED");
00515             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED; // 4.05
00516         }
00517     } else { //if(received_coap_header)
00518         tr_error("M2MObject::handle_post_request - COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED");
00519         msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED; // 4.05
00520     }
00521 
00522     if(coap_response) {
00523         coap_response->msg_code = msg_code;
00524     }
00525     return coap_response;
00526 }
00527 
00528 void M2MObject::notification_update(uint16_t obj_instance_id)
00529 {
00530     tr_debug("M2MObject::notification_update - id: %d", obj_instance_id);
00531     M2MReportHandler *report_handler = M2MBase::report_handler();
00532     if(report_handler && is_under_observation()) {
00533         report_handler->set_notification_trigger(obj_instance_id);
00534     }
00535 }
00536