pick up wakaama files from https://github.com/eclipse/wakaama

Committer:
terencez
Date:
Wed Apr 19 11:28:00 2017 +0000
Revision:
0:3f48af28ebcd
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
terencez 0:3f48af28ebcd 1 /*******************************************************************************
terencez 0:3f48af28ebcd 2 *
terencez 0:3f48af28ebcd 3 * Copyright (c) 2013, 2014 Intel Corporation and others.
terencez 0:3f48af28ebcd 4 * All rights reserved. This program and the accompanying materials
terencez 0:3f48af28ebcd 5 * are made available under the terms of the Eclipse Public License v1.0
terencez 0:3f48af28ebcd 6 * and Eclipse Distribution License v1.0 which accompany this distribution.
terencez 0:3f48af28ebcd 7 *
terencez 0:3f48af28ebcd 8 * The Eclipse Public License is available at
terencez 0:3f48af28ebcd 9 * http://www.eclipse.org/legal/epl-v10.html
terencez 0:3f48af28ebcd 10 * The Eclipse Distribution License is available at
terencez 0:3f48af28ebcd 11 * http://www.eclipse.org/org/documents/edl-v10.php.
terencez 0:3f48af28ebcd 12 *
terencez 0:3f48af28ebcd 13 * Contributors:
terencez 0:3f48af28ebcd 14 * David Navarro, Intel Corporation - initial API and implementation
terencez 0:3f48af28ebcd 15 * Toby Jaffey - Please refer to git log
terencez 0:3f48af28ebcd 16 * Bosch Software Innovations GmbH - Please refer to git log
terencez 0:3f48af28ebcd 17 *
terencez 0:3f48af28ebcd 18 *******************************************************************************/
terencez 0:3f48af28ebcd 19
terencez 0:3f48af28ebcd 20 /*
terencez 0:3f48af28ebcd 21 Copyright (c) 2013, 2014 Intel Corporation
terencez 0:3f48af28ebcd 22
terencez 0:3f48af28ebcd 23 Redistribution and use in source and binary forms, with or without modification,
terencez 0:3f48af28ebcd 24 are permitted provided that the following conditions are met:
terencez 0:3f48af28ebcd 25
terencez 0:3f48af28ebcd 26 * Redistributions of source code must retain the above copyright notice,
terencez 0:3f48af28ebcd 27 this list of conditions and the following disclaimer.
terencez 0:3f48af28ebcd 28 * Redistributions in binary form must reproduce the above copyright notice,
terencez 0:3f48af28ebcd 29 this list of conditions and the following disclaimer in the documentation
terencez 0:3f48af28ebcd 30 and/or other materials provided with the distribution.
terencez 0:3f48af28ebcd 31 * Neither the name of Intel Corporation nor the names of its contributors
terencez 0:3f48af28ebcd 32 may be used to endorse or promote products derived from this software
terencez 0:3f48af28ebcd 33 without specific prior written permission.
terencez 0:3f48af28ebcd 34
terencez 0:3f48af28ebcd 35 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
terencez 0:3f48af28ebcd 36 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
terencez 0:3f48af28ebcd 37 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
terencez 0:3f48af28ebcd 38 IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
terencez 0:3f48af28ebcd 39 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
terencez 0:3f48af28ebcd 40 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
terencez 0:3f48af28ebcd 41 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
terencez 0:3f48af28ebcd 42 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
terencez 0:3f48af28ebcd 43 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
terencez 0:3f48af28ebcd 44 THE POSSIBILITY OF SUCH DAMAGE.
terencez 0:3f48af28ebcd 45
terencez 0:3f48af28ebcd 46 David Navarro <david.navarro@intel.com>
terencez 0:3f48af28ebcd 47
terencez 0:3f48af28ebcd 48 */
terencez 0:3f48af28ebcd 49
terencez 0:3f48af28ebcd 50 #include "internals.h"
terencez 0:3f48af28ebcd 51 #include <stdio.h>
terencez 0:3f48af28ebcd 52
terencez 0:3f48af28ebcd 53
terencez 0:3f48af28ebcd 54 #ifdef LWM2M_CLIENT_MODE
terencez 0:3f48af28ebcd 55 static lwm2m_observed_t * prv_findObserved(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 56 lwm2m_uri_t * uriP)
terencez 0:3f48af28ebcd 57 {
terencez 0:3f48af28ebcd 58 lwm2m_observed_t * targetP;
terencez 0:3f48af28ebcd 59
terencez 0:3f48af28ebcd 60 targetP = contextP->observedList;
terencez 0:3f48af28ebcd 61 while (targetP != NULL
terencez 0:3f48af28ebcd 62 && (targetP->uri.objectId != uriP->objectId
terencez 0:3f48af28ebcd 63 || targetP->uri.flag != uriP->flag
terencez 0:3f48af28ebcd 64 || (LWM2M_URI_IS_SET_INSTANCE(uriP) && targetP->uri.instanceId != uriP->instanceId)
terencez 0:3f48af28ebcd 65 || (LWM2M_URI_IS_SET_RESOURCE(uriP) && targetP->uri.resourceId != uriP->resourceId)))
terencez 0:3f48af28ebcd 66 {
terencez 0:3f48af28ebcd 67 targetP = targetP->next;
terencez 0:3f48af28ebcd 68 }
terencez 0:3f48af28ebcd 69
terencez 0:3f48af28ebcd 70 return targetP;
terencez 0:3f48af28ebcd 71 }
terencez 0:3f48af28ebcd 72
terencez 0:3f48af28ebcd 73 static void prv_unlinkObserved(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 74 lwm2m_observed_t * observedP)
terencez 0:3f48af28ebcd 75 {
terencez 0:3f48af28ebcd 76 if (contextP->observedList == observedP)
terencez 0:3f48af28ebcd 77 {
terencez 0:3f48af28ebcd 78 contextP->observedList = contextP->observedList->next;
terencez 0:3f48af28ebcd 79 }
terencez 0:3f48af28ebcd 80 else
terencez 0:3f48af28ebcd 81 {
terencez 0:3f48af28ebcd 82 lwm2m_observed_t * parentP;
terencez 0:3f48af28ebcd 83
terencez 0:3f48af28ebcd 84 parentP = contextP->observedList;
terencez 0:3f48af28ebcd 85 while (parentP->next != NULL
terencez 0:3f48af28ebcd 86 && parentP->next != observedP)
terencez 0:3f48af28ebcd 87 {
terencez 0:3f48af28ebcd 88 parentP = parentP->next;
terencez 0:3f48af28ebcd 89 }
terencez 0:3f48af28ebcd 90 if (parentP->next != NULL)
terencez 0:3f48af28ebcd 91 {
terencez 0:3f48af28ebcd 92 parentP->next = parentP->next->next;
terencez 0:3f48af28ebcd 93 }
terencez 0:3f48af28ebcd 94 }
terencez 0:3f48af28ebcd 95 }
terencez 0:3f48af28ebcd 96
terencez 0:3f48af28ebcd 97 static lwm2m_watcher_t * prv_findWatcher(lwm2m_observed_t * observedP,
terencez 0:3f48af28ebcd 98 lwm2m_server_t * serverP)
terencez 0:3f48af28ebcd 99 {
terencez 0:3f48af28ebcd 100 lwm2m_watcher_t * targetP;
terencez 0:3f48af28ebcd 101
terencez 0:3f48af28ebcd 102 targetP = observedP->watcherList;
terencez 0:3f48af28ebcd 103 while (targetP != NULL
terencez 0:3f48af28ebcd 104 && targetP->server != serverP)
terencez 0:3f48af28ebcd 105 {
terencez 0:3f48af28ebcd 106 targetP = targetP->next;
terencez 0:3f48af28ebcd 107 }
terencez 0:3f48af28ebcd 108
terencez 0:3f48af28ebcd 109 return targetP;
terencez 0:3f48af28ebcd 110 }
terencez 0:3f48af28ebcd 111
terencez 0:3f48af28ebcd 112 static lwm2m_watcher_t * prv_getWatcher(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 113 lwm2m_uri_t * uriP,
terencez 0:3f48af28ebcd 114 lwm2m_server_t * serverP)
terencez 0:3f48af28ebcd 115 {
terencez 0:3f48af28ebcd 116 lwm2m_observed_t * observedP;
terencez 0:3f48af28ebcd 117 bool allocatedObserver;
terencez 0:3f48af28ebcd 118 lwm2m_watcher_t * watcherP;
terencez 0:3f48af28ebcd 119
terencez 0:3f48af28ebcd 120 allocatedObserver = false;
terencez 0:3f48af28ebcd 121
terencez 0:3f48af28ebcd 122 observedP = prv_findObserved(contextP, uriP);
terencez 0:3f48af28ebcd 123 if (observedP == NULL)
terencez 0:3f48af28ebcd 124 {
terencez 0:3f48af28ebcd 125 observedP = (lwm2m_observed_t *)lwm2m_malloc(sizeof(lwm2m_observed_t));
terencez 0:3f48af28ebcd 126 if (observedP == NULL) return NULL;
terencez 0:3f48af28ebcd 127 allocatedObserver = true;
terencez 0:3f48af28ebcd 128 memset(observedP, 0, sizeof(lwm2m_observed_t));
terencez 0:3f48af28ebcd 129 memcpy(&(observedP->uri), uriP, sizeof(lwm2m_uri_t));
terencez 0:3f48af28ebcd 130 observedP->next = contextP->observedList;
terencez 0:3f48af28ebcd 131 contextP->observedList = observedP;
terencez 0:3f48af28ebcd 132 }
terencez 0:3f48af28ebcd 133
terencez 0:3f48af28ebcd 134 watcherP = prv_findWatcher(observedP, serverP);
terencez 0:3f48af28ebcd 135 if (watcherP == NULL)
terencez 0:3f48af28ebcd 136 {
terencez 0:3f48af28ebcd 137 watcherP = (lwm2m_watcher_t *)lwm2m_malloc(sizeof(lwm2m_watcher_t));
terencez 0:3f48af28ebcd 138 if (watcherP == NULL)
terencez 0:3f48af28ebcd 139 {
terencez 0:3f48af28ebcd 140 if (allocatedObserver == true)
terencez 0:3f48af28ebcd 141 {
terencez 0:3f48af28ebcd 142 lwm2m_free(observedP);
terencez 0:3f48af28ebcd 143 }
terencez 0:3f48af28ebcd 144 return NULL;
terencez 0:3f48af28ebcd 145 }
terencez 0:3f48af28ebcd 146 memset(watcherP, 0, sizeof(lwm2m_watcher_t));
terencez 0:3f48af28ebcd 147 watcherP->active = false;
terencez 0:3f48af28ebcd 148 watcherP->server = serverP;
terencez 0:3f48af28ebcd 149 watcherP->next = observedP->watcherList;
terencez 0:3f48af28ebcd 150 observedP->watcherList = watcherP;
terencez 0:3f48af28ebcd 151 }
terencez 0:3f48af28ebcd 152
terencez 0:3f48af28ebcd 153 return watcherP;
terencez 0:3f48af28ebcd 154 }
terencez 0:3f48af28ebcd 155
terencez 0:3f48af28ebcd 156 coap_status_t observe_handleRequest(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 157 lwm2m_uri_t * uriP,
terencez 0:3f48af28ebcd 158 lwm2m_server_t * serverP,
terencez 0:3f48af28ebcd 159 int size,
terencez 0:3f48af28ebcd 160 lwm2m_data_t * dataP,
terencez 0:3f48af28ebcd 161 coap_packet_t * message,
terencez 0:3f48af28ebcd 162 coap_packet_t * response)
terencez 0:3f48af28ebcd 163 {
terencez 0:3f48af28ebcd 164 lwm2m_watcher_t * watcherP;
terencez 0:3f48af28ebcd 165 uint32_t count;
terencez 0:3f48af28ebcd 166
terencez 0:3f48af28ebcd 167 LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status));
terencez 0:3f48af28ebcd 168 LOG_URI(uriP);
terencez 0:3f48af28ebcd 169
terencez 0:3f48af28ebcd 170 coap_get_header_observe(message, &count);
terencez 0:3f48af28ebcd 171
terencez 0:3f48af28ebcd 172 switch (count)
terencez 0:3f48af28ebcd 173 {
terencez 0:3f48af28ebcd 174 case 0:
terencez 0:3f48af28ebcd 175 if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 176 if (message->token_len == 0) return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 177
terencez 0:3f48af28ebcd 178 watcherP = prv_getWatcher(contextP, uriP, serverP);
terencez 0:3f48af28ebcd 179 if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 180
terencez 0:3f48af28ebcd 181 watcherP->tokenLen = message->token_len;
terencez 0:3f48af28ebcd 182 memcpy(watcherP->token, message->token, message->token_len);
terencez 0:3f48af28ebcd 183 watcherP->active = true;
terencez 0:3f48af28ebcd 184 watcherP->lastTime = lwm2m_gettime();
terencez 0:3f48af28ebcd 185
terencez 0:3f48af28ebcd 186 if (LWM2M_URI_IS_SET_RESOURCE(uriP))
terencez 0:3f48af28ebcd 187 {
terencez 0:3f48af28ebcd 188 switch (dataP->type)
terencez 0:3f48af28ebcd 189 {
terencez 0:3f48af28ebcd 190 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 191 if (1 != lwm2m_data_decode_int(dataP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 192 break;
terencez 0:3f48af28ebcd 193 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 194 if (1 != lwm2m_data_decode_float(dataP, &(watcherP->lastValue.asFloat))) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 195 break;
terencez 0:3f48af28ebcd 196 default:
terencez 0:3f48af28ebcd 197 break;
terencez 0:3f48af28ebcd 198 }
terencez 0:3f48af28ebcd 199 }
terencez 0:3f48af28ebcd 200
terencez 0:3f48af28ebcd 201 coap_set_header_observe(response, watcherP->counter++);
terencez 0:3f48af28ebcd 202
terencez 0:3f48af28ebcd 203 return COAP_205_CONTENT;
terencez 0:3f48af28ebcd 204
terencez 0:3f48af28ebcd 205 case 1:
terencez 0:3f48af28ebcd 206 // cancellation
terencez 0:3f48af28ebcd 207 observe_cancel(contextP, LWM2M_MAX_ID, serverP->sessionH);
terencez 0:3f48af28ebcd 208 return COAP_205_CONTENT;
terencez 0:3f48af28ebcd 209
terencez 0:3f48af28ebcd 210 default:
terencez 0:3f48af28ebcd 211 return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 212 }
terencez 0:3f48af28ebcd 213 }
terencez 0:3f48af28ebcd 214
terencez 0:3f48af28ebcd 215 void observe_cancel(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 216 uint16_t mid,
terencez 0:3f48af28ebcd 217 void * fromSessionH)
terencez 0:3f48af28ebcd 218 {
terencez 0:3f48af28ebcd 219 lwm2m_observed_t * observedP;
terencez 0:3f48af28ebcd 220
terencez 0:3f48af28ebcd 221 LOG_ARG("mid: %d", mid);
terencez 0:3f48af28ebcd 222
terencez 0:3f48af28ebcd 223 for (observedP = contextP->observedList;
terencez 0:3f48af28ebcd 224 observedP != NULL;
terencez 0:3f48af28ebcd 225 observedP = observedP->next)
terencez 0:3f48af28ebcd 226 {
terencez 0:3f48af28ebcd 227 lwm2m_watcher_t * targetP = NULL;
terencez 0:3f48af28ebcd 228
terencez 0:3f48af28ebcd 229 if ((LWM2M_MAX_ID == mid || observedP->watcherList->lastMid == mid)
terencez 0:3f48af28ebcd 230 && lwm2m_session_is_equal(observedP->watcherList->server->sessionH, fromSessionH, contextP->userData))
terencez 0:3f48af28ebcd 231 {
terencez 0:3f48af28ebcd 232 targetP = observedP->watcherList;
terencez 0:3f48af28ebcd 233 observedP->watcherList = observedP->watcherList->next;
terencez 0:3f48af28ebcd 234 }
terencez 0:3f48af28ebcd 235 else
terencez 0:3f48af28ebcd 236 {
terencez 0:3f48af28ebcd 237 lwm2m_watcher_t * parentP;
terencez 0:3f48af28ebcd 238
terencez 0:3f48af28ebcd 239 parentP = observedP->watcherList;
terencez 0:3f48af28ebcd 240 while (parentP->next != NULL
terencez 0:3f48af28ebcd 241 && (parentP->next->lastMid != mid
terencez 0:3f48af28ebcd 242 || lwm2m_session_is_equal(parentP->next->server->sessionH, fromSessionH, contextP->userData)))
terencez 0:3f48af28ebcd 243 {
terencez 0:3f48af28ebcd 244 parentP = parentP->next;
terencez 0:3f48af28ebcd 245 }
terencez 0:3f48af28ebcd 246 if (parentP->next != NULL)
terencez 0:3f48af28ebcd 247 {
terencez 0:3f48af28ebcd 248 targetP = parentP->next;
terencez 0:3f48af28ebcd 249 parentP->next = parentP->next->next;
terencez 0:3f48af28ebcd 250 }
terencez 0:3f48af28ebcd 251 }
terencez 0:3f48af28ebcd 252 if (targetP != NULL)
terencez 0:3f48af28ebcd 253 {
terencez 0:3f48af28ebcd 254 lwm2m_free(targetP);
terencez 0:3f48af28ebcd 255 if (observedP->watcherList == NULL)
terencez 0:3f48af28ebcd 256 {
terencez 0:3f48af28ebcd 257 prv_unlinkObserved(contextP, observedP);
terencez 0:3f48af28ebcd 258 lwm2m_free(observedP);
terencez 0:3f48af28ebcd 259 }
terencez 0:3f48af28ebcd 260 return;
terencez 0:3f48af28ebcd 261 }
terencez 0:3f48af28ebcd 262 }
terencez 0:3f48af28ebcd 263 }
terencez 0:3f48af28ebcd 264
terencez 0:3f48af28ebcd 265 coap_status_t observe_setParameters(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 266 lwm2m_uri_t * uriP,
terencez 0:3f48af28ebcd 267 lwm2m_server_t * serverP,
terencez 0:3f48af28ebcd 268 lwm2m_attributes_t * attrP)
terencez 0:3f48af28ebcd 269 {
terencez 0:3f48af28ebcd 270 uint8_t result;
terencez 0:3f48af28ebcd 271 lwm2m_watcher_t * watcherP;
terencez 0:3f48af28ebcd 272
terencez 0:3f48af28ebcd 273 LOG_URI(uriP);
terencez 0:3f48af28ebcd 274 LOG_ARG("toSet: %08X, toClear: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f",
terencez 0:3f48af28ebcd 275 attrP->toSet, attrP->toClear, attrP->minPeriod, attrP->maxPeriod, attrP->greaterThan, attrP->lessThan, attrP->step);
terencez 0:3f48af28ebcd 276
terencez 0:3f48af28ebcd 277 if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 278
terencez 0:3f48af28ebcd 279 result = object_checkReadable(contextP, uriP);
terencez 0:3f48af28ebcd 280 if (COAP_205_CONTENT != result) return result;
terencez 0:3f48af28ebcd 281
terencez 0:3f48af28ebcd 282 if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC))
terencez 0:3f48af28ebcd 283 {
terencez 0:3f48af28ebcd 284 result = object_checkNumeric(contextP, uriP);
terencez 0:3f48af28ebcd 285 if (COAP_205_CONTENT != result) return result;
terencez 0:3f48af28ebcd 286 }
terencez 0:3f48af28ebcd 287
terencez 0:3f48af28ebcd 288 watcherP = prv_getWatcher(contextP, uriP, serverP);
terencez 0:3f48af28ebcd 289 if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 290
terencez 0:3f48af28ebcd 291 // Check rule “lt” value + 2*”stp” values < “gt” value
terencez 0:3f48af28ebcd 292 if ((((attrP->toSet | (watcherP->parameters?watcherP->parameters->toSet:0)) & ~attrP->toClear) & ATTR_FLAG_NUMERIC) == ATTR_FLAG_NUMERIC)
terencez 0:3f48af28ebcd 293 {
terencez 0:3f48af28ebcd 294 float gt;
terencez 0:3f48af28ebcd 295 float lt;
terencez 0:3f48af28ebcd 296 float stp;
terencez 0:3f48af28ebcd 297
terencez 0:3f48af28ebcd 298 if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN))
terencez 0:3f48af28ebcd 299 {
terencez 0:3f48af28ebcd 300 gt = attrP->greaterThan;
terencez 0:3f48af28ebcd 301 }
terencez 0:3f48af28ebcd 302 else
terencez 0:3f48af28ebcd 303 {
terencez 0:3f48af28ebcd 304 gt = watcherP->parameters->greaterThan;
terencez 0:3f48af28ebcd 305 }
terencez 0:3f48af28ebcd 306 if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN))
terencez 0:3f48af28ebcd 307 {
terencez 0:3f48af28ebcd 308 lt = attrP->lessThan;
terencez 0:3f48af28ebcd 309 }
terencez 0:3f48af28ebcd 310 else
terencez 0:3f48af28ebcd 311 {
terencez 0:3f48af28ebcd 312 lt = watcherP->parameters->lessThan;
terencez 0:3f48af28ebcd 313 }
terencez 0:3f48af28ebcd 314 if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_STEP))
terencez 0:3f48af28ebcd 315 {
terencez 0:3f48af28ebcd 316 stp = attrP->step;
terencez 0:3f48af28ebcd 317 }
terencez 0:3f48af28ebcd 318 else
terencez 0:3f48af28ebcd 319 {
terencez 0:3f48af28ebcd 320 stp = watcherP->parameters->step;
terencez 0:3f48af28ebcd 321 }
terencez 0:3f48af28ebcd 322
terencez 0:3f48af28ebcd 323 if (lt + (2 * stp) >= gt) return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 324 }
terencez 0:3f48af28ebcd 325
terencez 0:3f48af28ebcd 326 if (watcherP->parameters == NULL)
terencez 0:3f48af28ebcd 327 {
terencez 0:3f48af28ebcd 328 if (attrP->toSet != 0)
terencez 0:3f48af28ebcd 329 {
terencez 0:3f48af28ebcd 330 watcherP->parameters = (lwm2m_attributes_t *)lwm2m_malloc(sizeof(lwm2m_attributes_t));
terencez 0:3f48af28ebcd 331 if (watcherP->parameters == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 332 memcpy(watcherP->parameters, attrP, sizeof(lwm2m_attributes_t));
terencez 0:3f48af28ebcd 333 }
terencez 0:3f48af28ebcd 334 }
terencez 0:3f48af28ebcd 335 else
terencez 0:3f48af28ebcd 336 {
terencez 0:3f48af28ebcd 337 watcherP->parameters->toSet &= ~attrP->toClear;
terencez 0:3f48af28ebcd 338 if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
terencez 0:3f48af28ebcd 339 {
terencez 0:3f48af28ebcd 340 watcherP->parameters->minPeriod = attrP->minPeriod;
terencez 0:3f48af28ebcd 341 }
terencez 0:3f48af28ebcd 342 if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
terencez 0:3f48af28ebcd 343 {
terencez 0:3f48af28ebcd 344 watcherP->parameters->maxPeriod = attrP->maxPeriod;
terencez 0:3f48af28ebcd 345 }
terencez 0:3f48af28ebcd 346 if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN)
terencez 0:3f48af28ebcd 347 {
terencez 0:3f48af28ebcd 348 watcherP->parameters->greaterThan = attrP->greaterThan;
terencez 0:3f48af28ebcd 349 }
terencez 0:3f48af28ebcd 350 if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN)
terencez 0:3f48af28ebcd 351 {
terencez 0:3f48af28ebcd 352 watcherP->parameters->lessThan = attrP->lessThan;
terencez 0:3f48af28ebcd 353 }
terencez 0:3f48af28ebcd 354 if (attrP->toSet & LWM2M_ATTR_FLAG_STEP)
terencez 0:3f48af28ebcd 355 {
terencez 0:3f48af28ebcd 356 watcherP->parameters->step = attrP->step;
terencez 0:3f48af28ebcd 357 }
terencez 0:3f48af28ebcd 358 }
terencez 0:3f48af28ebcd 359
terencez 0:3f48af28ebcd 360 LOG_ARG("Final toSet: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f",
terencez 0:3f48af28ebcd 361 watcherP->parameters->toSet, watcherP->parameters->minPeriod, watcherP->parameters->maxPeriod, watcherP->parameters->greaterThan, watcherP->parameters->lessThan, watcherP->parameters->step);
terencez 0:3f48af28ebcd 362
terencez 0:3f48af28ebcd 363 return COAP_204_CHANGED;
terencez 0:3f48af28ebcd 364 }
terencez 0:3f48af28ebcd 365
terencez 0:3f48af28ebcd 366 lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 367 lwm2m_uri_t * uriP)
terencez 0:3f48af28ebcd 368 {
terencez 0:3f48af28ebcd 369 lwm2m_observed_t * targetP;
terencez 0:3f48af28ebcd 370
terencez 0:3f48af28ebcd 371 LOG_URI(uriP);
terencez 0:3f48af28ebcd 372 targetP = contextP->observedList;
terencez 0:3f48af28ebcd 373 while (targetP != NULL)
terencez 0:3f48af28ebcd 374 {
terencez 0:3f48af28ebcd 375 if (targetP->uri.objectId == uriP->objectId)
terencez 0:3f48af28ebcd 376 {
terencez 0:3f48af28ebcd 377 if ((!LWM2M_URI_IS_SET_INSTANCE(uriP) && !LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri)))
terencez 0:3f48af28ebcd 378 || (LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri)) && (uriP->instanceId == targetP->uri.instanceId)))
terencez 0:3f48af28ebcd 379 {
terencez 0:3f48af28ebcd 380 if ((!LWM2M_URI_IS_SET_RESOURCE(uriP) && !LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri)))
terencez 0:3f48af28ebcd 381 || (LWM2M_URI_IS_SET_RESOURCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri)) && (uriP->resourceId == targetP->uri.resourceId)))
terencez 0:3f48af28ebcd 382 {
terencez 0:3f48af28ebcd 383 LOG_ARG("Found one with%s observers.", targetP->watcherList ? "" : " no");
terencez 0:3f48af28ebcd 384 LOG_URI(&(targetP->uri));
terencez 0:3f48af28ebcd 385 return targetP;
terencez 0:3f48af28ebcd 386 }
terencez 0:3f48af28ebcd 387 }
terencez 0:3f48af28ebcd 388 }
terencez 0:3f48af28ebcd 389 targetP = targetP->next;
terencez 0:3f48af28ebcd 390 }
terencez 0:3f48af28ebcd 391
terencez 0:3f48af28ebcd 392 LOG("Found nothing");
terencez 0:3f48af28ebcd 393 return NULL;
terencez 0:3f48af28ebcd 394 }
terencez 0:3f48af28ebcd 395
terencez 0:3f48af28ebcd 396 void lwm2m_resource_value_changed(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 397 lwm2m_uri_t * uriP)
terencez 0:3f48af28ebcd 398 {
terencez 0:3f48af28ebcd 399 lwm2m_observed_t * targetP;
terencez 0:3f48af28ebcd 400
terencez 0:3f48af28ebcd 401 LOG_URI(uriP);
terencez 0:3f48af28ebcd 402 targetP = contextP->observedList;
terencez 0:3f48af28ebcd 403 while (targetP != NULL)
terencez 0:3f48af28ebcd 404 {
terencez 0:3f48af28ebcd 405 if (targetP->uri.objectId == uriP->objectId)
terencez 0:3f48af28ebcd 406 {
terencez 0:3f48af28ebcd 407 if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
terencez 0:3f48af28ebcd 408 || (targetP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0
terencez 0:3f48af28ebcd 409 || uriP->instanceId == targetP->uri.instanceId)
terencez 0:3f48af28ebcd 410 {
terencez 0:3f48af28ebcd 411 if (!LWM2M_URI_IS_SET_RESOURCE(uriP)
terencez 0:3f48af28ebcd 412 || (targetP->uri.flag & LWM2M_URI_FLAG_RESOURCE_ID) == 0
terencez 0:3f48af28ebcd 413 || uriP->resourceId == targetP->uri.resourceId)
terencez 0:3f48af28ebcd 414 {
terencez 0:3f48af28ebcd 415 lwm2m_watcher_t * watcherP;
terencez 0:3f48af28ebcd 416
terencez 0:3f48af28ebcd 417 LOG("Found an observation");
terencez 0:3f48af28ebcd 418 LOG_URI(&(targetP->uri));
terencez 0:3f48af28ebcd 419
terencez 0:3f48af28ebcd 420 for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
terencez 0:3f48af28ebcd 421 {
terencez 0:3f48af28ebcd 422 if (watcherP->active == true)
terencez 0:3f48af28ebcd 423 {
terencez 0:3f48af28ebcd 424 LOG("Tagging a watcher");
terencez 0:3f48af28ebcd 425 watcherP->update = true;
terencez 0:3f48af28ebcd 426 }
terencez 0:3f48af28ebcd 427 }
terencez 0:3f48af28ebcd 428 }
terencez 0:3f48af28ebcd 429 }
terencez 0:3f48af28ebcd 430 }
terencez 0:3f48af28ebcd 431 targetP = targetP->next;
terencez 0:3f48af28ebcd 432 }
terencez 0:3f48af28ebcd 433 }
terencez 0:3f48af28ebcd 434
terencez 0:3f48af28ebcd 435 void observe_step(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 436 time_t currentTime,
terencez 0:3f48af28ebcd 437 time_t * timeoutP)
terencez 0:3f48af28ebcd 438 {
terencez 0:3f48af28ebcd 439 lwm2m_observed_t * targetP;
terencez 0:3f48af28ebcd 440
terencez 0:3f48af28ebcd 441 LOG("Entering");
terencez 0:3f48af28ebcd 442 for (targetP = contextP->observedList ; targetP != NULL ; targetP = targetP->next)
terencez 0:3f48af28ebcd 443 {
terencez 0:3f48af28ebcd 444 lwm2m_watcher_t * watcherP;
terencez 0:3f48af28ebcd 445 uint8_t * buffer = NULL;
terencez 0:3f48af28ebcd 446 size_t length = 0;
terencez 0:3f48af28ebcd 447 lwm2m_data_t * dataP = NULL;
terencez 0:3f48af28ebcd 448 int size = 0;
terencez 0:3f48af28ebcd 449 double floatValue = 0;
terencez 0:3f48af28ebcd 450 int64_t integerValue = 0;
terencez 0:3f48af28ebcd 451 bool storeValue = false;
terencez 0:3f48af28ebcd 452 lwm2m_media_type_t format = LWM2M_CONTENT_TEXT;
terencez 0:3f48af28ebcd 453 coap_packet_t message[1];
terencez 0:3f48af28ebcd 454 time_t interval;
terencez 0:3f48af28ebcd 455
terencez 0:3f48af28ebcd 456 LOG_URI(&(targetP->uri));
terencez 0:3f48af28ebcd 457 if (LWM2M_URI_IS_SET_RESOURCE(&targetP->uri))
terencez 0:3f48af28ebcd 458 {
terencez 0:3f48af28ebcd 459 if (COAP_205_CONTENT != object_readData(contextP, &targetP->uri, &size, &dataP)) continue;
terencez 0:3f48af28ebcd 460 switch (dataP->type)
terencez 0:3f48af28ebcd 461 {
terencez 0:3f48af28ebcd 462 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 463 if (1 != lwm2m_data_decode_int(dataP, &integerValue)) continue;
terencez 0:3f48af28ebcd 464 storeValue = true;
terencez 0:3f48af28ebcd 465 break;
terencez 0:3f48af28ebcd 466 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 467 if (1 != lwm2m_data_decode_float(dataP, &floatValue)) continue;
terencez 0:3f48af28ebcd 468 storeValue = true;
terencez 0:3f48af28ebcd 469 break;
terencez 0:3f48af28ebcd 470 default:
terencez 0:3f48af28ebcd 471 break;
terencez 0:3f48af28ebcd 472 }
terencez 0:3f48af28ebcd 473 }
terencez 0:3f48af28ebcd 474 for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
terencez 0:3f48af28ebcd 475 {
terencez 0:3f48af28ebcd 476 if (watcherP->active == true)
terencez 0:3f48af28ebcd 477 {
terencez 0:3f48af28ebcd 478 bool notify = false;
terencez 0:3f48af28ebcd 479
terencez 0:3f48af28ebcd 480 if (watcherP->update == true)
terencez 0:3f48af28ebcd 481 {
terencez 0:3f48af28ebcd 482 // value changed, should we notify the server ?
terencez 0:3f48af28ebcd 483
terencez 0:3f48af28ebcd 484 if (watcherP->parameters == NULL || watcherP->parameters->toSet == 0)
terencez 0:3f48af28ebcd 485 {
terencez 0:3f48af28ebcd 486 // no conditions
terencez 0:3f48af28ebcd 487 notify = true;
terencez 0:3f48af28ebcd 488 LOG("Notify with no conditions");
terencez 0:3f48af28ebcd 489 LOG_URI(&(targetP->uri));
terencez 0:3f48af28ebcd 490 }
terencez 0:3f48af28ebcd 491
terencez 0:3f48af28ebcd 492 if (notify == false
terencez 0:3f48af28ebcd 493 && watcherP->parameters != NULL
terencez 0:3f48af28ebcd 494 && (watcherP->parameters->toSet & ATTR_FLAG_NUMERIC) != 0)
terencez 0:3f48af28ebcd 495 {
terencez 0:3f48af28ebcd 496 if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_LESS_THAN) != 0)
terencez 0:3f48af28ebcd 497 {
terencez 0:3f48af28ebcd 498 LOG("Checking lower treshold");
terencez 0:3f48af28ebcd 499 // Did we cross the lower treshold ?
terencez 0:3f48af28ebcd 500 switch (dataP->type)
terencez 0:3f48af28ebcd 501 {
terencez 0:3f48af28ebcd 502 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 503 if ((integerValue <= watcherP->parameters->lessThan
terencez 0:3f48af28ebcd 504 && watcherP->lastValue.asInteger > watcherP->parameters->lessThan)
terencez 0:3f48af28ebcd 505 || (integerValue >= watcherP->parameters->lessThan
terencez 0:3f48af28ebcd 506 && watcherP->lastValue.asInteger < watcherP->parameters->lessThan))
terencez 0:3f48af28ebcd 507 {
terencez 0:3f48af28ebcd 508 LOG("Notify on lower treshold crossing");
terencez 0:3f48af28ebcd 509 notify = true;
terencez 0:3f48af28ebcd 510 }
terencez 0:3f48af28ebcd 511 break;
terencez 0:3f48af28ebcd 512 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 513 if ((floatValue <= watcherP->parameters->lessThan
terencez 0:3f48af28ebcd 514 && watcherP->lastValue.asFloat > watcherP->parameters->lessThan)
terencez 0:3f48af28ebcd 515 || (floatValue >= watcherP->parameters->lessThan
terencez 0:3f48af28ebcd 516 && watcherP->lastValue.asFloat < watcherP->parameters->lessThan))
terencez 0:3f48af28ebcd 517 {
terencez 0:3f48af28ebcd 518 LOG("Notify on lower treshold crossing");
terencez 0:3f48af28ebcd 519 notify = true;
terencez 0:3f48af28ebcd 520 }
terencez 0:3f48af28ebcd 521 break;
terencez 0:3f48af28ebcd 522 default:
terencez 0:3f48af28ebcd 523 break;
terencez 0:3f48af28ebcd 524 }
terencez 0:3f48af28ebcd 525 }
terencez 0:3f48af28ebcd 526 if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) != 0)
terencez 0:3f48af28ebcd 527 {
terencez 0:3f48af28ebcd 528 LOG("Checking upper treshold");
terencez 0:3f48af28ebcd 529 // Did we cross the upper treshold ?
terencez 0:3f48af28ebcd 530 switch (dataP->type)
terencez 0:3f48af28ebcd 531 {
terencez 0:3f48af28ebcd 532 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 533 if ((integerValue <= watcherP->parameters->greaterThan
terencez 0:3f48af28ebcd 534 && watcherP->lastValue.asInteger > watcherP->parameters->greaterThan)
terencez 0:3f48af28ebcd 535 || (integerValue >= watcherP->parameters->greaterThan
terencez 0:3f48af28ebcd 536 && watcherP->lastValue.asInteger < watcherP->parameters->greaterThan))
terencez 0:3f48af28ebcd 537 {
terencez 0:3f48af28ebcd 538 LOG("Notify on lower upper crossing");
terencez 0:3f48af28ebcd 539 notify = true;
terencez 0:3f48af28ebcd 540 }
terencez 0:3f48af28ebcd 541 break;
terencez 0:3f48af28ebcd 542 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 543 if ((floatValue <= watcherP->parameters->greaterThan
terencez 0:3f48af28ebcd 544 && watcherP->lastValue.asFloat > watcherP->parameters->greaterThan)
terencez 0:3f48af28ebcd 545 || (floatValue >= watcherP->parameters->greaterThan
terencez 0:3f48af28ebcd 546 && watcherP->lastValue.asFloat < watcherP->parameters->greaterThan))
terencez 0:3f48af28ebcd 547 {
terencez 0:3f48af28ebcd 548 LOG("Notify on lower upper crossing");
terencez 0:3f48af28ebcd 549 notify = true;
terencez 0:3f48af28ebcd 550 }
terencez 0:3f48af28ebcd 551 break;
terencez 0:3f48af28ebcd 552 default:
terencez 0:3f48af28ebcd 553 break;
terencez 0:3f48af28ebcd 554 }
terencez 0:3f48af28ebcd 555 }
terencez 0:3f48af28ebcd 556 if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_STEP) != 0)
terencez 0:3f48af28ebcd 557 {
terencez 0:3f48af28ebcd 558 LOG("Checking step");
terencez 0:3f48af28ebcd 559
terencez 0:3f48af28ebcd 560 switch (dataP->type)
terencez 0:3f48af28ebcd 561 {
terencez 0:3f48af28ebcd 562 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 563 {
terencez 0:3f48af28ebcd 564 int64_t diff;
terencez 0:3f48af28ebcd 565
terencez 0:3f48af28ebcd 566 diff = integerValue - watcherP->lastValue.asInteger;
terencez 0:3f48af28ebcd 567 if ((diff < 0 && (0 - diff) >= watcherP->parameters->step)
terencez 0:3f48af28ebcd 568 || (diff >= 0 && diff >= watcherP->parameters->step))
terencez 0:3f48af28ebcd 569 {
terencez 0:3f48af28ebcd 570 LOG("Notify on step condition");
terencez 0:3f48af28ebcd 571 notify = true;
terencez 0:3f48af28ebcd 572 }
terencez 0:3f48af28ebcd 573 }
terencez 0:3f48af28ebcd 574 break;
terencez 0:3f48af28ebcd 575 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 576 {
terencez 0:3f48af28ebcd 577 double diff;
terencez 0:3f48af28ebcd 578
terencez 0:3f48af28ebcd 579 diff = floatValue - watcherP->lastValue.asFloat;
terencez 0:3f48af28ebcd 580 if ((diff < 0 && (0 - diff) >= watcherP->parameters->step)
terencez 0:3f48af28ebcd 581 || (diff >= 0 && diff >= watcherP->parameters->step))
terencez 0:3f48af28ebcd 582 {
terencez 0:3f48af28ebcd 583 LOG("Notify on step condition");
terencez 0:3f48af28ebcd 584 notify = true;
terencez 0:3f48af28ebcd 585 }
terencez 0:3f48af28ebcd 586 }
terencez 0:3f48af28ebcd 587 break;
terencez 0:3f48af28ebcd 588 default:
terencez 0:3f48af28ebcd 589 break;
terencez 0:3f48af28ebcd 590 }
terencez 0:3f48af28ebcd 591 }
terencez 0:3f48af28ebcd 592 }
terencez 0:3f48af28ebcd 593
terencez 0:3f48af28ebcd 594 if (watcherP->parameters != NULL
terencez 0:3f48af28ebcd 595 && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) != 0)
terencez 0:3f48af28ebcd 596 {
terencez 0:3f48af28ebcd 597 LOG_ARG("Checking minimal period (%d s)", watcherP->parameters->minPeriod);
terencez 0:3f48af28ebcd 598
terencez 0:3f48af28ebcd 599 if (watcherP->lastTime + watcherP->parameters->minPeriod > currentTime)
terencez 0:3f48af28ebcd 600 {
terencez 0:3f48af28ebcd 601 // Minimum Period did not elapse yet
terencez 0:3f48af28ebcd 602 interval = watcherP->lastTime + watcherP->parameters->minPeriod - currentTime;
terencez 0:3f48af28ebcd 603 if (*timeoutP > interval) *timeoutP = interval;
terencez 0:3f48af28ebcd 604 notify = false;
terencez 0:3f48af28ebcd 605 }
terencez 0:3f48af28ebcd 606 else
terencez 0:3f48af28ebcd 607 {
terencez 0:3f48af28ebcd 608 LOG("Notify on minimal period");
terencez 0:3f48af28ebcd 609 notify = true;
terencez 0:3f48af28ebcd 610 }
terencez 0:3f48af28ebcd 611 }
terencez 0:3f48af28ebcd 612 }
terencez 0:3f48af28ebcd 613
terencez 0:3f48af28ebcd 614 // Is the Maximum Period reached ?
terencez 0:3f48af28ebcd 615 if (notify == false
terencez 0:3f48af28ebcd 616 && watcherP->parameters != NULL
terencez 0:3f48af28ebcd 617 && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0)
terencez 0:3f48af28ebcd 618 {
terencez 0:3f48af28ebcd 619 LOG_ARG("Checking maximal period (%d s)", watcherP->parameters->minPeriod);
terencez 0:3f48af28ebcd 620
terencez 0:3f48af28ebcd 621 if (watcherP->lastTime + watcherP->parameters->maxPeriod <= currentTime)
terencez 0:3f48af28ebcd 622 {
terencez 0:3f48af28ebcd 623 LOG("Notify on maximal period");
terencez 0:3f48af28ebcd 624 notify = true;
terencez 0:3f48af28ebcd 625 }
terencez 0:3f48af28ebcd 626 }
terencez 0:3f48af28ebcd 627
terencez 0:3f48af28ebcd 628 if (notify == true)
terencez 0:3f48af28ebcd 629 {
terencez 0:3f48af28ebcd 630 if (buffer == NULL)
terencez 0:3f48af28ebcd 631 {
terencez 0:3f48af28ebcd 632 if (dataP != NULL)
terencez 0:3f48af28ebcd 633 {
terencez 0:3f48af28ebcd 634 int res;
terencez 0:3f48af28ebcd 635
terencez 0:3f48af28ebcd 636 res = lwm2m_data_serialize(&targetP->uri, size, dataP, &format, &buffer);
terencez 0:3f48af28ebcd 637 if (res < 0)
terencez 0:3f48af28ebcd 638 {
terencez 0:3f48af28ebcd 639 break;
terencez 0:3f48af28ebcd 640 }
terencez 0:3f48af28ebcd 641 else
terencez 0:3f48af28ebcd 642 {
terencez 0:3f48af28ebcd 643 length = (size_t)res;
terencez 0:3f48af28ebcd 644 }
terencez 0:3f48af28ebcd 645
terencez 0:3f48af28ebcd 646 }
terencez 0:3f48af28ebcd 647 else
terencez 0:3f48af28ebcd 648 {
terencez 0:3f48af28ebcd 649 if (COAP_205_CONTENT != object_read(contextP, &targetP->uri, &format, &buffer, &length))
terencez 0:3f48af28ebcd 650 {
terencez 0:3f48af28ebcd 651 buffer = NULL;
terencez 0:3f48af28ebcd 652 break;
terencez 0:3f48af28ebcd 653 }
terencez 0:3f48af28ebcd 654 }
terencez 0:3f48af28ebcd 655 coap_init_message(message, COAP_TYPE_NON, COAP_205_CONTENT, 0);
terencez 0:3f48af28ebcd 656 coap_set_header_content_type(message, format);
terencez 0:3f48af28ebcd 657 coap_set_payload(message, buffer, length);
terencez 0:3f48af28ebcd 658 }
terencez 0:3f48af28ebcd 659 watcherP->lastTime = currentTime;
terencez 0:3f48af28ebcd 660 watcherP->lastMid = contextP->nextMID++;
terencez 0:3f48af28ebcd 661 message->mid = watcherP->lastMid;
terencez 0:3f48af28ebcd 662 coap_set_header_token(message, watcherP->token, watcherP->tokenLen);
terencez 0:3f48af28ebcd 663 coap_set_header_observe(message, watcherP->counter++);
terencez 0:3f48af28ebcd 664 (void)message_send(contextP, message, watcherP->server->sessionH);
terencez 0:3f48af28ebcd 665 watcherP->update = false;
terencez 0:3f48af28ebcd 666 }
terencez 0:3f48af28ebcd 667
terencez 0:3f48af28ebcd 668 // Store this value
terencez 0:3f48af28ebcd 669 if (notify == true && storeValue == true)
terencez 0:3f48af28ebcd 670 {
terencez 0:3f48af28ebcd 671 switch (dataP->type)
terencez 0:3f48af28ebcd 672 {
terencez 0:3f48af28ebcd 673 case LWM2M_TYPE_INTEGER:
terencez 0:3f48af28ebcd 674 watcherP->lastValue.asInteger = integerValue;
terencez 0:3f48af28ebcd 675 break;
terencez 0:3f48af28ebcd 676 case LWM2M_TYPE_FLOAT:
terencez 0:3f48af28ebcd 677 watcherP->lastValue.asFloat = floatValue;
terencez 0:3f48af28ebcd 678 break;
terencez 0:3f48af28ebcd 679 default:
terencez 0:3f48af28ebcd 680 break;
terencez 0:3f48af28ebcd 681 }
terencez 0:3f48af28ebcd 682 }
terencez 0:3f48af28ebcd 683
terencez 0:3f48af28ebcd 684 if (watcherP->parameters != NULL && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0)
terencez 0:3f48af28ebcd 685 {
terencez 0:3f48af28ebcd 686 // update timers
terencez 0:3f48af28ebcd 687 interval = watcherP->lastTime + watcherP->parameters->maxPeriod - currentTime;
terencez 0:3f48af28ebcd 688 if (*timeoutP > interval) *timeoutP = interval;
terencez 0:3f48af28ebcd 689 }
terencez 0:3f48af28ebcd 690 }
terencez 0:3f48af28ebcd 691 }
terencez 0:3f48af28ebcd 692 if (dataP != NULL) lwm2m_data_free(size, dataP);
terencez 0:3f48af28ebcd 693 if (buffer != NULL) lwm2m_free(buffer);
terencez 0:3f48af28ebcd 694 }
terencez 0:3f48af28ebcd 695 }
terencez 0:3f48af28ebcd 696
terencez 0:3f48af28ebcd 697 #endif
terencez 0:3f48af28ebcd 698
terencez 0:3f48af28ebcd 699 #ifdef LWM2M_SERVER_MODE
terencez 0:3f48af28ebcd 700
terencez 0:3f48af28ebcd 701 typedef struct
terencez 0:3f48af28ebcd 702 {
terencez 0:3f48af28ebcd 703 lwm2m_observation_t * observationP;
terencez 0:3f48af28ebcd 704 lwm2m_result_callback_t callbackP;
terencez 0:3f48af28ebcd 705 void * userDataP;
terencez 0:3f48af28ebcd 706 } cancellation_data_t;
terencez 0:3f48af28ebcd 707
terencez 0:3f48af28ebcd 708 static lwm2m_observation_t * prv_findObservationByURI(lwm2m_client_t * clientP,
terencez 0:3f48af28ebcd 709 lwm2m_uri_t * uriP)
terencez 0:3f48af28ebcd 710 {
terencez 0:3f48af28ebcd 711 lwm2m_observation_t * targetP;
terencez 0:3f48af28ebcd 712
terencez 0:3f48af28ebcd 713 targetP = clientP->observationList;
terencez 0:3f48af28ebcd 714 while (targetP != NULL)
terencez 0:3f48af28ebcd 715 {
terencez 0:3f48af28ebcd 716 if (targetP->uri.objectId == uriP->objectId
terencez 0:3f48af28ebcd 717 && targetP->uri.flag == uriP->flag
terencez 0:3f48af28ebcd 718 && targetP->uri.instanceId == uriP->instanceId
terencez 0:3f48af28ebcd 719 && targetP->uri.resourceId == uriP->resourceId)
terencez 0:3f48af28ebcd 720 {
terencez 0:3f48af28ebcd 721 return targetP;
terencez 0:3f48af28ebcd 722 }
terencez 0:3f48af28ebcd 723
terencez 0:3f48af28ebcd 724 targetP = targetP->next;
terencez 0:3f48af28ebcd 725 }
terencez 0:3f48af28ebcd 726
terencez 0:3f48af28ebcd 727 return targetP;
terencez 0:3f48af28ebcd 728 }
terencez 0:3f48af28ebcd 729
terencez 0:3f48af28ebcd 730 void observe_remove(lwm2m_observation_t * observationP)
terencez 0:3f48af28ebcd 731 {
terencez 0:3f48af28ebcd 732 LOG("Entering");
terencez 0:3f48af28ebcd 733 observationP->clientP->observationList = (lwm2m_observation_t *) LWM2M_LIST_RM(observationP->clientP->observationList, observationP->id, NULL);
terencez 0:3f48af28ebcd 734 lwm2m_free(observationP);
terencez 0:3f48af28ebcd 735 }
terencez 0:3f48af28ebcd 736
terencez 0:3f48af28ebcd 737 static void prv_obsRequestCallback(lwm2m_transaction_t * transacP,
terencez 0:3f48af28ebcd 738 void * message)
terencez 0:3f48af28ebcd 739 {
terencez 0:3f48af28ebcd 740 lwm2m_observation_t * observationP = (lwm2m_observation_t *)transacP->userData;
terencez 0:3f48af28ebcd 741 coap_packet_t * packet = (coap_packet_t *)message;
terencez 0:3f48af28ebcd 742 uint8_t code;
terencez 0:3f48af28ebcd 743
terencez 0:3f48af28ebcd 744 switch (observationP->status)
terencez 0:3f48af28ebcd 745 {
terencez 0:3f48af28ebcd 746 case STATE_DEREG_PENDING:
terencez 0:3f48af28ebcd 747 // Observation was canceled by the user.
terencez 0:3f48af28ebcd 748 observe_remove(observationP);
terencez 0:3f48af28ebcd 749 return;
terencez 0:3f48af28ebcd 750
terencez 0:3f48af28ebcd 751 case STATE_REG_PENDING:
terencez 0:3f48af28ebcd 752 observationP->status = STATE_REGISTERED;
terencez 0:3f48af28ebcd 753 break;
terencez 0:3f48af28ebcd 754
terencez 0:3f48af28ebcd 755 default:
terencez 0:3f48af28ebcd 756 break;
terencez 0:3f48af28ebcd 757 }
terencez 0:3f48af28ebcd 758
terencez 0:3f48af28ebcd 759 if (message == NULL)
terencez 0:3f48af28ebcd 760 {
terencez 0:3f48af28ebcd 761 code = COAP_503_SERVICE_UNAVAILABLE;
terencez 0:3f48af28ebcd 762 }
terencez 0:3f48af28ebcd 763 else if (packet->code == COAP_205_CONTENT
terencez 0:3f48af28ebcd 764 && !IS_OPTION(packet, COAP_OPTION_OBSERVE))
terencez 0:3f48af28ebcd 765 {
terencez 0:3f48af28ebcd 766 code = COAP_405_METHOD_NOT_ALLOWED;
terencez 0:3f48af28ebcd 767 }
terencez 0:3f48af28ebcd 768 else
terencez 0:3f48af28ebcd 769 {
terencez 0:3f48af28ebcd 770 code = packet->code;
terencez 0:3f48af28ebcd 771 }
terencez 0:3f48af28ebcd 772
terencez 0:3f48af28ebcd 773 if (code != COAP_205_CONTENT)
terencez 0:3f48af28ebcd 774 {
terencez 0:3f48af28ebcd 775 observationP->callback(observationP->clientP->internalID,
terencez 0:3f48af28ebcd 776 &observationP->uri,
terencez 0:3f48af28ebcd 777 code,
terencez 0:3f48af28ebcd 778 LWM2M_CONTENT_TEXT, NULL, 0,
terencez 0:3f48af28ebcd 779 observationP->userData);
terencez 0:3f48af28ebcd 780 observe_remove(observationP);
terencez 0:3f48af28ebcd 781 }
terencez 0:3f48af28ebcd 782 else
terencez 0:3f48af28ebcd 783 {
terencez 0:3f48af28ebcd 784 observationP->callback(observationP->clientP->internalID,
terencez 0:3f48af28ebcd 785 &observationP->uri,
terencez 0:3f48af28ebcd 786 0,
terencez 0:3f48af28ebcd 787 packet->content_type, packet->payload, packet->payload_len,
terencez 0:3f48af28ebcd 788 observationP->userData);
terencez 0:3f48af28ebcd 789 }
terencez 0:3f48af28ebcd 790 }
terencez 0:3f48af28ebcd 791
terencez 0:3f48af28ebcd 792
terencez 0:3f48af28ebcd 793 static void prv_obsCancelRequestCallback(lwm2m_transaction_t * transacP,
terencez 0:3f48af28ebcd 794 void * message)
terencez 0:3f48af28ebcd 795 {
terencez 0:3f48af28ebcd 796 cancellation_data_t * cancelP = (cancellation_data_t *)transacP->userData;
terencez 0:3f48af28ebcd 797 coap_packet_t * packet = (coap_packet_t *)message;
terencez 0:3f48af28ebcd 798 uint8_t code;
terencez 0:3f48af28ebcd 799
terencez 0:3f48af28ebcd 800 if (message == NULL)
terencez 0:3f48af28ebcd 801 {
terencez 0:3f48af28ebcd 802 code = COAP_503_SERVICE_UNAVAILABLE;
terencez 0:3f48af28ebcd 803 }
terencez 0:3f48af28ebcd 804 else
terencez 0:3f48af28ebcd 805 {
terencez 0:3f48af28ebcd 806 code = packet->code;
terencez 0:3f48af28ebcd 807 }
terencez 0:3f48af28ebcd 808
terencez 0:3f48af28ebcd 809 if (code != COAP_205_CONTENT)
terencez 0:3f48af28ebcd 810 {
terencez 0:3f48af28ebcd 811 cancelP->callbackP(cancelP->observationP->clientP->internalID,
terencez 0:3f48af28ebcd 812 &cancelP->observationP->uri,
terencez 0:3f48af28ebcd 813 code,
terencez 0:3f48af28ebcd 814 LWM2M_CONTENT_TEXT, NULL, 0,
terencez 0:3f48af28ebcd 815 cancelP->userDataP);
terencez 0:3f48af28ebcd 816 }
terencez 0:3f48af28ebcd 817 else
terencez 0:3f48af28ebcd 818 {
terencez 0:3f48af28ebcd 819 cancelP->callbackP(cancelP->observationP->clientP->internalID,
terencez 0:3f48af28ebcd 820 &cancelP->observationP->uri,
terencez 0:3f48af28ebcd 821 0,
terencez 0:3f48af28ebcd 822 packet->content_type, packet->payload, packet->payload_len,
terencez 0:3f48af28ebcd 823 cancelP->userDataP);
terencez 0:3f48af28ebcd 824 }
terencez 0:3f48af28ebcd 825
terencez 0:3f48af28ebcd 826 observe_remove(cancelP->observationP);
terencez 0:3f48af28ebcd 827
terencez 0:3f48af28ebcd 828 lwm2m_free(cancelP);
terencez 0:3f48af28ebcd 829 }
terencez 0:3f48af28ebcd 830
terencez 0:3f48af28ebcd 831
terencez 0:3f48af28ebcd 832 int lwm2m_observe(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 833 uint16_t clientID,
terencez 0:3f48af28ebcd 834 lwm2m_uri_t * uriP,
terencez 0:3f48af28ebcd 835 lwm2m_result_callback_t callback,
terencez 0:3f48af28ebcd 836 void * userData)
terencez 0:3f48af28ebcd 837 {
terencez 0:3f48af28ebcd 838 lwm2m_client_t * clientP;
terencez 0:3f48af28ebcd 839 lwm2m_transaction_t * transactionP;
terencez 0:3f48af28ebcd 840 lwm2m_observation_t * observationP;
terencez 0:3f48af28ebcd 841 uint8_t token[4];
terencez 0:3f48af28ebcd 842
terencez 0:3f48af28ebcd 843 LOG_ARG("clientID: %d", clientID);
terencez 0:3f48af28ebcd 844 LOG_URI(uriP);
terencez 0:3f48af28ebcd 845
terencez 0:3f48af28ebcd 846 if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
terencez 0:3f48af28ebcd 847
terencez 0:3f48af28ebcd 848 clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
terencez 0:3f48af28ebcd 849 if (clientP == NULL) return COAP_404_NOT_FOUND;
terencez 0:3f48af28ebcd 850
terencez 0:3f48af28ebcd 851 observationP = (lwm2m_observation_t *)lwm2m_malloc(sizeof(lwm2m_observation_t));
terencez 0:3f48af28ebcd 852 if (observationP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 853 memset(observationP, 0, sizeof(lwm2m_observation_t));
terencez 0:3f48af28ebcd 854
terencez 0:3f48af28ebcd 855 observationP->id = lwm2m_list_newId((lwm2m_list_t *)clientP->observationList);
terencez 0:3f48af28ebcd 856 memcpy(&observationP->uri, uriP, sizeof(lwm2m_uri_t));
terencez 0:3f48af28ebcd 857 observationP->clientP = clientP;
terencez 0:3f48af28ebcd 858 observationP->status = STATE_REG_PENDING;
terencez 0:3f48af28ebcd 859 observationP->callback = callback;
terencez 0:3f48af28ebcd 860 observationP->userData = userData;
terencez 0:3f48af28ebcd 861
terencez 0:3f48af28ebcd 862 token[0] = clientP->internalID >> 8;
terencez 0:3f48af28ebcd 863 token[1] = clientP->internalID & 0xFF;
terencez 0:3f48af28ebcd 864 token[2] = observationP->id >> 8;
terencez 0:3f48af28ebcd 865 token[3] = observationP->id & 0xFF;
terencez 0:3f48af28ebcd 866
terencez 0:3f48af28ebcd 867 transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 4, token);
terencez 0:3f48af28ebcd 868 if (transactionP == NULL)
terencez 0:3f48af28ebcd 869 {
terencez 0:3f48af28ebcd 870 lwm2m_free(observationP);
terencez 0:3f48af28ebcd 871 return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 872 }
terencez 0:3f48af28ebcd 873
terencez 0:3f48af28ebcd 874 observationP->clientP->observationList = (lwm2m_observation_t *)LWM2M_LIST_ADD(observationP->clientP->observationList, observationP);
terencez 0:3f48af28ebcd 875
terencez 0:3f48af28ebcd 876 coap_set_header_observe(transactionP->message, 0);
terencez 0:3f48af28ebcd 877 coap_set_header_token(transactionP->message, token, sizeof(token));
terencez 0:3f48af28ebcd 878
terencez 0:3f48af28ebcd 879 transactionP->callback = prv_obsRequestCallback;
terencez 0:3f48af28ebcd 880 transactionP->userData = (void *)observationP;
terencez 0:3f48af28ebcd 881
terencez 0:3f48af28ebcd 882 contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);
terencez 0:3f48af28ebcd 883
terencez 0:3f48af28ebcd 884 return transaction_send(contextP, transactionP);
terencez 0:3f48af28ebcd 885 }
terencez 0:3f48af28ebcd 886
terencez 0:3f48af28ebcd 887 int lwm2m_observe_cancel(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 888 uint16_t clientID,
terencez 0:3f48af28ebcd 889 lwm2m_uri_t * uriP,
terencez 0:3f48af28ebcd 890 lwm2m_result_callback_t callback,
terencez 0:3f48af28ebcd 891 void * userData)
terencez 0:3f48af28ebcd 892 {
terencez 0:3f48af28ebcd 893 lwm2m_client_t * clientP;
terencez 0:3f48af28ebcd 894 lwm2m_observation_t * observationP;
terencez 0:3f48af28ebcd 895
terencez 0:3f48af28ebcd 896 LOG_ARG("clientID: %d", clientID);
terencez 0:3f48af28ebcd 897 LOG_URI(uriP);
terencez 0:3f48af28ebcd 898
terencez 0:3f48af28ebcd 899 clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
terencez 0:3f48af28ebcd 900 if (clientP == NULL) return COAP_404_NOT_FOUND;
terencez 0:3f48af28ebcd 901
terencez 0:3f48af28ebcd 902 observationP = prv_findObservationByURI(clientP, uriP);
terencez 0:3f48af28ebcd 903 if (observationP == NULL) return COAP_404_NOT_FOUND;
terencez 0:3f48af28ebcd 904
terencez 0:3f48af28ebcd 905 switch (observationP->status)
terencez 0:3f48af28ebcd 906 {
terencez 0:3f48af28ebcd 907 case STATE_REGISTERED:
terencez 0:3f48af28ebcd 908 {
terencez 0:3f48af28ebcd 909 lwm2m_transaction_t * transactionP;
terencez 0:3f48af28ebcd 910 cancellation_data_t * cancelP;
terencez 0:3f48af28ebcd 911
terencez 0:3f48af28ebcd 912 transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 0, NULL);
terencez 0:3f48af28ebcd 913 if (transactionP == NULL)
terencez 0:3f48af28ebcd 914 {
terencez 0:3f48af28ebcd 915 return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 916 }
terencez 0:3f48af28ebcd 917 cancelP = (cancellation_data_t *)lwm2m_malloc(sizeof(cancellation_data_t));
terencez 0:3f48af28ebcd 918 if (cancelP == NULL)
terencez 0:3f48af28ebcd 919 {
terencez 0:3f48af28ebcd 920 lwm2m_free(transactionP);
terencez 0:3f48af28ebcd 921 return COAP_500_INTERNAL_SERVER_ERROR;
terencez 0:3f48af28ebcd 922 }
terencez 0:3f48af28ebcd 923
terencez 0:3f48af28ebcd 924 coap_set_header_observe(transactionP->message, 1);
terencez 0:3f48af28ebcd 925
terencez 0:3f48af28ebcd 926 cancelP->observationP = observationP;
terencez 0:3f48af28ebcd 927 cancelP->callbackP = callback;
terencez 0:3f48af28ebcd 928 cancelP->userDataP = userData;
terencez 0:3f48af28ebcd 929
terencez 0:3f48af28ebcd 930 transactionP->callback = prv_obsCancelRequestCallback;
terencez 0:3f48af28ebcd 931 transactionP->userData = (void *)cancelP;
terencez 0:3f48af28ebcd 932
terencez 0:3f48af28ebcd 933 contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);
terencez 0:3f48af28ebcd 934
terencez 0:3f48af28ebcd 935 return transaction_send(contextP, transactionP);
terencez 0:3f48af28ebcd 936 }
terencez 0:3f48af28ebcd 937
terencez 0:3f48af28ebcd 938 case STATE_REG_PENDING:
terencez 0:3f48af28ebcd 939 observationP->status = STATE_DEREG_PENDING;
terencez 0:3f48af28ebcd 940 break;
terencez 0:3f48af28ebcd 941
terencez 0:3f48af28ebcd 942 default:
terencez 0:3f48af28ebcd 943 // Should not happen
terencez 0:3f48af28ebcd 944 break;
terencez 0:3f48af28ebcd 945 }
terencez 0:3f48af28ebcd 946
terencez 0:3f48af28ebcd 947 return COAP_NO_ERROR;
terencez 0:3f48af28ebcd 948 }
terencez 0:3f48af28ebcd 949
terencez 0:3f48af28ebcd 950 bool observe_handleNotify(lwm2m_context_t * contextP,
terencez 0:3f48af28ebcd 951 void * fromSessionH,
terencez 0:3f48af28ebcd 952 coap_packet_t * message,
terencez 0:3f48af28ebcd 953 coap_packet_t * response)
terencez 0:3f48af28ebcd 954 {
terencez 0:3f48af28ebcd 955 uint8_t * tokenP;
terencez 0:3f48af28ebcd 956 int token_len;
terencez 0:3f48af28ebcd 957 uint16_t clientID;
terencez 0:3f48af28ebcd 958 uint16_t obsID;
terencez 0:3f48af28ebcd 959 lwm2m_client_t * clientP;
terencez 0:3f48af28ebcd 960 lwm2m_observation_t * observationP;
terencez 0:3f48af28ebcd 961 uint32_t count;
terencez 0:3f48af28ebcd 962
terencez 0:3f48af28ebcd 963 LOG("Entering");
terencez 0:3f48af28ebcd 964 token_len = coap_get_header_token(message, (const uint8_t **)&tokenP);
terencez 0:3f48af28ebcd 965 if (token_len != sizeof(uint32_t)) return false;
terencez 0:3f48af28ebcd 966
terencez 0:3f48af28ebcd 967 if (1 != coap_get_header_observe(message, &count)) return false;
terencez 0:3f48af28ebcd 968
terencez 0:3f48af28ebcd 969 clientID = (tokenP[0] << 8) | tokenP[1];
terencez 0:3f48af28ebcd 970 obsID = (tokenP[2] << 8) | tokenP[3];
terencez 0:3f48af28ebcd 971
terencez 0:3f48af28ebcd 972 clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
terencez 0:3f48af28ebcd 973 if (clientP == NULL) return false;
terencez 0:3f48af28ebcd 974
terencez 0:3f48af28ebcd 975 observationP = (lwm2m_observation_t *)lwm2m_list_find((lwm2m_list_t *)clientP->observationList, obsID);
terencez 0:3f48af28ebcd 976 if (observationP == NULL)
terencez 0:3f48af28ebcd 977 {
terencez 0:3f48af28ebcd 978 coap_init_message(response, COAP_TYPE_RST, 0, message->mid);
terencez 0:3f48af28ebcd 979 message_send(contextP, response, fromSessionH);
terencez 0:3f48af28ebcd 980 }
terencez 0:3f48af28ebcd 981 else
terencez 0:3f48af28ebcd 982 {
terencez 0:3f48af28ebcd 983 if (message->type == COAP_TYPE_CON ) {
terencez 0:3f48af28ebcd 984 coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
terencez 0:3f48af28ebcd 985 message_send(contextP, response, fromSessionH);
terencez 0:3f48af28ebcd 986 }
terencez 0:3f48af28ebcd 987 observationP->callback(clientID,
terencez 0:3f48af28ebcd 988 &observationP->uri,
terencez 0:3f48af28ebcd 989 (int)count,
terencez 0:3f48af28ebcd 990 message->content_type, message->payload, message->payload_len,
terencez 0:3f48af28ebcd 991 observationP->userData);
terencez 0:3f48af28ebcd 992 }
terencez 0:3f48af28ebcd 993 return true;
terencez 0:3f48af28ebcd 994 }
terencez 0:3f48af28ebcd 995 #endif