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

Revision:
17:fa1bba4c6053
Parent:
14:0c00e67a4ab1
Child:
18:58b667752399
--- a/commanddecoder.c	Thu Oct 20 17:08:06 2016 -0700
+++ b/commanddecoder.c	Wed Nov 16 21:38:26 2016 -0800
@@ -19,12 +19,12 @@
 
 DEFINE_ENUM_STRINGS(COMMANDDECODER_RESULT, COMMANDDECODER_RESULT_VALUES);
 
-typedef struct COMMAND_DECODER_INSTANCE_TAG
+typedef struct COMMAND_DECODER_HANDLE_DATA_TAG
 {
     SCHEMA_MODEL_TYPE_HANDLE ModelHandle;
     ACTION_CALLBACK_FUNC ActionCallback;
     void* ActionCallbackContext;
-} COMMAND_DECODER_INSTANCE;
+} COMMAND_DECODER_HANDLE_DATA;
 
 static int DecodeValueFromNode(SCHEMA_HANDLE schemaHandle, AGENT_DATA_TYPE* agentDataType, MULTITREE_HANDLE node, const char* edmTypeName)
 {
@@ -169,7 +169,7 @@
     return result;
 }
 
-static EXECUTE_COMMAND_RESULT DecodeAndExecuteModelAction(COMMAND_DECODER_INSTANCE* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeActionPath, const char* actionName, MULTITREE_HANDLE commandNode)
+static EXECUTE_COMMAND_RESULT DecodeAndExecuteModelAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, SCHEMA_MODEL_TYPE_HANDLE modelHandle, const char* relativeActionPath, const char* actionName, MULTITREE_HANDLE commandNode)
 {
     EXECUTE_COMMAND_RESULT result;
     char tempStr[128];
@@ -292,7 +292,7 @@
     return result;
 }
 
-static EXECUTE_COMMAND_RESULT ScanActionPathAndExecuteAction(COMMAND_DECODER_INSTANCE* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* actionPath, MULTITREE_HANDLE commandNode)
+static EXECUTE_COMMAND_RESULT ScanActionPathAndExecuteAction(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, SCHEMA_HANDLE schemaHandle, const char* actionPath, MULTITREE_HANDLE commandNode)
 {
     EXECUTE_COMMAND_RESULT result;
     char* relativeActionPath;
@@ -377,7 +377,7 @@
     return result;
 }
 
