A modelling and serializer library for Microsoft Azure IoTHub client applications
Dependents: sht15_remote_monitoring f767zi_mqtt remote_monitoring simplesample_amqp ... more
This library implements a serializer library to be used in projects involving Microsoft Azure IoT Hub connectivity. The code is replicated from https://github.com/Azure/azure-iot-sdks
Diff: serializer_devicetwin.h
- Revision:
- 17:fa1bba4c6053
- Child:
- 18:58b667752399
diff -r ef107f3f230c -r fa1bba4c6053 serializer_devicetwin.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serializer_devicetwin.h Wed Nov 16 21:38:26 2016 -0800 @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +#ifndef SERIALIZER_DEVICE_TWIN_H +#define SERIALIZER_DEVICE_TWIN_H + +#include "serializer.h" + +#include "iothub_client.h" +#include "iothub_client_ll.h" +#include "parson.h" +#include "vector.h" + +static void serializer_ingest(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) +{ + /*by convention, userContextCallback is a pointer to a model instance created with CodeFirst_CreateDevice*/ + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_001: [ serializer_ingest shall clone the payload into a null terminated string. ]*/ + char* copyOfPayload = (char*)malloc(size + 1); + if (copyOfPayload == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("unable to malloc\n"); + } + else + { + memcpy(copyOfPayload, payLoad, size); + copyOfPayload[size] = '\0'; + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_002: [ serializer_ingest shall parse the null terminated string into parson data types. ]*/ + JSON_Value* allJSON = json_parse_string(copyOfPayload); + if (allJSON == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure in json_parse_string"); + } + else + { + JSON_Object *allObject = json_value_get_object(allJSON); + if (allObject == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure in json_value_get_object"); + } + else + { + switch (update_state) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_003: [ If update_state is DEVICE_TWIN_UPDATE_COMPLETE then serializer_ingest shall locate "desired" json name. ]*/ + case DEVICE_TWIN_UPDATE_COMPLETE: + { + JSON_Object* desired = json_object_get_object(allObject, "desired"); + if (desired == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure in json_object_get_object"); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_004: [ If "desired" contains "$version" then serializer_ingest shall remove it. ]*/ + (void)json_object_remove(desired, "$version"); //it might not exist + JSON_Value* desiredAfterRemove = json_object_get_value(allObject, "desired"); + if (desiredAfterRemove != NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_005: [ The "desired" value shall be outputed to a null terminated string and serializer_ingest shall call CodeFirst_IngestDesiredProperties. ]*/ + char* pretty = json_serialize_to_string(desiredAfterRemove); + if (pretty == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure in json_serialize_to_string\n"); + } + else + { + if (CodeFirst_IngestDesiredProperties(userContextCallback, pretty) != CODEFIRST_OK) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure ingesting desired properties\n"); + } + else + { + /*all is fine*/ + } + free(pretty); + } + } + } + break; + } + case DEVICE_TWIN_UPDATE_PARTIAL: + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_006: [ If update_state is DEVICE_TWIN_UPDATE_PARTIAL then serializer_ingest shall remove "$version" (if it exists). ]*/ + (void)json_object_remove(allObject, "$version"); + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_007: [ The JSON shall be outputed to a null terminated string and serializer_ingest shall call CodeFirst_IngestDesiredProperties. ]*/ + char* pretty = json_serialize_to_string(allJSON); + if (pretty == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure in json_serialize_to_string\n"); + } + else + { + if (CodeFirst_IngestDesiredProperties(userContextCallback, pretty) != CODEFIRST_OK) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_008: [ If any of the above operations fail, then serializer_ingest shall return. ]*/ + LogError("failure ingesting desired properties\n"); + } + else + { + /*all is fine*/ + } + free(pretty); + } + break; + } + default: + { + LogError("INTERNAL ERROR: unexpected value for update_state=%d\n", (int)update_state); + } + } + } + json_value_free(allJSON); + } + free(copyOfPayload); + } +} + +/*an enum that sets the type of the handle used to record IoTHubDeviceTwin_Create was called*/ +#define IOTHUB_CLIENT_HANDLE_TYPE_VALUES \ + IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE, \ + IOTHUB_CLIENT_LL_HANDLE_TYPE + +DEFINE_ENUM(IOTHUB_CLIENT_HANDLE_TYPE, IOTHUB_CLIENT_HANDLE_TYPE_VALUES) + +typedef union IOTHUB_CLIENT_HANDLE_VALUE_TAG +{ + IOTHUB_CLIENT_HANDLE iothubClientHandle; + IOTHUB_CLIENT_LL_HANDLE iothubClientLLHandle; +} IOTHUB_CLIENT_HANDLE_VALUE; + +typedef struct IOTHUB_CLIENT_HANDLE_VARIANT_TAG +{ + IOTHUB_CLIENT_HANDLE_TYPE iothubClientHandleType; + IOTHUB_CLIENT_HANDLE_VALUE iothubClientHandleValue; +} IOTHUB_CLIENT_HANDLE_VARIANT; + +typedef struct SERIALIZER_DEVICETWIN_PROTOHANDLE_TAG /*it is called "PROTOHANDLE" because it is a primitive type of handle*/ +{ + IOTHUB_CLIENT_HANDLE_VARIANT iothubClientHandleVariant; + void* deviceAssigned; +} SERIALIZER_DEVICETWIN_PROTOHANDLE; + +static VECTOR_HANDLE g_allProtoHandles=NULL; /*contains SERIALIZER_DEVICETWIN_PROTOHANDLE*/ + +static int lazilyAddProtohandle(const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle) +{ + int result; + if ((g_allProtoHandles == NULL) && ((g_allProtoHandles = VECTOR_create(sizeof(SERIALIZER_DEVICETWIN_PROTOHANDLE))) == NULL)) + { + LogError("failure in VECTOR_create"); + result = __LINE__; + } + else + { + if (VECTOR_push_back(g_allProtoHandles, protoHandle, 1) != 0) + { + LogError("failure in VECTOR_push_back"); + result = __LINE__; + + /*leave it as it was*/ + + if (VECTOR_size(g_allProtoHandles) == 0) + { + VECTOR_destroy(g_allProtoHandles); + g_allProtoHandles = NULL; + } + } + else + { + result = 0; + } + } + return result; +} + +static IOTHUB_CLIENT_RESULT Generic_IoTHubClient_SetDeviceTwinCallback(const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK deviceTwinCallback, void* userContextCallback) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_SERIALIZERDEVICETWIN_02_011: [ IoTHubDeviceTwinCreate_Impl shall set the device twin callback. ]*/ + switch (protoHandle->iothubClientHandleVariant.iothubClientHandleType) + { + case IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE: + { + if ((result = IoTHubClient_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, deviceTwinCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_SetDeviceTwinCallback"); + } + break; + } + case IOTHUB_CLIENT_LL_HANDLE_TYPE: + { + if ((result =IoTHubClient_LL_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, deviceTwinCallback, userContextCallback)) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_LL_SetDeviceTwinCallback"); + } + break; + } + default: + { + result = IOTHUB_CLIENT_ERROR; + LogError("INTERNAL ERROR"); + } + }/*switch*/ + return result; +} + +static void* IoTHubDeviceTwinCreate_Impl(const char* name, size_t sizeOfName, SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle) +{ + void* result; + /*Codes_SRS_SERIALIZERDEVICETWIN_02_009: [ IoTHubDeviceTwinCreate_Impl shall locate the model and the metadata for name by calling Schema_GetSchemaForModel/Schema_GetMetadata/Schema_GetModelByName. ]*/ + SCHEMA_HANDLE h = Schema_GetSchemaForModel(name); + if (h == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Schema_GetSchemaForModel."); + result = NULL; + } + else + { + void* metadata = Schema_GetMetadata(h); + SCHEMA_MODEL_TYPE_HANDLE modelType = Schema_GetModelByName(h, name); + if (modelType == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Schema_GetModelByName"); + result = NULL; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_010: [ IoTHubDeviceTwinCreate_Impl shall call CodeFirst_CreateDevice. ]*/ + result = CodeFirst_CreateDevice(modelType, (REFLECTED_DATA_FROM_DATAPROVIDER *)metadata, sizeOfName, true); + if (result == NULL) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in CodeFirst_CreateDevice"); + /*return as is*/ + } + else + { + protoHandle->deviceAssigned = result; + if (Generic_IoTHubClient_SetDeviceTwinCallback(protoHandle, serializer_ingest, result) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("failure in Generic_IoTHubClient_SetDeviceTwinCallback"); + CodeFirst_DestroyDevice(result); + result = NULL; + } + else + { + /*lazily add the protohandle to the array of tracking handles*/ + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_012: [ IoTHubDeviceTwinCreate_Impl shall record the pair of (device, IoTHubClient(_LL)). ]*/ + if (lazilyAddProtohandle(protoHandle) != 0) + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_014: [ Otherwise, IoTHubDeviceTwinCreate_Impl shall fail and return NULL. ]*/ + LogError("unable to add the protohandle to the collection of handles"); + /*unsubscribe*/ + if (Generic_IoTHubClient_SetDeviceTwinCallback(protoHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + /*just log the error*/ + LogError("failure in Generic_IoTHubClient_SetDeviceTwinCallback"); + } + CodeFirst_DestroyDevice(result); + result = NULL; + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_013: [ If all operations complete successfully then IoTHubDeviceTwinCreate_Impl shall succeeds and return a non-NULL value. ]*/ + /*return as is*/ + } + } + } + } + } + return result; +} + +static bool protoHandleHasDeviceStartAddress(const void* element, const void* value) +{ + const SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle = (const SERIALIZER_DEVICETWIN_PROTOHANDLE*)element; + return protoHandle->deviceAssigned == value; +} + +static void IoTHubDeviceTwin_Destroy_Impl(void* model) +{ + /*Codes_SRS_SERIALIZERDEVICETWIN_02_020: [ If model is NULL then IoTHubDeviceTwin_Destroy_Impl shall return. ]*/ + if (model == NULL) + { + LogError("invalid argument void* model=%p", model); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_015: [ IoTHubDeviceTwin_Destroy_Impl shall locate the saved handle belonging to model. ]*/ + SERIALIZER_DEVICETWIN_PROTOHANDLE* protoHandle = (SERIALIZER_DEVICETWIN_PROTOHANDLE*)VECTOR_find_if(g_allProtoHandles, protoHandleHasDeviceStartAddress, model); + if (protoHandle == NULL) + { + LogError("failure in VECTOR_find_if [not found]"); + } + else + { + /*Codes_SRS_SERIALIZERDEVICETWIN_02_016: [ IoTHubDeviceTwin_Destroy_Impl shall set the devicetwin callback to NULL. ]*/ + switch (protoHandle->iothubClientHandleVariant.iothubClientHandleType) + { + case IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE: + { + if (IoTHubClient_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_SetDeviceTwinCallback"); + } + break; + } + case IOTHUB_CLIENT_LL_HANDLE_TYPE: + { + if (IoTHubClient_LL_SetDeviceTwinCallback(protoHandle->iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogError("failure in IoTHubClient_LL_SetDeviceTwinCallback"); + } + break; + } + default: + { + LogError("INTERNAL ERROR"); + } + }/*switch*/ + } + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_017: [ IoTHubDeviceTwin_Destroy_Impl shall call CodeFirst_DestroyDevice. ]*/ + CodeFirst_DestroyDevice(protoHandle->deviceAssigned); + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_018: [ IoTHubDeviceTwin_Destroy_Impl shall remove the IoTHubClient_Handle and the device handle from the recorded set. ]*/ + VECTOR_erase(g_allProtoHandles, protoHandle, 1); + + /*Codes_SRS_SERIALIZERDEVICETWIN_02_019: [ If the recorded set of IoTHubClient handles is zero size, then the set shall be destroyed. ]*/ + if (VECTOR_size(g_allProtoHandles) == 0) /*lazy init means more work @ destroy time*/ + { + VECTOR_destroy(g_allProtoHandles); + g_allProtoHandles = NULL; + } + } +} + +#define DECLARE_DEVICETWIN_MODEL(name, ...) \ + DECLARE_MODEL(name, __VA_ARGS__) \ + static name* C2(IoTHubDeviceTwin_Create, name)(IOTHUB_CLIENT_HANDLE iotHubClientHandle) \ + { \ + SERIALIZER_DEVICETWIN_PROTOHANDLE protoHandle; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleType = IOTHUB_CLIENT_CONVENIENCE_HANDLE_TYPE; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleValue.iothubClientHandle = iotHubClientHandle; \ + return (name*)IoTHubDeviceTwinCreate_Impl(#name, sizeof(name), &protoHandle); \ + } \ + \ + static void C2(IoTHubDeviceTwin_Destroy, name) (name* model) \ + { \ + IoTHubDeviceTwin_Destroy_Impl(model); \ + } \ + \ + static name* C2(IoTHubDeviceTwin_LL_Create, name)(IOTHUB_CLIENT_LL_HANDLE iotHubClientLLHandle) \ + { \ + SERIALIZER_DEVICETWIN_PROTOHANDLE protoHandle; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleType = IOTHUB_CLIENT_LL_HANDLE_TYPE; \ + protoHandle.iothubClientHandleVariant.iothubClientHandleValue.iothubClientLLHandle = iotHubClientLLHandle; \ + return (name*)IoTHubDeviceTwinCreate_Impl(#name, sizeof(name), &protoHandle); \ + } \ + \ + static void C2(IoTHubDeviceTwin_LL_Destroy, name) (name* model) \ + { \ + IoTHubDeviceTwin_Destroy_Impl(model); \ + } \ + +#endif /*SERIALIZER_DEVICE_TWIN_H*/ + +