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 m2mreporthandler.cpp Source File

m2mreporthandler.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 // fixup the compilation on ARMCC for PRId32
00018 #define __STDC_FORMAT_MACROS
00019 #include <inttypes.h>
00020 
00021 #include "mbed-client/m2mreportobserver.h"
00022 #include "mbed-client/m2mconstants.h"
00023 #include "mbed-client/m2mtimer.h"
00024 #include "include/m2mreporthandler.h"
00025 #include "mbed-trace/mbed_trace.h"
00026 #include <string.h>
00027 #include <stdlib.h>
00028 
00029 #define TRACE_GROUP "mClt"
00030 
00031 M2MReportHandler::M2MReportHandler(M2MReportObserver &observer)
00032 : _observer(observer),
00033   _is_under_observation(false),
00034   _observation_level(M2MBase::None),
00035   _attribute_state(0),
00036   _token_length(0),
00037   _notify(false),
00038   _pmin_exceeded(false),
00039   _pmax_exceeded(false),
00040   _observation_number(0),
00041   _pmin_timer(*this),
00042   _pmax_timer(*this),
00043   _token(NULL),
00044   _pmax(-1.0f),
00045   _pmin(1.0f),
00046   _current_value(0.0f),
00047   _gt(0.0f),
00048   _lt(0.0f),
00049   _st(0.0f),
00050   _high_step(0.0f),
00051   _low_step(0.0f),
00052   _last_value(-1.0f),
00053   _notification_send_in_progress(false),
00054   _notification_in_queue(false),
00055   _blockwise_notify(false),
00056   _pmin_quiet_period(false)
00057 {
00058     tr_debug("M2MReportHandler::M2MReportHandler()");
00059 }
00060 
00061 M2MReportHandler::~M2MReportHandler()
00062 {
00063     tr_debug("M2MReportHandler::~M2MReportHandler()");
00064     free(_token);
00065 }
00066 
00067 void M2MReportHandler::set_under_observation(bool observed)
00068 {
00069     tr_debug("M2MReportHandler::set_under_observation(observed %d)", (int)observed);
00070 
00071     _is_under_observation = observed;
00072 
00073     stop_timers();
00074     if (observed) {
00075         handle_timers();
00076     }
00077     else {
00078         set_default_values();
00079     }
00080 }
00081 
00082 void M2MReportHandler::set_value(float value)
00083 {
00084     tr_debug("M2MReportHandler::set_value() - current %f, last %f", value, _last_value);
00085     _current_value = value;
00086 
00087     if (_current_value != _last_value) {
00088         tr_debug("M2MReportHandler::set_value() - new value");
00089         set_notification_in_queue(true);
00090         if (check_threshold_values()) {
00091             schedule_report();
00092         } else {
00093             tr_debug("M2MReportHandler::set_value - value not in range");
00094             _notify = false;
00095             if ((_attribute_state & M2MReportHandler::Lt) == M2MReportHandler::Lt ||
00096                 (_attribute_state & M2MReportHandler::Gt) == M2MReportHandler::Gt ||
00097                 (_attribute_state & M2MReportHandler::St) == M2MReportHandler::St) {
00098                 tr_debug("M2MReportHandler::set_value - stop pmin timer");
00099                 _pmin_timer.stop_timer();
00100                 _pmin_exceeded = true;
00101             }
00102         }
00103         _high_step = _last_value + _st;
00104         _low_step = _last_value - _st;
00105     }
00106 }
00107 
00108 void M2MReportHandler::set_notification_trigger(uint16_t obj_instance_id)
00109 {
00110     tr_debug("M2MReportHandler::set_notification_trigger(): %d", obj_instance_id);
00111     // Add to array if not there yet
00112     m2m::Vector<uint16_t>::const_iterator it;
00113     it = _changed_instance_ids.begin();
00114     bool found = false;
00115     for ( ; it != _changed_instance_ids.end(); it++) {
00116         if ((*it) == obj_instance_id) {
00117             found = true;
00118             break;
00119         }
00120     }
00121     if (!found) {
00122         _changed_instance_ids.push_back(obj_instance_id);
00123     }
00124 
00125     _current_value = 0.0f;
00126     _last_value = 1.0f;
00127     set_notification_in_queue(true);
00128     schedule_report();
00129 }
00130 
00131 bool M2MReportHandler::parse_notification_attribute(const char *query,
00132                                                     M2MBase::BaseType type,
00133                                                     M2MResourceInstance::ResourceType resource_type)
00134 {
00135     tr_debug("M2MReportHandler::parse_notification_attribute(Query %s, Base type %d)", query, (int)type);
00136     bool success = false;
00137     const char* sep_pos = strchr(query, '&');
00138     const char* rest = query;
00139     if( sep_pos != NULL ){
00140         char query_options[5][20];
00141         float pmin = _pmin;
00142         float pmax = _pmax;
00143         float lt = _lt;
00144         float gt = _gt;
00145         float st = _st;
00146         float high = _high_step;
00147         float low = _low_step;
00148         uint8_t attr = _attribute_state;
00149 
00150         memset(query_options, 0, sizeof(query_options[0][0]) * 5 * 20);
00151         uint8_t num_options = 0;
00152         while( sep_pos != NULL && num_options < 5){
00153             size_t len = (size_t)(sep_pos-rest);
00154             if( len > 19 ){
00155                 len = 19;
00156             }
00157             memcpy(query_options[num_options], rest, len);
00158             sep_pos++;
00159             rest = sep_pos;
00160             sep_pos = strchr(rest, '&');
00161             num_options++;
00162         }
00163         if( num_options < 5 && strlen(rest) > 0){
00164             size_t len = (size_t)strlen(rest);
00165             if( len > 19 ){
00166                 len = 19;
00167             }
00168             memcpy(query_options[num_options++], rest, len);
00169         }
00170 
00171         for (int option = 0; option < num_options; option++) {
00172             success = set_notification_attribute(query_options[option],type, resource_type);
00173             if (!success) {
00174                 tr_debug("M2MReportHandler::parse_notification_attribute - break");
00175                 break;
00176             }
00177         }
00178 
00179         if(success) {
00180              success = check_attribute_validity();
00181         }
00182         else {
00183             tr_debug("M2MReportHandler::parse_notification_attribute - not valid query");
00184             _pmin = pmin;
00185             _pmax = pmax;
00186             _st = st;
00187             _lt = lt;
00188             _gt = gt;
00189             _high_step = high;
00190             _low_step = low;
00191             _attribute_state = attr;
00192         }
00193     }
00194     else {
00195         if(set_notification_attribute(query, type, resource_type)) {
00196             success = check_attribute_validity();
00197         }
00198     }
00199 
00200     return success;
00201 }
00202 
00203 void M2MReportHandler::timer_expired(M2MTimerObserver::Type type)
00204 {
00205     switch(type) {
00206         case M2MTimerObserver::PMinTimer: {
00207             tr_debug("M2MReportHandler::timer_expired - PMIN");
00208 
00209             _pmin_exceeded = true;
00210             if (_notify ||
00211                 (_pmin > 0 && (_attribute_state & M2MReportHandler::Pmax) != M2MReportHandler::Pmax)){
00212                 report();
00213             }
00214 
00215             // If value hasn't changed since last expiration, next value change should send notification immediately
00216             if (_current_value == _last_value) {
00217                 _pmin_quiet_period = true;
00218             }
00219         }
00220         break;
00221         case M2MTimerObserver::PMaxTimer: {
00222             tr_debug("M2MReportHandler::timer_expired - PMAX");
00223             _pmax_exceeded = true;
00224             if (_pmin_exceeded ||
00225                     (_attribute_state & M2MReportHandler::Pmin) != M2MReportHandler::Pmin ) {
00226                 report();
00227             }
00228         }
00229         break;
00230         default:
00231             break;
00232     }
00233 }
00234 
00235 bool M2MReportHandler::set_notification_attribute(const char* option,
00236                                                   M2MBase::BaseType type,
00237                                                   M2MResourceInstance::ResourceType resource_type)
00238 {
00239     tr_debug("M2MReportHandler::set_notification_attribute()");
00240     bool success = false;
00241     char attribute[20];
00242     char value[20];
00243     memset(&attribute, 0, 20);
00244     memset(&value, 0, 20);
00245 
00246     const char* pos = strstr(option, EQUAL);
00247     if( pos != NULL ){
00248         memcpy(attribute, option, (size_t)(pos-option));
00249         pos++;
00250         memcpy(value, pos, strlen(pos));
00251     }else{
00252         memcpy(attribute, option, (size_t)strlen(option) + 1);
00253     }
00254 
00255     if (strlen(value)) {
00256         if (strcmp(attribute, PMIN) == 0) {
00257            _pmin = atoi(value);
00258             success = true;
00259             _attribute_state |= M2MReportHandler::Pmin;
00260             tr_info("M2MReportHandler::set_notification_attribute %s to %" PRId32, attribute, _pmin);
00261         }
00262         else if(strcmp(attribute, PMAX) == 0) {
00263             _pmax = atoi(value);
00264             success = true;
00265             _attribute_state |= M2MReportHandler::Pmax;
00266             tr_info("M2MReportHandler::set_notification_attribute %s to %" PRId32, attribute, _pmax);
00267         }
00268         else if(strcmp(attribute, GT) == 0 &&
00269                 (M2MBase::Resource == type)){
00270             _gt = atof(value);
00271             success = true;
00272             _attribute_state |= M2MReportHandler::Gt;
00273             tr_info("M2MReportHandler::set_notification_attribute %s to %f", attribute, _gt);
00274         }
00275         else if(strcmp(attribute, LT) == 0 &&
00276                 (M2MBase::Resource == type)){
00277             _lt = atof(value);
00278             success = true;
00279             _attribute_state |= M2MReportHandler::Lt;
00280             tr_info("M2MReportHandler::set_notification_attribute %s to %f", attribute, _lt);
00281         }
00282         else if((strcmp(attribute, ST_SIZE) == 0 || (strcmp(attribute, STP) == 0))
00283                 && (M2MBase::Resource == type)){
00284             _st = atof(value);
00285             success = true;
00286             _high_step = _current_value + _st;
00287             _low_step = _current_value - _st;
00288             _attribute_state |= M2MReportHandler::St;
00289             tr_info("M2MReportHandler::set_notification_attribute %s to %f", attribute, _st);
00290         }
00291         // Return false if try to set gt,lt or st when the resource type is something else than numerical
00292         if ((resource_type != M2MResourceInstance::INTEGER &&
00293                 resource_type != M2MResourceInstance::FLOAT) &&
00294                 ((_attribute_state & M2MReportHandler::Gt) == M2MReportHandler::Gt ||
00295                 (_attribute_state & M2MReportHandler::Lt) == M2MReportHandler::Lt ||
00296                 (_attribute_state & M2MReportHandler::St) == M2MReportHandler::St)) {
00297             tr_debug("M2MReportHandler::set_notification_attribute - not numerical resource");
00298             success = false;
00299         }
00300     }
00301     return success;
00302 }
00303 
00304 void M2MReportHandler::schedule_report(bool in_queue)
00305 {
00306     tr_debug("M2MReportHandler::schedule_report()");
00307     _notify = true;
00308 
00309     if ((_attribute_state & M2MReportHandler::Pmin) != M2MReportHandler::Pmin ||
00310          _pmin_exceeded ||
00311          _pmin_quiet_period) {
00312         report(in_queue);
00313     }
00314 }
00315 
00316 void M2MReportHandler::report(bool in_queue)
00317 {
00318     tr_debug("M2MReportHandler::report() - current %2f, last %2f, notify %d, queued %d", _current_value, _last_value, _notify, in_queue);
00319 
00320     if((_current_value != _last_value && _notify) || in_queue) {
00321         if (_pmin_exceeded) {
00322             tr_debug("M2MReportHandler::report()- send with PMIN expiration");
00323         } else {
00324             tr_debug("M2MReportHandler::report()- send with VALUE change");
00325         }
00326 
00327         _pmin_exceeded = false;
00328         _pmax_exceeded = false;
00329         _notify = false;
00330         _pmin_quiet_period = false;
00331         _observation_number++;
00332 
00333         if (_observation_number == 1) {
00334             // Increment the observation number by 1 if it is already 1 because CoAP specification has reserved 1 for DEREGISTER notification
00335             _observation_number++;
00336         }
00337 
00338         if (_observer.observation_to_be_sent(_changed_instance_ids, observation_number())) {
00339             _changed_instance_ids.clear();
00340             set_notification_send_in_progress(true);
00341         }
00342 
00343         _pmax_timer.stop_timer();
00344     }
00345     else {
00346         if (_pmax_exceeded) {
00347             tr_debug("M2MReportHandler::report()- send with PMAX expiration");
00348             _observation_number++;
00349 
00350             if (_observation_number == 1) {
00351                 // Increment the observation number by 1 if it is already 1 because CoAP specification has reserved 1 for DEREGISTER notification
00352                 _observation_number++;
00353             }
00354 
00355             if (_observer.observation_to_be_sent(_changed_instance_ids, observation_number(), true)) {
00356                 _changed_instance_ids.clear();
00357                 set_notification_send_in_progress(true);
00358             } else {
00359                 set_notification_in_queue(true);
00360             }
00361         }
00362         else {
00363             tr_debug("M2MReportHandler::report()- no need to send");
00364         }
00365     }
00366     handle_timers();
00367     _last_value = _current_value;
00368 }
00369 
00370 void M2MReportHandler::handle_timers()
00371 {
00372     tr_debug("M2MReportHandler::handle_timers()");
00373     uint64_t time_interval = 0;
00374     if ((_attribute_state & M2MReportHandler::Pmin) == M2MReportHandler::Pmin) {
00375         if (_pmin == _pmax) {
00376             _pmin_exceeded = true;
00377         } else {
00378             _pmin_exceeded = false;
00379             time_interval = (uint64_t) ((uint64_t)_pmin * 1000);
00380             tr_debug("M2MReportHandler::handle_timers() - Start PMIN interval: %d", (int)time_interval);
00381             _pmin_timer.start_timer(time_interval,
00382                                      M2MTimerObserver::PMinTimer,
00383                                      true);
00384         }
00385     }
00386     if ((_attribute_state & M2MReportHandler::Pmax) == M2MReportHandler::Pmax) {
00387         if (_pmax > 0) {
00388             time_interval = (uint64_t) ((uint64_t)_pmax * 1000);
00389             tr_debug("M2MReportHandler::handle_timers() - Start PMAX interval: %d", (int)time_interval);
00390             _pmax_timer.start_timer(time_interval,
00391                                      M2MTimerObserver::PMaxTimer,
00392                                      true);
00393         }
00394     }
00395 }
00396 
00397 bool M2MReportHandler::check_attribute_validity() const
00398 {
00399     bool success = true;
00400     if ((_attribute_state & M2MReportHandler::Pmax) == M2MReportHandler::Pmax &&
00401             ((_pmax >= -1.0) && (_pmin > _pmax))) {
00402         success = false;
00403     }
00404     float low = _lt + 2 * _st;
00405     if ((_attribute_state & M2MReportHandler::Gt) == M2MReportHandler::Gt &&
00406             (low >= _gt)) {
00407         success = false;
00408     }
00409     return success;
00410 }
00411 
00412 void M2MReportHandler::stop_timers()
00413 {
00414     tr_debug("M2MReportHandler::stop_timers()");
00415 
00416     _pmin_exceeded = false;
00417     _pmin_timer.stop_timer();
00418 
00419     _pmax_exceeded = false;
00420     _pmax_timer.stop_timer();
00421 
00422     tr_debug("M2MReportHandler::stop_timers() - out");
00423 }
00424 
00425 void M2MReportHandler::set_default_values()
00426 {
00427     tr_debug("M2MReportHandler::set_default_values");
00428     _pmax = -1.0;
00429     _pmin = 1.0;
00430     _gt = 0.0f;
00431     _lt = 0.0f;
00432     _st = 0.0f;
00433     _high_step = 0.0f;
00434     _low_step = 0.0f;
00435     _pmin_exceeded = false;
00436     _pmax_exceeded = false;
00437     _last_value = -1.0f;
00438     _attribute_state = 0;
00439     _changed_instance_ids.clear();
00440     _notification_in_queue = false;
00441     _notification_send_in_progress = false;
00442     _pmin_quiet_period = false;
00443 }
00444 
00445 bool M2MReportHandler::check_threshold_values() const
00446 {
00447     tr_debug("M2MReportHandler::check_threshold_values");
00448     tr_debug("Current value: %f", _current_value);
00449     tr_debug("Last value: %f", _last_value);
00450     tr_debug("High step: %f", _high_step);
00451     tr_debug("Low step: %f", _low_step);
00452     tr_debug("Less than: %f", _lt);
00453     tr_debug("Greater than: %f", _gt);
00454     tr_debug("Step: %f", _st);
00455 
00456     bool can_send = check_gt_lt_params();
00457     if (can_send) {
00458         if ((_attribute_state & M2MReportHandler::St) == M2MReportHandler::St) {
00459             if ((_current_value >= _high_step ||
00460                 _current_value <= _low_step)) {
00461                 can_send = true;
00462             } else {
00463                 can_send = false;
00464             }
00465         }
00466     }
00467 
00468     tr_debug("M2MReportHandler::check_threshold_values - value can be sent = %d", (int)can_send);
00469     return can_send;
00470 }
00471 
00472 bool M2MReportHandler::check_gt_lt_params() const
00473 {
00474     tr_debug("M2MReportHandler::check_gt_lt_params");
00475     bool can_send = false;
00476     // GT & LT set.
00477     if ((_attribute_state & (M2MReportHandler::Lt | M2MReportHandler::Gt)) ==
00478         (M2MReportHandler::Lt | M2MReportHandler::Gt)) {
00479         if (_current_value > _gt || _current_value < _lt) {
00480             can_send = true;
00481         }
00482         else {
00483             can_send = false;
00484         }
00485     }
00486     // Only LT
00487     else if ((_attribute_state & M2MReportHandler::Lt) == M2MReportHandler::Lt &&
00488            (_attribute_state & M2MReportHandler::Gt) == 0 ) {
00489         if (_current_value < _lt) {
00490             can_send = true;
00491         }
00492         else {
00493             can_send = false;
00494         }
00495     }
00496     // Only GT
00497     else if ((_attribute_state & M2MReportHandler::Gt) == M2MReportHandler::Gt &&
00498            (_attribute_state & M2MReportHandler::Lt) == 0 ) {
00499         if (_current_value > _gt) {
00500             can_send = true;
00501         }
00502         else {
00503             can_send = false;
00504         }
00505     }
00506     // GT & LT not set.
00507     else {
00508         can_send = true;
00509     }
00510     tr_debug("M2MReportHandler::check_gt_lt_params - value in range = %d", (int)can_send);
00511     return can_send;
00512 }
00513 
00514 uint8_t M2MReportHandler::attribute_flags() const
00515 {
00516     return _attribute_state;
00517 }
00518 
00519 void M2MReportHandler::set_observation_token(const uint8_t *token, const uint8_t length)
00520 {
00521      free(_token);
00522      _token = NULL;
00523      _token_length = 0;
00524 
00525     if( token != NULL && length > 0 ) {
00526         _token = alloc_string_copy((uint8_t *)token, length);
00527         if(_token) {
00528             _token_length = length;
00529         }
00530     }
00531 }
00532 
00533 void M2MReportHandler::get_observation_token(uint8_t *token, uint8_t &token_length) const
00534 {
00535     memcpy(token, _token, _token_length);
00536     token_length = _token_length;
00537 }
00538 
00539 uint16_t M2MReportHandler::observation_number() const
00540 {
00541     return _observation_number;
00542 }
00543 
00544 void M2MReportHandler::add_observation_level(M2MBase::Observation obs_level)
00545 {
00546     _observation_level = (M2MBase::Observation)(_observation_level | obs_level);
00547 }
00548 
00549 void M2MReportHandler::remove_observation_level(M2MBase::Observation obs_level)
00550 {
00551     _observation_level = (M2MBase::Observation)(_observation_level & ~obs_level);
00552 }
00553 
00554 M2MBase::Observation M2MReportHandler::observation_level() const
00555 {
00556     return _observation_level;
00557 }
00558 
00559 bool M2MReportHandler::is_under_observation() const
00560 {
00561     return _is_under_observation;
00562 }
00563 
00564 uint8_t* M2MReportHandler::alloc_string_copy(const uint8_t* source, uint32_t size)
00565 {
00566     assert(source != NULL);
00567 
00568     uint8_t* result = (uint8_t*)malloc(size + 1);
00569     if (result) {
00570         memcpy(result, source, size);
00571         result[size] = '\0';
00572     }
00573     return result;
00574 }
00575 
00576 void M2MReportHandler::set_notification_in_queue(bool to_queue)
00577 {
00578     _notification_in_queue = to_queue;
00579 }
00580 
00581 bool M2MReportHandler::notification_in_queue() const
00582 {
00583     return _notification_in_queue;
00584 }
00585 
00586 void M2MReportHandler::set_notification_send_in_progress(bool progress)
00587 {
00588     _notification_send_in_progress = progress;
00589 }
00590 
00591 bool M2MReportHandler::notification_send_in_progress() const
00592 {
00593     return _notification_send_in_progress;
00594 }
00595 
00596 void M2MReportHandler::set_blockwise_notify(bool blockwise_notify)
00597 {
00598     _blockwise_notify = blockwise_notify;
00599 }
00600 
00601 bool M2MReportHandler::blockwise_notify() const
00602 {
00603     return _blockwise_notify;
00604 }