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

m2mtimerpimpl.cpp

00001 /*
00002  * Copyright (c) 2015-2016 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 #include "mbed-client-classic/m2mtimerpimpl.h"
00018 #include "mbed-client/m2mtimerobserver.h"
00019 
00020 #include "eventOS_event_timer.h"
00021 #include "eventOS_scheduler.h"
00022 
00023 #include <assert.h>
00024 #include <string.h>
00025 
00026 
00027 #define MBED_CLIENT_TIMER_TASKLET_INIT_EVENT 0 // Tasklet init occurs always when generating a tasklet
00028 #define MBED_CLIENT_TIMER_EVENT 10
00029 
00030 // This is set to _status on constructor, which forces the lazy second phase initialization
00031 // to happen once in initialize_tasklet(). Whole scheme is there to avoid overhead or
00032 // unwanted serialization on event OS scheduler mutex, as the whole tasklet needs to be initialized
00033 // just once for the whole lifecycle of cloud client.
00034 #define STATUS_INIT_NOT_DONE_YET 3
00035 
00036 
00037 int8_t M2MTimerPimpl::_tasklet_id = -1;
00038 
00039 extern "C" void tasklet_func(arm_event_s *event)
00040 {
00041     // skip the init event as there will be a timer event after
00042     if (event->event_type == MBED_CLIENT_TIMER_EVENT) {
00043 
00044         M2MTimerPimpl* timer = (M2MTimerPimpl*)event->data_ptr;
00045         assert(timer);
00046         timer->handle_timer_event(*event);
00047     }
00048 }
00049 
00050 void M2MTimerPimpl::handle_timer_event(const arm_event_s &event)
00051 {
00052     // Clear the reference to timer event which is now received and handled.
00053     // This avoids the useless work from canceling a event if the timer is restarted
00054     // and also lets the assertions verify the object state correctly.
00055     _timer_event = NULL;
00056 
00057     if (get_still_left_time() > 0) {
00058         start_still_left_timer();
00059     } else {
00060         timer_expired();
00061     }
00062 }
00063 
00064 M2MTimerPimpl::M2MTimerPimpl(M2MTimerObserver& observer)
00065 : _observer(observer),
00066   _interval(0),
00067   _intermediate_interval(0),
00068   _total_interval(0),
00069   _still_left(0),
00070   _timer_event(NULL),
00071   _type(M2MTimerObserver::Notdefined),
00072   _status(STATUS_INIT_NOT_DONE_YET),
00073   _dtls_type(false),
00074   _single_shot(true)
00075 {
00076 }
00077 
00078 M2MTimerPimpl::~M2MTimerPimpl()
00079 {
00080     // cancel the timer request, if any is pending
00081     cancel();
00082 
00083     // there is no turning back, event os does not have eventOS_event_handler_delete() or similar,
00084     // so the tasklet is lost forever.
00085 }
00086 
00087 void M2MTimerPimpl::initialize_tasklet()
00088 {
00089     // A micro-optimization to avoid operations on mutex on every time the timer is started.
00090     // After all, the tasklet needs to be created just once for the lifecyle of whole Mbed cloud client.
00091     if (_status == STATUS_INIT_NOT_DONE_YET) {
00092 
00093         eventOS_scheduler_mutex_wait();
00094 
00095         if (_tasklet_id < 0) {
00096             _tasklet_id = eventOS_event_handler_create(tasklet_func, MBED_CLIENT_TIMER_TASKLET_INIT_EVENT);
00097             assert(_tasklet_id >= 0);
00098         }
00099 
00100         _status = 0;
00101 
00102         eventOS_scheduler_mutex_release();
00103     }
00104 }
00105 
00106 void M2MTimerPimpl::start_timer(uint64_t interval,
00107                                 M2MTimerObserver::Type type,
00108                                 bool single_shot)
00109 {
00110     initialize_tasklet();
00111 
00112     _dtls_type = false;
00113     _intermediate_interval = 0;
00114     _total_interval = 0;
00115     _status = 0;
00116     _single_shot = single_shot;
00117     _interval = interval;
00118     _type = type;
00119     _still_left = 0;
00120     start();
00121 }
00122 
00123 void M2MTimerPimpl::start_dtls_timer(uint64_t intermediate_interval, uint64_t total_interval, M2MTimerObserver::Type type)
00124 {
00125     initialize_tasklet();
00126 
00127     _dtls_type = true;
00128     _intermediate_interval = intermediate_interval;
00129     _total_interval = total_interval;
00130     _interval = _intermediate_interval;
00131     _status = 0;
00132     _single_shot = false;
00133     _type = type;
00134     start();
00135 }
00136 
00137 void M2MTimerPimpl::start()
00138 {
00139     // Cancel ongoing events before creating a new one.
00140     // Otherwise it can happen that there are multiple events running at the same time.
00141     cancel();
00142 
00143     int32_t wait_time;
00144 
00145     if (_interval > INT32_MAX) {
00146         _still_left = _interval - INT32_MAX;
00147         wait_time = INT32_MAX;
00148     } else {
00149         wait_time = _interval;
00150     }
00151 
00152     request_event_in(wait_time);
00153 }
00154 
00155 void M2MTimerPimpl::request_event_in(int32_t delay_ms)
00156 {
00157     // init struct to zero to avoid hassle when new fields are added to it
00158     arm_event_t event = { 0 };
00159 
00160     event.receiver = _tasklet_id;
00161     event.sender = _tasklet_id;
00162     event.event_type = MBED_CLIENT_TIMER_EVENT;
00163     event.data_ptr = this;
00164     event.priority = ARM_LIB_MED_PRIORITY_EVENT;
00165 
00166     // check first, that there is no timer event still pending
00167     assert(_timer_event == NULL);
00168 
00169     const uint32_t delay_ticks = eventOS_event_timer_ms_to_ticks(delay_ms);
00170 
00171     _timer_event = eventOS_event_timer_request_in(&event, delay_ticks);
00172 
00173     // The timer request may fail only if the system is out of pre-allocated
00174     // timers and it can not allocate more.
00175     assert(_timer_event != NULL);
00176 }
00177 
00178 void M2MTimerPimpl::cancel()
00179 {
00180     // NULL event is ok to cancel
00181     eventOS_cancel(_timer_event);
00182 
00183     _timer_event = NULL;
00184 }
00185 
00186 void M2MTimerPimpl::stop_timer()
00187 {
00188     _interval = 0;
00189     _single_shot = true;
00190     _still_left = 0;
00191     cancel();
00192 }
00193 
00194 void M2MTimerPimpl::timer_expired()
00195 {
00196     _status++;
00197 
00198     // The code is  expecting that the expiration has happened 0, 1 or more times,
00199     // and we also need to check for overflow as the _status is stored in 2 bits slot.
00200     if (_status > 2) {
00201         _status = 2;
00202     }
00203 
00204     _observer.timer_expired(_type);
00205 
00206     if ((!_dtls_type) && (!_single_shot)) {
00207         // start next round of periodic timer
00208         start();
00209     } else if ((_dtls_type) && (!is_total_interval_passed())) {
00210         // if only the intermediate time has passed, we need still wait up to total time
00211         _interval = _total_interval - _intermediate_interval;
00212         start();
00213     }
00214 }
00215 
00216 bool M2MTimerPimpl::is_intermediate_interval_passed() const
00217 {
00218     if (_status > 0) {
00219         return true;
00220     }
00221     return false;
00222 }
00223 
00224 bool M2MTimerPimpl::is_total_interval_passed() const
00225 {
00226     if (_status > 1) {
00227         return true;
00228     }
00229     return false;
00230 }
00231 
00232 uint64_t M2MTimerPimpl::get_still_left_time() const
00233 {
00234    return _still_left;
00235 }
00236 
00237 void M2MTimerPimpl::start_still_left_timer()
00238 {
00239     if (_still_left > 0) {
00240 
00241         int32_t wait_time;
00242 
00243         if (_still_left > INT32_MAX) {
00244             _still_left = _still_left - INT32_MAX;
00245             wait_time = INT32_MAX;
00246         } else {
00247             wait_time = _still_left;
00248             _still_left = 0;
00249         }
00250 
00251         request_event_in(wait_time);
00252 
00253     } else {
00254         _observer.timer_expired(_type);
00255         if (!_single_shot) {
00256             start_timer(_interval, _type, _single_shot);
00257         }
00258     }
00259 }