-static EXECUTE_COMMAND_RESULT DecodeCommand(COMMAND_DECODER_INSTANCE* commandDecoderInstance, MULTITREE_HANDLE commandNode)
+static EXECUTE_COMMAND_RESULT DecodeCommand(COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance, MULTITREE_HANDLE commandNode)
 {
     EXECUTE_COMMAND_RESULT result;
     SCHEMA_HANDLE schemaHandle;
@@ -422,7 +422,7 @@
 EXECUTE_COMMAND_RESULT CommandDecoder_ExecuteCommand(COMMAND_DECODER_HANDLE handle, const char* command)
 {
     EXECUTE_COMMAND_RESULT result;
-    COMMAND_DECODER_INSTANCE* commandDecoderInstance = (COMMAND_DECODER_INSTANCE*)handle;
+    COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
     /*Codes_SRS_COMMAND_DECODER_01_010: [If either the buffer or the receiveCallbackContext argument is NULL, the processing shall stop and the command shall not be dispatched and it shall return EXECUTE_COMMAND_ERROR.]*/
     if (
         (command == NULL) ||
@@ -481,7 +481,7 @@
 
 COMMAND_DECODER_HANDLE CommandDecoder_Create(SCHEMA_MODEL_TYPE_HANDLE modelHandle, ACTION_CALLBACK_FUNC actionCallback, void* actionCallbackContext)
 {
-    COMMAND_DECODER_INSTANCE* result;
+    COMMAND_DECODER_HANDLE_DATA* result;
     /* Codes_SRS_COMMAND_DECODER_99_019:[ For all exposed APIs argument validity checks shall precede other checks.] */
     /* Codes_SRS_COMMAND_DECODER_01_003: [If any of the arguments modelHandle is NULL, CommandDecoder_Create shall return NULL.]*/
     if (
@@ -495,11 +495,11 @@
     else
     {
         /* Codes_SRS_COMMAND_DECODER_01_001: [CommandDecoder_Create shall create a new instance of a CommandDecoder.] */
-        result = malloc(sizeof(COMMAND_DECODER_INSTANCE));
+        result = malloc(sizeof(COMMAND_DECODER_HANDLE_DATA));
         if (result == NULL)
         {
             /* Codes_SRS_COMMAND_DECODER_01_004: [If any error is encountered during CommandDecoder_Create CommandDecoder_Create shall return NULL.] */
-            result = NULL;
+            /*return as is*/
         }
         else
         {
@@ -517,9 +517,204 @@
     /* Codes_SRS_COMMAND_DECODER_01_007: [If CommandDecoder_Destroy is called with a NULL handle, CommandDecoder_Destroy shall do nothing.] */
     if (commandDecoderHandle != NULL)
     {
-        COMMAND_DECODER_INSTANCE* commandDecoderInstance = (COMMAND_DECODER_INSTANCE*)commandDecoderHandle;
+        COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)commandDecoderHandle;
 
         /* Codes_SRS_COMMAND_DECODER_01_005: [CommandDecoder_Destroy shall free all resources associated with the commandDecoderHandle instance.] */
         free(commandDecoderInstance);
     }
-}
\ No newline at end of file
+}
+
+DEFINE_ENUM_STRINGS(AGENT_DATA_TYPE_TYPE, AGENT_DATA_TYPE_TYPE_VALUES);
+
+/*validates that the multitree (coming from a JSON) is actually a serialization of the model (complete or incomplete)*/
+/*if the serialization contains more than the model, then it fails.*/
+/*if the serialization does not contain mandatory items from the model, it fails*/
+static bool validateModel_vs_Multitree(void* startAddress, SCHEMA_MODEL_TYPE_HANDLE modelHandle, MULTITREE_HANDLE desiredPropertiesTree, size_t offset)
+{
+    
+    bool result;
+    size_t nChildren;
+    size_t nProcessedChildren = 0;
+    (void)MultiTree_GetChildCount(desiredPropertiesTree, &nChildren);
+    for (size_t i = 0;i < nChildren;i++)
+    {
+        MULTITREE_HANDLE child;
+        if (MultiTree_GetChild(desiredPropertiesTree, i, &child) != MULTITREE_OK)
+        {
+            LogError("failure in MultiTree_GetChild");
+            i = nChildren;
+        }
+        else
+        {
+            STRING_HANDLE childName = STRING_new();
+            if (childName == NULL)
+            {
+                LogError("failure to STRING_new");
+                i = nChildren;
+            }
+            else
+            {
+                if (MultiTree_GetName(child, childName) != MULTITREE_OK)
+                {
+                    LogError("failure to MultiTree_GetName");
+                    i = nChildren;
+                }
+                else
+                {
+                    const char *childName_str = STRING_c_str(childName);
+                    SCHEMA_MODEL_ELEMENT elementType = Schema_GetModelElementByName(modelHandle, childName_str);
+                    switch (elementType.elementType)
+                    {
+                        default:
+                        {
+                            LogError("INTERNAL ERROR: unexpected function return");
+                            i = nChildren;
+                            break;
+                        }
+                        case (SCHEMA_PROPERTY):
+                        {
+                            LogError("cannot ingest name (WITH_DATA instead of WITH_DESIRED_PROPERTY): %s", STRING_c_str);
+                            i = nChildren;
+                            break;
+                        }
+                        case (SCHEMA_REPORTED_PROPERTY):
+                        {
+                            LogError("cannot ingest name (WITH_REPORTED_PROPERTY instead of WITH_DESIRED_PROPERTY): %s", STRING_c_str);
+                            i = nChildren;
+                            break;
+                        }
+                        case (SCHEMA_DESIRED_PROPERTY):
+                        {
+                            /*Codes_SRS_COMMAND_DECODER_02_007: [ If the child name corresponds to a desired property then an AGENT_DATA_TYPE shall be constructed from the MULTITREE node. ]*/
+                            SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = elementType.elementHandle.desiredPropertyHandle;
+                            
+                            const char* desiredPropertyType = Schema_GetModelDesiredPropertyType(desiredPropertyHandle);
+                            AGENT_DATA_TYPE output;
+                            if (DecodeValueFromNode(Schema_GetSchemaForModelType(modelHandle), &output, child, desiredPropertyType) != 0)
+                            {
+                                LogError("failure in DecodeValueFromNode");
+                                i = nChildren;
+                            }
+                            else
+                            {
+                                /*Codes_SRS_COMMAND_DECODER_02_008: [ The desired property shall be constructed in memory by calling pfDesiredPropertyFromAGENT_DATA_TYPE. ]*/
+                                pfDesiredPropertyFromAGENT_DATA_TYPE leFunction = Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(desiredPropertyHandle);
+                                if (leFunction(&output, (char*)startAddress + offset + Schema_GetModelDesiredProperty_offset(desiredPropertyHandle)) != 0)
+                                {
+                                    LogError("failure in a function that converts from AGENT_DATA_TYPE to C data");
+                                }
+                                else
+                                {
+                                    /*Codes_SRS_COMMAND_DECODER_02_013: [ If the desired property has a non-NULL pfOnDesiredProperty then it shall be called. ]*/
+                                    pfOnDesiredProperty onDesiredProperty = Schema_GetModelDesiredProperty_pfOnDesiredProperty(desiredPropertyHandle);
+                                    if (onDesiredProperty != NULL)
+                                    {
+                                        onDesiredProperty((char*)startAddress + offset);
+                                    }
+                                    nProcessedChildren++;
+                                }
+                                Destroy_AGENT_DATA_TYPE(&output);
+                            }
+                            
+                            break;
+                        }
+                        case(SCHEMA_MODEL_IN_MODEL):
+                        {
+                            SCHEMA_MODEL_TYPE_HANDLE modelModel = elementType.elementHandle.modelHandle;
+                            
+                            /*Codes_SRS_COMMAND_DECODER_02_009: [ If the child name corresponds to a model in model then the function shall call itself recursively. ]*/
+                            if (!validateModel_vs_Multitree(startAddress, modelModel, child, offset + Schema_GetModelModelByName_Offset(modelHandle, childName_str)))
+                            {
+                                LogError("failure in validateModel_vs_Multitree");
+                                i = nChildren;
+                            }
+                            else
+                            {
+                                /*if the model in model so happened to be a WITH_DESIRED_PROPERTY... (only those has non_NULL pfOnDesiredProperty) */
+                                /*Codes_SRS_COMMAND_DECODER_02_012: [ If the child model in model has a non-NULL pfOnDesiredProperty then pfOnDesiredProperty shall be called. ]*/
+                                pfOnDesiredProperty onDesiredProperty = Schema_GetModelModelByName_OnDesiredProperty(modelHandle, childName_str);
+                                if (onDesiredProperty != NULL)
+                                {
+                                    onDesiredProperty((char*)startAddress + offset);
+                                }
+                                
+                                nProcessedChildren++;
+                            }
+                            
+                            break;
+                        }
+
+                    } /*switch*/
+                }
+                STRING_delete(childName);
+            }
+        }
+    }
+
+    if(nProcessedChildren == nChildren)
+    {
+        /*Codes_SRS_COMMAND_DECODER_02_010: [ If the complete MULTITREE has been parsed then CommandDecoder_IngestDesiredProperties shall succeed and return EXECUTE_COMMAND_SUCCESS. ]*/
+        result = true;
+    }
+    else
+    {
+        /*Codes_SRS_COMMAND_DECODER_02_011: [ Otherwise CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_FAILED. ]*/
+        LogError("not all constituents of the JSON have been ingested");
+        result = false;
+    }
+    return result;
+}
+
+static EXECUTE_COMMAND_RESULT DecodeDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE_DATA* handle, MULTITREE_HANDLE desiredPropertiesTree)
+{
+    /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
+    return validateModel_vs_Multitree(startAddress, handle->ModelHandle, desiredPropertiesTree, 0 )?EXECUTE_COMMAND_SUCCESS:EXECUTE_COMMAND_FAILED;
+}
+
+EXECUTE_COMMAND_RESULT CommandDecoder_IngestDesiredProperties(void* startAddress, COMMAND_DECODER_HANDLE handle, const char* desiredProperties)
+{
+    EXECUTE_COMMAND_RESULT result;
+    /*Codes_SRS_COMMAND_DECODER_02_001: [ If startAddress is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
+    /*Codes_SRS_COMMAND_DECODER_02_002: [ If handle is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
+    /*Codes_SRS_COMMAND_DECODER_02_003: [ If desiredProperties is NULL then CommandDecoder_IngestDesiredProperties shall fail and return EXECUTE_COMMAND_ERROR. ]*/
+    if(
+        (startAddress == NULL) ||
+        (handle == NULL) ||
+        (desiredProperties == NULL)
+        )
+    {
+        LogError("invalid argument COMMAND_DECODER_HANDLE handle=%p, const char* desiredProperties=%p", handle, desiredProperties);
+        result = EXECUTE_COMMAND_ERROR;
+    }
+    else
+    {
+        /*Codes_SRS_COMMAND_DECODER_02_004: [ CommandDecoder_IngestDesiredProperties shall clone desiredProperties. ]*/
+        char* copy;
+        if (mallocAndStrcpy_s(&copy, desiredProperties) != 0)
+        {
+            LogError("failure in mallocAndStrcpy_s");
+            result = EXECUTE_COMMAND_FAILED;
+        }
+        else
+        {
+            /*Codes_SRS_COMMAND_DECODER_02_005: [ CommandDecoder_IngestDesiredProperties shall create a MULTITREE_HANDLE ouf of the clone of desiredProperties. ]*/
+            MULTITREE_HANDLE desiredPropertiesTree;
+            if (JSONDecoder_JSON_To_MultiTree(copy, &desiredPropertiesTree) != JSON_DECODER_OK)
+            {
+                LogError("Decoding JSON to a multi tree failed");
+                result = EXECUTE_COMMAND_ERROR;
+            }
+            else
+            {
+                COMMAND_DECODER_HANDLE_DATA* commandDecoderInstance = (COMMAND_DECODER_HANDLE_DATA*)handle;
+
+                /*Codes_SRS_COMMAND_DECODER_02_006: [ CommandDecoder_IngestDesiredProperties shall parse the MULTITREEE recursively. ]*/
+                result = DecodeDesiredProperties(startAddress, commandDecoderInstance, desiredPropertiesTree);
+
+                MultiTree_Destroy(desiredPropertiesTree);
+            }
+            free(copy);
+        }
+    }
+    return result;
+}