Azure IoT / serializer

Dependents:   sht15_remote_monitoring f767zi_mqtt remote_monitoring simplesample_amqp ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers codefirst.c Source File

codefirst.c

00001 // Copyright (c) Microsoft. All rights reserved.
00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
00003 
00004 #include <stdlib.h>
00005 #include <stdarg.h>
00006 #include "azure_c_shared_utility/gballoc.h"
00007 
00008 #include "codefirst.h"
00009 #include "azure_c_shared_utility/macro_utils.h"
00010 #include "azure_c_shared_utility/crt_abstractions.h"
00011 #include "azure_c_shared_utility/xlogging.h"
00012 #include <stddef.h>
00013 #include "azure_c_shared_utility/crt_abstractions.h"
00014 #include "iotdevice.h"
00015 
00016 DEFINE_ENUM_STRINGS(CODEFIRST_RESULT, CODEFIRST_RESULT_VALUES)
00017 DEFINE_ENUM_STRINGS(EXECUTE_COMMAND_RESULT, EXECUTE_COMMAND_RESULT_VALUES)
00018 
00019 #define LOG_CODEFIRST_ERROR \
00020     LogError("(result = %s)", ENUM_TO_STRING(CODEFIRST_RESULT, result))
00021 
00022 typedef struct DEVICE_HEADER_DATA_TAG
00023 {
00024     DEVICE_HANDLE DeviceHandle;
00025     const REFLECTED_DATA_FROM_DATAPROVIDER* ReflectedData;
00026     SCHEMA_MODEL_TYPE_HANDLE ModelHandle;
00027     size_t DataSize;
00028     unsigned char* data;
00029 } DEVICE_HEADER_DATA;
00030 
00031 #define COUNT_OF(A) (sizeof(A) / sizeof((A)[0]))
00032 
00033 /*design considerations for lazy init of CodeFirst:
00034     There are 2 main states: either CodeFirst is in an initialized state, or it is not initialized.
00035     The initialized state means there's a g_OverrideSchemaNamespace set (even when it is set to NULL).
00036     The uninitialized state means g_OverrideSchemaNamespace is not set.
00037 
00038     To switch to Init state, either call CodeFirst_Init (that sets g_OverrideSchemaNamespace to something)
00039     or call directly an API (that will set automatically g_OverrideSchemaNamespace to NULL).
00040 
00041     To switch to NOT INIT state, depending on the method used to initialize:
00042         - if CodeFirst_Init was called, then only by a call to CodeFirst_Deinit the switch will take place
00043             (note how in this case g_OverrideSchemaNamespace survives destruction of all devices).
00044         - if the init has been done "lazily" by an API call then the module returns to uninitialized state
00045             when the number of devices reaches zero.
00046 
00047                               +-----------------------------+
00048      Start  +---------------->|                             |
00049                               |  NOT INIT                   |
00050             +---------------->|                             |
00051             |                 +------------+----------------+
00052             |                              |
00053             |                              |
00054             |                              | _Init | APIs
00055             |                              |
00056             |                              v
00057             |     +---------------------------------------------------+
00058             |     | Init State                                        |
00059             |     | +-----------------+       +-----------------+     |
00060             |     | |                 |       |                 |     |
00061             |     | |  init by _Init  |       |   init by API   |     |
00062             |     | +------+----------+       +---------+-------+     |
00063             |     |        |                            |             |
00064             |     |        |_DeInit                     | nDevices==0 |
00065             |     |        |                            |             |
00066             |     +--------v----------------+-----------v-------------+
00067             |                               |
00068             +-------------------------------+
00069 
00070 */
00071 
00072 
00073 #define CODEFIRST_STATE_VALUES \
00074     CODEFIRST_STATE_NOT_INIT, \
00075     CODEFIRST_STATE_INIT_BY_INIT, \
00076     CODEFIRST_STATE_INIT_BY_API
00077 
00078 DEFINE_ENUM(CODEFIRST_STATE, CODEFIRST_STATE_VALUES)
00079 
00080 static CODEFIRST_STATE g_state = CODEFIRST_STATE_NOT_INIT;
00081 static const char* g_OverrideSchemaNamespace;
00082 static size_t g_DeviceCount = 0;
00083 static DEVICE_HEADER_DATA** g_Devices = NULL;
00084 
00085 static void deinitializeDesiredProperties(SCHEMA_MODEL_TYPE_HANDLE model, void* destination)
00086 {
00087     size_t nDesiredProperties;
00088     if (Schema_GetModelDesiredPropertyCount(model, &nDesiredProperties) != SCHEMA_OK)
00089     {
00090         LogError("unexpected error in Schema_GetModelDesiredPropertyCount");
00091     }
00092     else
00093     {
00094         size_t nProcessedDesiredProperties = 0;
00095         for (size_t i = 0;i < nDesiredProperties;i++)
00096         {
00097             SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = Schema_GetModelDesiredPropertyByIndex(model, i);
00098             if (desiredPropertyHandle == NULL)
00099             {
00100                 LogError("unexpected error in Schema_GetModelDesiredPropertyByIndex");
00101                 i = nDesiredProperties;
00102             }
00103             else
00104             {
00105                 pfDesiredPropertyDeinitialize desiredPropertyDeinitialize = Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize(desiredPropertyHandle);
00106                 if (desiredPropertyDeinitialize == NULL)
00107                 {
00108                     LogError("unexpected error in Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize");
00109                     i = nDesiredProperties;
00110                 }
00111                 else
00112                 {
00113                     size_t offset = Schema_GetModelDesiredProperty_offset(desiredPropertyHandle);
00114                     desiredPropertyDeinitialize((char*)destination + offset);
00115                     nProcessedDesiredProperties++;
00116                 }
00117             }
00118         }
00119 
00120         if (nDesiredProperties == nProcessedDesiredProperties)
00121         {
00122             /*recursively go in the model and initialize the other fields*/
00123             size_t nModelInModel;
00124             if (Schema_GetModelModelCount(model, &nModelInModel) != SCHEMA_OK)
00125             {
00126                 LogError("unexpected error in Schema_GetModelModelCount");
00127             }
00128             else
00129             {
00130                 size_t nProcessedModelInModel = 0;
00131                 for (size_t i = 0; i < nModelInModel;i++)
00132                 {
00133                     SCHEMA_MODEL_TYPE_HANDLE modelInModel = Schema_GetModelModelyByIndex(model, i);
00134                     if (modelInModel == NULL)
00135                     {
00136                         LogError("unexpected failure in Schema_GetModelModelyByIndex");
00137                         i = nModelInModel;
00138                     }
00139                     else
00140                     {
00141                         size_t offset = Schema_GetModelModelByIndex_Offset(model, i);
00142                         deinitializeDesiredProperties(modelInModel, (char*)destination + offset);
00143                         nProcessedModelInModel++;
00144                     }
00145                 }
00146 
00147                 if (nProcessedModelInModel == nModelInModel)
00148                 {
00149                     /*all is fine... */
00150                 }
00151             }
00152         }
00153     }
00154 }
00155 
00156 static void DestroyDevice(DEVICE_HEADER_DATA* deviceHeader)
00157 {
00158     /* Codes_SRS_CODEFIRST_99_085:[CodeFirst_DestroyDevice shall free all resources associated with a device.] */
00159     /* Codes_SRS_CODEFIRST_99_087:[In order to release the device handle, CodeFirst_DestroyDevice shall call Device_Destroy.] */
00160 
00161     Device_Destroy(deviceHeader->DeviceHandle);
00162     free(deviceHeader->data);
00163     free(deviceHeader);
00164 }
00165 
00166 static CODEFIRST_RESULT buildStructTypes(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData)
00167 {
00168     CODEFIRST_RESULT result = CODEFIRST_OK;
00169 
00170     const REFLECTED_SOMETHING* something;
00171     for (something = reflectedData->reflectedData; something != NULL; something = something->next)
00172     {
00173         if (something->type == REFLECTION_STRUCT_TYPE)
00174         {
00175             SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle;
00176             structTypeHandle = Schema_CreateStructType(schemaHandle, something->what.structure.name);
00177 
00178             if (structTypeHandle == NULL)
00179             {
00180                 /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00181                 result = CODEFIRST_SCHEMA_ERROR;
00182                 LogError("create struct failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00183                 break;
00184             }
00185             else
00186             {
00187                 const REFLECTED_SOMETHING* maybeField;
00188                 /*look for the field... */
00189                 for (maybeField = reflectedData->reflectedData; maybeField != NULL; maybeField = maybeField->next)
00190                 {
00191                     if (maybeField->type == REFLECTION_FIELD_TYPE)
00192                     {
00193                         if (strcmp(maybeField->what.field.structName, something->what.structure.name) == 0)
00194                         {
00195                             if (Schema_AddStructTypeProperty(structTypeHandle, maybeField->what.field.fieldName, maybeField->what.field.fieldType) != SCHEMA_OK)
00196                             {
00197                                 /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00198                                 result = CODEFIRST_SCHEMA_ERROR;
00199                                 LogError("add struct property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00200                                 break;
00201                             }
00202                         }
00203                     }
00204                 }
00205             }
00206         }
00207     }
00208 
00209     return result;
00210 }
00211 
00212 static CODEFIRST_RESULT buildModel(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData, const REFLECTED_SOMETHING* modelReflectedData)
00213 {
00214     CODEFIRST_RESULT result = CODEFIRST_OK;
00215     const REFLECTED_SOMETHING* something;
00216     SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle;
00217 
00218     modelTypeHandle = Schema_GetModelByName(schemaHandle, modelReflectedData->what.model.name);
00219     if (modelTypeHandle == NULL)
00220     {
00221         /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00222         result = CODEFIRST_SCHEMA_ERROR;
00223         LogError("cannot get model %s %s", modelReflectedData->what.model.name, ENUM_TO_STRING(CODEFIRST_RESULT, result));
00224         goto out;
00225     }
00226 
00227     for (something = reflectedData->reflectedData; something != NULL; something = something->next)
00228     {
00229         /* looking for all elements that belong to a model: properties and actions */
00230         if ((something->type == REFLECTION_PROPERTY_TYPE) &&
00231             (strcmp(something->what.property.modelName, modelReflectedData->what.model.name) == 0))
00232         {
00233             SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.property.type);
00234 
00235             /* if this is a model type use the appropriate APIs for it */
00236             if (childModelHande != NULL)
00237             {
00238                 if (Schema_AddModelModel(modelTypeHandle, something->what.property.name, childModelHande, something->what.property.offset, NULL) != SCHEMA_OK)
00239                 {
00240                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00241                     result = CODEFIRST_SCHEMA_ERROR;
00242                     LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00243                     goto out;
00244                 }
00245             }
00246             else
00247             {
00248                 if (Schema_AddModelProperty(modelTypeHandle, something->what.property.name, something->what.property.type) != SCHEMA_OK)
00249                 {
00250                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00251                     result = CODEFIRST_SCHEMA_ERROR;
00252                     LogError("add property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00253                     goto out;
00254                 }
00255             }
00256         }
00257 
00258         if ((something->type == REFLECTION_REPORTED_PROPERTY_TYPE) &&
00259             (strcmp(something->what.reportedProperty.modelName, modelReflectedData->what.model.name) == 0))
00260         {
00261             SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.reportedProperty.type);
00262 
00263             /* if this is a model type use the appropriate APIs for it */
00264             if (childModelHande != NULL)
00265             {
00266                 if (Schema_AddModelModel(modelTypeHandle, something->what.reportedProperty.name, childModelHande, something->what.reportedProperty.offset, NULL) != SCHEMA_OK)
00267                 {
00268                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00269                     result = CODEFIRST_SCHEMA_ERROR;
00270                     LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00271                     goto out;
00272                 }
00273             }
00274             else
00275             {
00276                 if (Schema_AddModelReportedProperty(modelTypeHandle, something->what.reportedProperty.name, something->what.reportedProperty.type) != SCHEMA_OK)
00277                 {
00278                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00279                     result = CODEFIRST_SCHEMA_ERROR;
00280                     LogError("add reported property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00281                     goto out;
00282                 }
00283             }
00284         }
00285 
00286         if ((something->type == REFLECTION_DESIRED_PROPERTY_TYPE) &&
00287             (strcmp(something->what.desiredProperty.modelName, modelReflectedData->what.model.name) == 0))
00288         {
00289             SCHEMA_MODEL_TYPE_HANDLE childModelHande = Schema_GetModelByName(schemaHandle, something->what.desiredProperty.type);
00290 
00291             /* if this is a model type use the appropriate APIs for it */
00292             if (childModelHande != NULL)
00293             {
00294                 if (Schema_AddModelModel(modelTypeHandle, something->what.desiredProperty.name, childModelHande, something->what.desiredProperty.offset, something->what.desiredProperty.onDesiredProperty) != SCHEMA_OK)
00295                 {
00296                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00297                     result = CODEFIRST_SCHEMA_ERROR;
00298                     LogError("add model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00299                     goto out;
00300                 }
00301             }
00302             else
00303             {
00304                 if (Schema_AddModelDesiredProperty(modelTypeHandle,
00305                     something->what.desiredProperty.name,
00306                     something->what.desiredProperty.type,
00307                     something->what.desiredProperty.FromAGENT_DATA_TYPE,
00308                     something->what.desiredProperty.desiredPropertInitialize,
00309                     something->what.desiredProperty.desiredPropertDeinitialize,
00310                     something->what.desiredProperty.offset,
00311                     something->what.desiredProperty.onDesiredProperty) != SCHEMA_OK)
00312                 {
00313                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00314                     result = CODEFIRST_SCHEMA_ERROR;
00315                     LogError("add desired property failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00316                     goto out;
00317                 }
00318             }
00319         }
00320 
00321         if ((something->type == REFLECTION_ACTION_TYPE) &&
00322             (strcmp(something->what.action.modelName, modelReflectedData->what.model.name) == 0))
00323         {
00324             SCHEMA_ACTION_HANDLE actionHandle;
00325             size_t i;
00326 
00327             if ((actionHandle = Schema_CreateModelAction(modelTypeHandle, something->what.action.name)) == NULL)
00328             {
00329                 /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00330                 result = CODEFIRST_SCHEMA_ERROR;
00331                 LogError("add model action failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00332                 goto out;
00333             }
00334 
00335             for (i = 0; i < something->what.action.nArguments; i++)
00336             {
00337                 if (Schema_AddModelActionArgument(actionHandle, something->what.action.arguments[i].name, something->what.action.arguments[i].type) != SCHEMA_OK)
00338                 {
00339                     /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00340                     result = CODEFIRST_SCHEMA_ERROR;
00341                     LogError("add model action argument failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00342                     goto out;
00343                 }
00344             }
00345         }
00346 
00347         if ((something->type == REFLECTION_METHOD_TYPE) &&
00348             (strcmp(something->what.method.modelName, modelReflectedData->what.model.name) == 0))
00349         {
00350             SCHEMA_METHOD_HANDLE methodHandle;
00351             size_t i;
00352 
00353             if ((methodHandle = Schema_CreateModelMethod(modelTypeHandle, something->what.method.name)) == NULL)
00354             {
00355                 /*Codes_SRS_CODEFIRST_99_076: [ If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL. ]*/
00356                 result = CODEFIRST_SCHEMA_ERROR;
00357                 LogError("add model method failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00358                 goto out;
00359             }
00360 
00361             for (i = 0; i < something->what.method.nArguments; i++)
00362             {
00363                 if (Schema_AddModelMethodArgument(methodHandle, something->what.method.arguments[i].name, something->what.method.arguments[i].type) != SCHEMA_OK)
00364                 {
00365                     /*Codes_SRS_CODEFIRST_99_076: [ If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL. ]*/
00366                     result = CODEFIRST_SCHEMA_ERROR;
00367                     LogError("add model method argument failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00368                     goto out;
00369                 }
00370             }
00371         }
00372     }
00373 
00374 out:
00375     return result;
00376 }
00377 
00378 static CODEFIRST_RESULT buildModelTypes(SCHEMA_HANDLE schemaHandle, const REFLECTED_DATA_FROM_DATAPROVIDER* reflectedData)
00379 {
00380     CODEFIRST_RESULT result = CODEFIRST_OK;
00381     const REFLECTED_SOMETHING* something;
00382 
00383     /* first have a pass and add all the model types */
00384     for (something = reflectedData->reflectedData; something != NULL; something = something->next)
00385     {
00386         if (something->type == REFLECTION_MODEL_TYPE)
00387         {
00388             if (Schema_CreateModelType(schemaHandle, something->what.model.name) == NULL)
00389             {
00390                 /*Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CODEFIRST_SCHEMA_ERROR shall be returned.]*/
00391                 result = CODEFIRST_SCHEMA_ERROR;
00392                 LogError("create model failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00393                 goto out;
00394             }
00395         }
00396     }
00397 
00398     for (something = reflectedData->reflectedData; something != NULL; something = something->next)
00399     {
00400         if (something->type == REFLECTION_MODEL_TYPE)
00401         {
00402             result = buildModel(schemaHandle, reflectedData, something);
00403             if (result != CODEFIRST_OK)
00404             {
00405                 break;
00406             }
00407         }
00408     }
00409 
00410 out:
00411     return result;
00412 }
00413 
00414 static CODEFIRST_RESULT CodeFirst_Init_impl(const char* overrideSchemaNamespace, bool calledFromCodeFirst_Init)
00415 {
00416     /*shall build the default EntityContainer*/
00417     CODEFIRST_RESULT result;
00418 
00419     if (g_state != CODEFIRST_STATE_NOT_INIT)
00420     {
00421         /*Codes_SRS_CODEFIRST_99_003:[ If the module is already initialized, the initialization shall fail and the return value shall be CODEFIRST_ALREADY_INIT.]*/
00422         result = CODEFIRST_ALREADY_INIT;
00423         if(calledFromCodeFirst_Init) /*do not log this error when APIs attempt lazy init*/
00424         {
00425              LogError("CodeFirst was already init %s", ENUM_TO_STRING(CODEFIRST_RESULT, result));
00426         }
00427     }
00428     else
00429     {
00430         g_DeviceCount = 0;
00431         g_OverrideSchemaNamespace = overrideSchemaNamespace;
00432         g_Devices = NULL;
00433 
00434         /*Codes_SRS_CODEFIRST_99_002:[ CodeFirst_Init shall initialize the CodeFirst module. If initialization is successful, it shall return CODEFIRST_OK.]*/
00435         g_state = calledFromCodeFirst_Init? CODEFIRST_STATE_INIT_BY_INIT: CODEFIRST_STATE_INIT_BY_API;
00436         result = CODEFIRST_OK;
00437     }
00438     return result;
00439 }
00440 
00441 /*Codes_SRS_CODEFIRST_99_002:[ CodeFirst_Init shall initialize the CodeFirst module. If initialization is successful, it shall return CODEFIRST_OK.]*/
00442 CODEFIRST_RESULT CodeFirst_Init(const char* overrideSchemaNamespace)
00443 {
00444     return CodeFirst_Init_impl(overrideSchemaNamespace, true);
00445 }
00446 
00447 void CodeFirst_Deinit(void)
00448 {
00449     /*Codes_SRS_CODEFIRST_99_006:[If the module is not previously initialed, CodeFirst_Deinit shall do nothing.]*/
00450     if (g_state != CODEFIRST_STATE_INIT_BY_INIT)
00451     {
00452         LogError("CodeFirst_Deinit called when CodeFirst was not initialized by CodeFirst_Init");
00453     }
00454     else
00455     {
00456         size_t i;
00457 
00458         /*Codes_SRS_CODEFIRST_99_005:[ CodeFirst_Deinit shall deinitialize the module, freeing all the resources and placing the module in an uninitialized state.]*/
00459         for (i = 0; i < g_DeviceCount; i++)
00460         {
00461             DestroyDevice(g_Devices[i]);
00462         }
00463 
00464         free(g_Devices);
00465         g_Devices = NULL;
00466         g_DeviceCount = 0;
00467 
00468         g_state = CODEFIRST_STATE_NOT_INIT;
00469     }
00470 }
00471 
00472 static const REFLECTED_SOMETHING* FindModelInCodeFirstMetadata(const REFLECTED_SOMETHING* reflectedData, const char* modelName)
00473 {
00474     const REFLECTED_SOMETHING* result;
00475 
00476     for (result = reflectedData; result != NULL; result = result->next)
00477     {
00478         if ((result->type == REFLECTION_MODEL_TYPE) &&
00479             (strcmp(result->what.model.name, modelName) == 0))
00480         {
00481             /* found model type */
00482             break;
00483         }
00484     }
00485 
00486     return result;
00487 }
00488 
00489 static const REFLECTED_SOMETHING* FindChildModelInCodeFirstMetadata(const REFLECTED_SOMETHING* reflectedData, const REFLECTED_SOMETHING* startModel, const char* relativePath, size_t* offset)
00490 {
00491     const REFLECTED_SOMETHING* result = startModel;
00492     *offset = 0;
00493 
00494     /* Codes_SRS_CODEFIRST_99_139:[If the relativeActionPath is empty then the action shall be looked up in the device model.] */
00495     while ((*relativePath != 0) && (result != NULL))
00496     {
00497         /* Codes_SRS_CODEFIRST_99_142:[The relativeActionPath argument shall be in the format "childModel1/childModel2/.../childModelN".] */
00498         const REFLECTED_SOMETHING* childModelProperty;
00499         size_t propertyNameLength;
00500         const char* slashPos = strchr(relativePath, '/');
00501         if (slashPos == NULL)
00502         {
00503             slashPos = &relativePath[strlen(relativePath)];
00504         }
00505 
00506         propertyNameLength = slashPos - relativePath;
00507 
00508         for (childModelProperty = reflectedData; childModelProperty != NULL; childModelProperty = childModelProperty->next)
00509         {
00510             if ((childModelProperty->type == REFLECTION_PROPERTY_TYPE) &&
00511                 (strcmp(childModelProperty->what.property.modelName, result->what.model.name) == 0) &&
00512                 (strncmp(childModelProperty->what.property.name, relativePath, propertyNameLength) == 0) &&
00513                 (strlen(childModelProperty->what.property.name) == propertyNameLength))
00514             {
00515                 /* property found, now let's find the model */
00516                 /* Codes_SRS_CODEFIRST_99_140:[CodeFirst_InvokeAction shall pass to the action wrapper that it calls a pointer to the model where the action is defined.] */
00517                 *offset += childModelProperty->what.property.offset;
00518                 break;
00519             }
00520         }
00521 
00522         if (childModelProperty == NULL)
00523         {
00524             /* not found */
00525             result = NULL;
00526         }
00527         else
00528         {
00529             result = FindModelInCodeFirstMetadata(reflectedData, childModelProperty->what.property.type);
00530         }
00531 
00532         relativePath = slashPos;
00533     }
00534 
00535     return result;
00536 }
00537 
00538 EXECUTE_COMMAND_RESULT CodeFirst_InvokeAction(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeActionPath, const char* actionName, size_t parameterCount, const AGENT_DATA_TYPE* parameterValues)
00539 {
00540     EXECUTE_COMMAND_RESULT result;
00541     DEVICE_HEADER_DATA* deviceHeader = (DEVICE_HEADER_DATA*)callbackUserContext;
00542 
00543     /*Codes_SRS_CODEFIRST_99_068:[ If the function is called before CodeFirst is initialized then EXECUTE_COMMAND_ERROR shall be returned.] */
00544     if (g_state == CODEFIRST_STATE_NOT_INIT)
00545     {
00546         result = EXECUTE_COMMAND_ERROR;
00547         LogError("CodeFirst_InvokeAction called before init has an error %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result));
00548     }
00549     /*Codes_SRS_CODEFIRST_99_066:[ If actionName, relativeActionPath or deviceHandle is NULL then EXECUTE_COMMAND_ERROR shall be returned*/
00550     else if ((actionName == NULL) ||
00551         (deviceHandle == NULL) ||
00552         (relativeActionPath == NULL))
00553     {
00554         result = EXECUTE_COMMAND_ERROR;
00555         LogError("action Name is NULL %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result));
00556     }
00557     /*Codes_SRS_CODEFIRST_99_067:[ If parameterCount is greater than zero and parameterValues is NULL then EXECUTE_COMMAND_ERROR shall be returned.]*/
00558     else if ((parameterCount > 0) && (parameterValues == NULL))
00559     {
00560         result = EXECUTE_COMMAND_ERROR;
00561         LogError("parameterValues error %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result));
00562     }
00563     else
00564     {
00565         const REFLECTED_SOMETHING* something;
00566         const REFLECTED_SOMETHING* childModel;
00567         const char* modelName;
00568         size_t offset;
00569 
00570         modelName = Schema_GetModelName(deviceHeader->ModelHandle);
00571 
00572         if (((childModel = FindModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, modelName)) == NULL) ||
00573             /* Codes_SRS_CODEFIRST_99_138:[The relativeActionPath argument shall be used by CodeFirst_InvokeAction to find the child model where the action is declared.] */
00574             ((childModel = FindChildModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, childModel, relativeActionPath, &offset)) == NULL))
00575         {
00576             /*Codes_SRS_CODEFIRST_99_141:[If a child model specified in the relativeActionPath argument cannot be found by CodeFirst_InvokeAction, it shall return EXECUTE_COMMAND_ERROR.] */
00577             result = EXECUTE_COMMAND_ERROR;
00578             LogError("action %s was not found %s ", actionName, ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result));
00579         }
00580         else
00581         {
00582             /* Codes_SRS_CODEFIRST_99_062:[ When CodeFirst_InvokeAction is called it shall look through the codefirst metadata associated with a specific device for a previously declared action (function) named actionName.]*/
00583             /* Codes_SRS_CODEFIRST_99_078:[If such a function is not found then the function shall return EXECUTE_COMMAND_ERROR.]*/
00584             result = EXECUTE_COMMAND_ERROR;
00585             for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next)
00586             {
00587                 if ((something->type == REFLECTION_ACTION_TYPE) &&
00588                     (strcmp(actionName, something->what.action.name) == 0) &&
00589                     (strcmp(childModel->what.model.name, something->what.action.modelName) == 0))
00590                 {
00591                     /*Codes_SRS_CODEFIRST_99_063:[ If the function is found, then CodeFirst shall call the wrapper of the found function inside the data provider. The wrapper is linked in the reflected data to the function name. The wrapper shall be called with the same arguments as CodeFirst_InvokeAction has been called.]*/
00592                     /*Codes_SRS_CODEFIRST_99_064:[ If the wrapper call succeeds then CODEFIRST_OK shall be returned. ]*/
00593                     /*Codes_SRS_CODEFIRST_99_065:[ For all the other return values CODEFIRST_ACTION_EXECUTION_ERROR shall be returned.]*/
00594                     /* Codes_SRS_CODEFIRST_99_140:[CodeFirst_InvokeAction shall pass to the action wrapper that it calls a pointer to the model where the action is defined.] */
00595                     /*Codes_SRS_CODEFIRST_02_013: [The wrapper's return value shall be returned.]*/
00596                     result = something->what.action.wrapper(deviceHeader->data + offset, parameterCount, parameterValues);
00597                     break;
00598                 }
00599             }
00600         }
00601     }
00602 
00603     if (result == EXECUTE_COMMAND_ERROR)
00604     {
00605         LogError(" %s ", ENUM_TO_STRING(EXECUTE_COMMAND_RESULT, result));
00606     }
00607     return result;
00608 }
00609 
00610 METHODRETURN_HANDLE CodeFirst_InvokeMethod(DEVICE_HANDLE deviceHandle, void* callbackUserContext, const char* relativeMethodPath, const char* methodName, size_t parameterCount, const AGENT_DATA_TYPE* parameterValues)
00611 {
00612     METHODRETURN_HANDLE result;
00613     DEVICE_HEADER_DATA* deviceHeader = (DEVICE_HEADER_DATA*)callbackUserContext;
00614 
00615     if (g_state == CODEFIRST_STATE_NOT_INIT)
00616     {
00617         result = NULL;
00618         LogError("CodeFirst_InvokeMethod called before CodeFirst_Init");
00619     }
00620     else if ((methodName == NULL) ||
00621         (deviceHandle == NULL) ||
00622         (relativeMethodPath == NULL))
00623     {
00624         result = NULL;
00625         LogError("invalid args: DEVICE_HANDLE deviceHandle=%p, void* callbackUserContext=%p, const char* relativeMethodPath=%p, const char* methodName=%p, size_t parameterCount=%zu, const AGENT_DATA_TYPE* parameterValues=%p",
00626             deviceHandle, callbackUserContext, relativeMethodPath, methodName, parameterCount, parameterValues);
00627     }
00628     else if ((parameterCount > 0) && (parameterValues == NULL))
00629     {
00630         result = NULL;
00631         LogError("parameterValues error ");
00632     }
00633     else
00634     {
00635         const REFLECTED_SOMETHING* something;
00636         const REFLECTED_SOMETHING* childModel;
00637         const char* modelName;
00638         size_t offset;
00639 
00640         modelName = Schema_GetModelName(deviceHeader->ModelHandle);
00641 
00642         if (((childModel = FindModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, modelName)) == NULL) ||
00643             ((childModel = FindChildModelInCodeFirstMetadata(deviceHeader->ReflectedData->reflectedData, childModel, relativeMethodPath, &offset)) == NULL))
00644         {
00645             result = NULL;
00646             LogError("method %s was not found", methodName);
00647         }
00648         else
00649         {
00650             result = NULL;
00651             for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next)
00652             {
00653                 if ((something->type == REFLECTION_METHOD_TYPE) &&
00654                     (strcmp(methodName, something->what.method.name) == 0) &&
00655                     (strcmp(childModel->what.model.name, something->what.method.modelName) == 0))
00656                 {
00657                     break; /*found...*/
00658                 }
00659             }
00660 
00661             if (something == NULL)
00662             {
00663                 LogError("method \"%s\" not found", methodName);
00664                 result = NULL;
00665             }
00666             else
00667             {
00668                 result = something->what.method.wrapper(deviceHeader->data + offset, parameterCount, parameterValues);
00669                 if (result == NULL)
00670                 {
00671                     LogError("method \"%s\" execution error (returned NULL)", methodName);
00672                 }
00673             }
00674         }
00675     }
00676     return result;
00677 }
00678 
00679 
00680 /* Codes_SRS_CODEFIRST_99_002:[ CodeFirst_RegisterSchema shall create the schema information and give it to the Schema module for one schema, identified by the metadata argument. On success, it shall return a handle to the schema.] */
00681 SCHEMA_HANDLE CodeFirst_RegisterSchema(const char* schemaNamespace, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata)
00682 {
00683     SCHEMA_HANDLE result;
00684     /*Codes_SRS_CODEFIRST_02_048: [ If schemaNamespace is NULL then CodeFirst_RegisterSchema shall fail and return NULL. ]*/
00685     /*Codes_SRS_CODEFIRST_02_049: [ If metadata is NULL then CodeFirst_RegisterSchema shall fail and return NULL. ]*/
00686     if (
00687         (schemaNamespace == NULL) ||
00688         (metadata == NULL)
00689         )
00690     {
00691         LogError("invalid arg const char* schemaNamespace=%p, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata=%p", schemaNamespace, metadata);
00692         result = NULL;
00693     }
00694     else
00695     {
00696         if (g_OverrideSchemaNamespace != NULL)
00697         {
00698             schemaNamespace = g_OverrideSchemaNamespace;
00699         }
00700 
00701         /* Codes_SRS_CODEFIRST_99_121:[If the schema has already been registered, CodeFirst_RegisterSchema shall return its handle.] */
00702         result = Schema_GetSchemaByNamespace(schemaNamespace);
00703         if (result == NULL)
00704         {
00705             if ((result = Schema_Create(schemaNamespace, (void*)metadata)) == NULL)
00706             {
00707                 /* Codes_SRS_CODEFIRST_99_076:[If any Schema APIs fail, CodeFirst_RegisterSchema shall return NULL.] */
00708                 result = NULL;
00709                 LogError("schema init failed %s", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_SCHEMA_ERROR));
00710             }
00711             else
00712             {
00713                 if ((buildStructTypes(result, metadata) != CODEFIRST_OK) ||
00714                     (buildModelTypes(result, metadata) != CODEFIRST_OK))
00715                 {
00716                     Schema_Destroy(result);
00717                     result = NULL;
00718                 }
00719                 else
00720                 {
00721                     /* do nothing, everything is OK */
00722                 }
00723             }
00724         }
00725     }
00726 
00727     return result;
00728 }
00729 
00730 AGENT_DATA_TYPE_TYPE CodeFirst_GetPrimitiveType(const char* typeName)
00731 {
00732 #ifndef NO_FLOATS
00733     if (strcmp(typeName, "double") == 0)
00734     {
00735         return EDM_DOUBLE_TYPE;
00736     }
00737     else if (strcmp(typeName, "float") == 0)
00738     {
00739         return EDM_SINGLE_TYPE;
00740     }
00741     else
00742 #endif
00743     if (strcmp(typeName, "int") == 0)
00744     {
00745         return EDM_INT32_TYPE;
00746     }
00747     else if (strcmp(typeName, "long") == 0)
00748     {
00749         return EDM_INT64_TYPE;
00750     }
00751     else if (strcmp(typeName, "int8_t") == 0)
00752     {
00753         return EDM_SBYTE_TYPE;
00754     }
00755     else if (strcmp(typeName, "uint8_t") == 0)
00756     {
00757         return EDM_BYTE_TYPE;
00758     }
00759     else if (strcmp(typeName, "int16_t") == 0)
00760     {
00761         return EDM_INT16_TYPE;
00762     }
00763     else if (strcmp(typeName, "int32_t") == 0)
00764     {
00765         return EDM_INT32_TYPE;
00766     }
00767     else if (strcmp(typeName, "int64_t") == 0)
00768     {
00769         return EDM_INT64_TYPE;
00770     }
00771     else if (
00772         (strcmp(typeName, "_Bool") == 0) ||
00773         (strcmp(typeName, "bool") == 0)
00774         )
00775     {
00776         return EDM_BOOLEAN_TYPE;
00777     }
00778     else if (strcmp(typeName, "ascii_char_ptr") == 0)
00779     {
00780         return EDM_STRING_TYPE;
00781     }
00782     else if (strcmp(typeName, "ascii_char_ptr_no_quotes") == 0)
00783     {
00784         return EDM_STRING_NO_QUOTES_TYPE;
00785     }
00786     else if (strcmp(typeName, "EDM_DATE_TIME_OFFSET") == 0)
00787     {
00788         return EDM_DATE_TIME_OFFSET_TYPE;
00789     }
00790     else if (strcmp(typeName, "EDM_GUID") == 0)
00791     {
00792         return EDM_GUID_TYPE;
00793     }
00794     else if (strcmp(typeName, "EDM_BINARY") == 0)
00795     {
00796         return EDM_BINARY_TYPE;
00797     }
00798     else
00799     {
00800         return EDM_NO_TYPE;
00801     }
00802 }
00803 
00804 static void initializeDesiredProperties(SCHEMA_MODEL_TYPE_HANDLE model, void* destination)
00805 {
00806     /*this function assumes that at the address given by destination there is an instance of a model*/
00807     /*some constituents of the model need to be initialized - ascii_char_ptr for example should be set to NULL*/
00808 
00809     size_t nDesiredProperties;
00810     if (Schema_GetModelDesiredPropertyCount(model, &nDesiredProperties) != SCHEMA_OK)
00811     {
00812         LogError("unexpected error in Schema_GetModelDesiredPropertyCount");
00813     }
00814     else
00815     {
00816         size_t nProcessedDesiredProperties = 0;
00817         for (size_t i = 0;i < nDesiredProperties;i++)
00818         {
00819             SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle = Schema_GetModelDesiredPropertyByIndex(model, i);
00820             if (desiredPropertyHandle == NULL)
00821             {
00822                 LogError("unexpected error in Schema_GetModelDesiredPropertyByIndex");
00823                 i = nDesiredProperties;
00824             }
00825             else
00826             {
00827                 pfDesiredPropertyInitialize desiredPropertyInitialize = Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize(desiredPropertyHandle);
00828                 if (desiredPropertyInitialize == NULL)
00829                 {
00830                     LogError("unexpected error in Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize");
00831                     i = nDesiredProperties;
00832                 }
00833                 else
00834                 {
00835                     /*Codes_SRS_CODEFIRST_02_036: [ CodeFirst_CreateDevice shall initialize all the desired properties to their default values. ]*/
00836                     size_t offset = Schema_GetModelDesiredProperty_offset(desiredPropertyHandle);
00837                     desiredPropertyInitialize((char*)destination + offset);
00838                     nProcessedDesiredProperties++;
00839                 }
00840             }
00841         }
00842 
00843         if (nDesiredProperties == nProcessedDesiredProperties)
00844         {
00845             /*recursively go in the model and initialize the other fields*/
00846             size_t nModelInModel;
00847             if (Schema_GetModelModelCount(model, &nModelInModel) != SCHEMA_OK)
00848             {
00849                 LogError("unexpected error in Schema_GetModelModelCount");
00850             }
00851             else
00852             {
00853                 size_t nProcessedModelInModel = 0;
00854                 for (size_t i = 0; i < nModelInModel;i++)
00855                 {
00856                     SCHEMA_MODEL_TYPE_HANDLE modelInModel = Schema_GetModelModelyByIndex(model, i);
00857                     if (modelInModel == NULL)
00858                     {
00859                         LogError("unexpected failure in Schema_GetModelModelyByIndex");
00860                         i = nModelInModel;
00861                     }
00862                     else
00863                     {
00864                         size_t offset = Schema_GetModelModelByIndex_Offset(model, i);
00865                         initializeDesiredProperties(modelInModel, (char*)destination + offset);
00866                         nProcessedModelInModel++;
00867                     }
00868                 }
00869 
00870                 if (nProcessedModelInModel == nModelInModel)
00871                 {
00872                     /*all is fine... */
00873                 }
00874             }
00875         }
00876     }
00877 }
00878 
00879 /* Codes_SRS_CODEFIRST_99_079:[CodeFirst_CreateDevice shall create a device and allocate a memory block that should hold the device data.] */
00880 void* CodeFirst_CreateDevice(SCHEMA_MODEL_TYPE_HANDLE model, const REFLECTED_DATA_FROM_DATAPROVIDER* metadata, size_t dataSize, bool includePropertyPath)
00881 {
00882     void* result;
00883     DEVICE_HEADER_DATA* deviceHeader;
00884 
00885     /* Codes_SRS_CODEFIRST_99_080:[If CodeFirst_CreateDevice is invoked with a NULL model, it shall return NULL.]*/
00886     if (model == NULL)
00887     {
00888         result = NULL;
00889         LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_INVALID_ARG));
00890     }
00891     else
00892     {
00893         /*Codes_SRS_CODEFIRST_02_037: [ CodeFirst_CreateDevice shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/
00894         (void)CodeFirst_Init_impl(NULL, false); /*lazy init*/
00895 
00896         if ((deviceHeader = (DEVICE_HEADER_DATA*)malloc(sizeof(DEVICE_HEADER_DATA))) == NULL)
00897         {
00898             /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */
00899             result = NULL;
00900             LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR));
00901         }
00902         /* Codes_SRS_CODEFIRST_99_081:[CodeFirst_CreateDevice shall use Device_Create to create a device handle.] */
00903         /* Codes_SRS_CODEFIRST_99_082: [ CodeFirst_CreateDevice shall pass to Device_Create the function CodeFirst_InvokeAction, action callback argument and the CodeFirst_InvokeMethod ] */
00904         else
00905         {
00906             if ((deviceHeader->data = malloc(dataSize)) == NULL)
00907             {
00908                 free(deviceHeader);
00909                 deviceHeader = NULL;
00910                 result = NULL;
00911                 LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR));
00912             }
00913             else
00914             {
00915                 DEVICE_HEADER_DATA** newDevices;
00916 
00917                 initializeDesiredProperties(model, deviceHeader->data);
00918 
00919                 if (Device_Create(model, CodeFirst_InvokeAction, deviceHeader, CodeFirst_InvokeMethod, deviceHeader,
00920                     includePropertyPath, &deviceHeader->DeviceHandle) != DEVICE_OK)
00921                 {
00922                     free(deviceHeader->data);
00923                     free(deviceHeader);
00924 
00925                     /* Codes_SRS_CODEFIRST_99_084:[If Device_Create fails, CodeFirst_CreateDevice shall return NULL.] */
00926                     result = NULL;
00927                     LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_DEVICE_FAILED));
00928                 }
00929                 else if ((newDevices = (DEVICE_HEADER_DATA**)realloc(g_Devices, sizeof(DEVICE_HEADER_DATA*) * (g_DeviceCount + 1))) == NULL)
00930                 {
00931                     Device_Destroy(deviceHeader->DeviceHandle);
00932                     free(deviceHeader->data);
00933                     free(deviceHeader);
00934 
00935                     /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */
00936                     result = NULL;
00937                     LogError(" %s ", ENUM_TO_STRING(CODEFIRST_RESULT, CODEFIRST_ERROR));
00938                 }
00939                 else
00940                 {
00941                     SCHEMA_RESULT schemaResult;
00942                     deviceHeader->ReflectedData = metadata;
00943                     deviceHeader->DataSize = dataSize;
00944                     deviceHeader->ModelHandle = model;
00945                     schemaResult = Schema_AddDeviceRef(model);
00946                     if (schemaResult != SCHEMA_OK)
00947                     {
00948                         Device_Destroy(deviceHeader->DeviceHandle);
00949                         free(newDevices);
00950                         free(deviceHeader->data);
00951                         free(deviceHeader);
00952 
00953                         /* Codes_SRS_CODEFIRST_99_102:[On any other errors, Device_Create shall return NULL.] */
00954                         result = NULL;
00955                     }
00956                     else
00957                     {
00958                         g_Devices = newDevices;
00959                         g_Devices[g_DeviceCount] = deviceHeader;
00960                         g_DeviceCount++;
00961 
00962                         /* Codes_SRS_CODEFIRST_99_101:[On success, CodeFirst_CreateDevice shall return a non NULL pointer to the device data.] */
00963                         result = deviceHeader->data;
00964                     }
00965                 }
00966             }
00967         }
00968 
00969     }
00970 
00971     return result;
00972 }
00973 
00974 void CodeFirst_DestroyDevice(void* device)
00975 {
00976     /* Codes_SRS_CODEFIRST_99_086:[If the argument is NULL, CodeFirst_DestroyDevice shall do nothing.] */
00977     if (device != NULL)
00978     {
00979         size_t i;
00980 
00981         for (i = 0; i < g_DeviceCount; i++)
00982         {
00983             if (g_Devices[i]->data == device)
00984             {
00985                 deinitializeDesiredProperties(g_Devices[i]->ModelHandle, g_Devices[i]->data);
00986                 Schema_ReleaseDeviceRef(g_Devices[i]->ModelHandle);
00987 
00988                 // Delete the Created Schema if all the devices are unassociated
00989                 Schema_DestroyIfUnused(g_Devices[i]->ModelHandle);
00990 
00991                 DestroyDevice(g_Devices[i]);
00992                 (void)memcpy(&g_Devices[i], &g_Devices[i + 1], (g_DeviceCount - i - 1) * sizeof(DEVICE_HEADER_DATA*));
00993                 g_DeviceCount--;
00994                 break;
00995             }
00996         }
00997 
00998         /*Codes_SRS_CODEFIRST_02_039: [ If the current device count is zero then CodeFirst_DestroyDevice shall deallocate all other used resources. ]*/
00999         if ((g_state == CODEFIRST_STATE_INIT_BY_API) && (g_DeviceCount == 0))
01000         {
01001             free(g_Devices);
01002             g_Devices = NULL;
01003             g_state = CODEFIRST_STATE_NOT_INIT;
01004         }
01005     }
01006 }
01007 
01008 static DEVICE_HEADER_DATA* FindDevice(void* value)
01009 {
01010     size_t i;
01011     DEVICE_HEADER_DATA* result = NULL;
01012 
01013     for (i = 0; i < g_DeviceCount; i++)
01014     {
01015         if ((g_Devices[i]->data <= (unsigned char*)value) &&
01016             (g_Devices[i]->data + g_Devices[i]->DataSize > (unsigned char*)value))
01017         {
01018             result = g_Devices[i];
01019             break;
01020         }
01021     }
01022 
01023     return result;
01024 }
01025 
01026 static const REFLECTED_SOMETHING* FindValue(DEVICE_HEADER_DATA* deviceHeader, void* value, const char* modelName, size_t startOffset, STRING_HANDLE valuePath)
01027 {
01028     const REFLECTED_SOMETHING* result;
01029     size_t valueOffset = (size_t)((unsigned char*)value - (unsigned char*)deviceHeader->data) - startOffset;
01030 
01031     for (result = deviceHeader->ReflectedData->reflectedData; result != NULL; result = result->next)
01032     {
01033         if (result->type == REFLECTION_PROPERTY_TYPE &&
01034             (strcmp(result->what.property.modelName, modelName) == 0) &&
01035             (result->what.property.offset <= valueOffset) &&
01036             (result->what.property.offset + result->what.property.size > valueOffset))
01037         {
01038             if (startOffset != 0)
01039             {
01040                 STRING_concat(valuePath, "/");
01041             }
01042 
01043             STRING_concat(valuePath, result->what.property.name);
01044             break;
01045         }
01046     }
01047 
01048     if (result != NULL)
01049     {
01050         /* Codes_SRS_CODEFIRST_99_133:[CodeFirst_SendAsync shall allow sending of properties that are part of a child model.] */
01051         if (result->what.property.offset < valueOffset)
01052         {
01053             /* find recursively the property in the inner model, if there is one */
01054             result = FindValue(deviceHeader, value, result->what.property.type, startOffset + result->what.property.offset, valuePath);
01055         }
01056     }
01057 
01058     return result;
01059 }
01060 
01061 static const REFLECTED_SOMETHING* FindReportedProperty(DEVICE_HEADER_DATA* deviceHeader, void* value, const char* modelName, size_t startOffset, STRING_HANDLE valuePath)
01062 {
01063     const REFLECTED_SOMETHING* result;
01064     size_t valueOffset = (size_t)((unsigned char*)value - (unsigned char*)deviceHeader->data) - startOffset;
01065 
01066     for (result = deviceHeader->ReflectedData->reflectedData; result != NULL; result = result->next)
01067     {
01068         if (result->type == REFLECTION_REPORTED_PROPERTY_TYPE &&
01069             (strcmp(result->what.reportedProperty.modelName, modelName) == 0) &&
01070             (result->what.reportedProperty.offset <= valueOffset) &&
01071             (result->what.reportedProperty.offset + result->what.reportedProperty.size > valueOffset))
01072         {
01073             if (startOffset != 0)
01074             {
01075                 if (STRING_concat(valuePath, "/") != 0)
01076                 {
01077                     LogError("unable to STRING_concat");
01078                     result = NULL;
01079                     break;
01080                 }
01081             }
01082 
01083             if (STRING_concat(valuePath, result->what.reportedProperty.name) != 0)
01084             {
01085                 LogError("unable to STRING_concat");
01086                 result = NULL;
01087                 break;
01088             }
01089             break;
01090         }
01091     }
01092 
01093     if (result != NULL)
01094     {
01095         /* Codes_SRS_CODEFIRST_99_133:[CodeFirst_SendAsync shall allow sending of properties that are part of a child model.] */
01096         if (result->what.reportedProperty.offset < valueOffset)
01097         {
01098             /* find recursively the property in the inner model, if there is one */
01099             result = FindReportedProperty(deviceHeader, value, result->what.reportedProperty.type, startOffset + result->what.reportedProperty.offset, valuePath);
01100         }
01101     }
01102 
01103     return result;
01104 }
01105 
01106 /* Codes_SRS_CODEFIRST_99_130:[If a pointer to the beginning of a device block is passed to CodeFirst_SendAsync instead of a pointer to a property, CodeFirst_SendAsync shall send all the properties that belong to that device.] */
01107 /* Codes_SRS_CODEFIRST_99_131:[The properties shall be given to Device as one transaction, as if they were all passed as individual arguments to Code_First.] */
01108 static CODEFIRST_RESULT SendAllDeviceProperties(DEVICE_HEADER_DATA* deviceHeader, TRANSACTION_HANDLE transaction)
01109 {
01110     const char* modelName = Schema_GetModelName(deviceHeader->ModelHandle);
01111     const REFLECTED_SOMETHING* something;
01112     unsigned char* deviceAddress = (unsigned char*)deviceHeader->data;
01113     CODEFIRST_RESULT result = CODEFIRST_OK;
01114 
01115     for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next)
01116     {
01117         if ((something->type == REFLECTION_PROPERTY_TYPE) &&
01118             (strcmp(something->what.property.modelName, modelName) == 0))
01119         {
01120             AGENT_DATA_TYPE agentDataType;
01121 
01122             /* Codes_SRS_CODEFIRST_99_097:[For each value marshalling to AGENT_DATA_TYPE shall be performed.] */
01123             /* Codes_SRS_CODEFIRST_99_098:[The marshalling shall be done by calling the Create_AGENT_DATA_TYPE_from_Ptr function associated with the property.] */
01124             if (something->what.property.Create_AGENT_DATA_TYPE_from_Ptr(deviceAddress + something->what.property.offset, &agentDataType) != AGENT_DATA_TYPES_OK)
01125             {
01126                 /* Codes_SRS_CODEFIRST_99_099:[If Create_AGENT_DATA_TYPE_from_Ptr fails, CodeFirst_SendAsync shall return CODEFIRST_AGENT_DATA_TYPE_ERROR.] */
01127                 result = CODEFIRST_AGENT_DATA_TYPE_ERROR;
01128                 LOG_CODEFIRST_ERROR;
01129                 break;
01130             }
01131             else
01132             {
01133                 /* Codes_SRS_CODEFIRST_99_092:[CodeFirst shall publish each value by using Device_PublishTransacted.] */
01134                 if (Device_PublishTransacted(transaction, something->what.property.name, &agentDataType) != DEVICE_OK)
01135                 {
01136                     Destroy_AGENT_DATA_TYPE(&agentDataType);
01137 
01138                     /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */
01139                     result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01140                     LOG_CODEFIRST_ERROR;
01141                     break;
01142                 }
01143 
01144                 Destroy_AGENT_DATA_TYPE(&agentDataType);
01145             }
01146         }
01147     }
01148 
01149     return result;
01150 }
01151 
01152 static CODEFIRST_RESULT SendAllDeviceReportedProperties(DEVICE_HEADER_DATA* deviceHeader, REPORTED_PROPERTIES_TRANSACTION_HANDLE transaction)
01153 {
01154     const char* modelName = Schema_GetModelName(deviceHeader->ModelHandle);
01155     const REFLECTED_SOMETHING* something;
01156     unsigned char* deviceAddress = (unsigned char*)deviceHeader->data;
01157     CODEFIRST_RESULT result = CODEFIRST_OK;
01158 
01159     for (something = deviceHeader->ReflectedData->reflectedData; something != NULL; something = something->next)
01160     {
01161         if ((something->type == REFLECTION_REPORTED_PROPERTY_TYPE) &&
01162             (strcmp(something->what.reportedProperty.modelName, modelName) == 0))
01163         {
01164             AGENT_DATA_TYPE agentDataType;
01165 
01166             if (something->what.reportedProperty.Create_AGENT_DATA_TYPE_from_Ptr(deviceAddress + something->what.reportedProperty.offset, &agentDataType) != AGENT_DATA_TYPES_OK)
01167             {
01168                 result = CODEFIRST_AGENT_DATA_TYPE_ERROR;
01169                 LOG_CODEFIRST_ERROR;
01170                 break;
01171             }
01172             else
01173             {
01174                 if (Device_PublishTransacted_ReportedProperty(transaction, something->what.reportedProperty.name, &agentDataType) != DEVICE_OK)
01175                 {
01176                     Destroy_AGENT_DATA_TYPE(&agentDataType);
01177                     result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01178                     LOG_CODEFIRST_ERROR;
01179                     break;
01180                 }
01181 
01182                 Destroy_AGENT_DATA_TYPE(&agentDataType);
01183             }
01184         }
01185     }
01186 
01187     return result;
01188 }
01189 
01190 
01191 /* Codes_SRS_CODEFIRST_99_088:[CodeFirst_SendAsync shall send to the Device module a set of properties, a destination and a destinationSize.]*/
01192 CODEFIRST_RESULT CodeFirst_SendAsync(unsigned char** destination, size_t* destinationSize, size_t numProperties, ...)
01193 {
01194     CODEFIRST_RESULT result;
01195     va_list ap;
01196 
01197     if (
01198         (numProperties == 0) ||
01199         (destination == NULL) ||
01200         (destinationSize == NULL)
01201         )
01202     {
01203         /* Codes_SRS_CODEFIRST_04_002: [If CodeFirst_SendAsync receives destination or destinationSize NULL, CodeFirst_SendAsync shall return Invalid Argument.]*/
01204         /* Codes_SRS_CODEFIRST_99_103:[If CodeFirst_SendAsync is called with numProperties being zero, CODEFIRST_INVALID_ARG shall be returned.] */
01205         result = CODEFIRST_INVALID_ARG;
01206         LOG_CODEFIRST_ERROR;
01207     }
01208     else
01209     {
01210         /*Codes_SRS_CODEFIRST_02_040: [ CodeFirst_SendAsync shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/
01211         (void)CodeFirst_Init_impl(NULL, false); /*lazy init*/
01212 
01213         DEVICE_HEADER_DATA* deviceHeader = NULL;
01214         size_t i;
01215         TRANSACTION_HANDLE transaction = NULL;
01216         result = CODEFIRST_OK;
01217 
01218         /* Codes_SRS_CODEFIRST_99_105:[The properties are passed as pointers to the memory locations where the data exists in the device block allocated by CodeFirst_CreateDevice.] */
01219         va_start(ap, numProperties);
01220 
01221         /* Codes_SRS_CODEFIRST_99_089:[The numProperties argument shall indicate how many properties are to be sent.] */
01222         for (i = 0; i < numProperties; i++)
01223         {
01224             void* value = (void*)va_arg(ap, void*);
01225 
01226             /* Codes_SRS_CODEFIRST_99_095:[For each value passed to it, CodeFirst_SendAsync shall look up to which device the value belongs.] */
01227             DEVICE_HEADER_DATA* currentValueDeviceHeader = FindDevice(value);
01228             if (currentValueDeviceHeader == NULL)
01229             {
01230                 /* Codes_SRS_CODEFIRST_99_104:[If a property cannot be associated with a device, CodeFirst_SendAsync shall return CODEFIRST_INVALID_ARG.] */
01231                 result = CODEFIRST_INVALID_ARG;
01232                 LOG_CODEFIRST_ERROR;
01233                 break;
01234             }
01235             else if ((deviceHeader != NULL) &&
01236                 (currentValueDeviceHeader != deviceHeader))
01237             {
01238                 /* Codes_SRS_CODEFIRST_99_096:[All values have to belong to the same device, otherwise CodeFirst_SendAsync shall return CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR.] */
01239                 result = CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR;
01240                 LOG_CODEFIRST_ERROR;
01241                 break;
01242             }
01243             /* Codes_SRS_CODEFIRST_99_090:[All the properties shall be sent together by using the transacted APIs of the device.] */
01244             /* Codes_SRS_CODEFIRST_99_091:[CodeFirst_SendAsync shall start a transaction by calling Device_StartTransaction.] */
01245             else if ((deviceHeader == NULL) &&
01246                 ((transaction = Device_StartTransaction(currentValueDeviceHeader->DeviceHandle)) == NULL))
01247             {
01248                 /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */
01249                 result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01250                 LOG_CODEFIRST_ERROR;
01251                 break;
01252             }
01253             else
01254             {
01255                 deviceHeader = currentValueDeviceHeader;
01256 
01257                 if (value == ((unsigned char*)deviceHeader->data))
01258                 {
01259                     /* we got a full device, send all its state data */
01260                     result = SendAllDeviceProperties(deviceHeader, transaction);
01261                     if (result != CODEFIRST_OK)
01262                     {
01263                         LOG_CODEFIRST_ERROR;
01264                         break;
01265                     }
01266                 }
01267                 else
01268                 {
01269                     const REFLECTED_SOMETHING* propertyReflectedData;
01270                     const char* modelName;
01271                     STRING_HANDLE valuePath;
01272 
01273                     if ((valuePath = STRING_new()) == NULL)
01274                     {
01275                         /* Codes_SRS_CODEFIRST_99_134:[If CodeFirst_Notify fails for any other reason it shall return CODEFIRST_ERROR.] */
01276                         result = CODEFIRST_ERROR;
01277                         LOG_CODEFIRST_ERROR;
01278                         break;
01279                     }
01280                     else
01281                     {
01282                         if ((modelName = Schema_GetModelName(deviceHeader->ModelHandle)) == NULL)
01283                         {
01284                             /* Codes_SRS_CODEFIRST_99_134:[If CodeFirst_Notify fails for any other reason it shall return CODEFIRST_ERROR.] */
01285                             result = CODEFIRST_ERROR;
01286                             LOG_CODEFIRST_ERROR;
01287                             STRING_delete(valuePath);
01288                             break;
01289                         }
01290                         else if ((propertyReflectedData = FindValue(deviceHeader, value, modelName, 0, valuePath)) == NULL)
01291                         {
01292                             /* Codes_SRS_CODEFIRST_99_104:[If a property cannot be associated with a device, CodeFirst_SendAsync shall return CODEFIRST_INVALID_ARG.] */
01293                             result = CODEFIRST_INVALID_ARG;
01294                             LOG_CODEFIRST_ERROR;
01295                             STRING_delete(valuePath);
01296                             break;
01297                         }
01298                         else
01299                         {
01300                             AGENT_DATA_TYPE agentDataType;
01301 
01302                             /* Codes_SRS_CODEFIRST_99_097:[For each value marshalling to AGENT_DATA_TYPE shall be performed.] */
01303                             /* Codes_SRS_CODEFIRST_99_098:[The marshalling shall be done by calling the Create_AGENT_DATA_TYPE_from_Ptr function associated with the property.] */
01304                             if (propertyReflectedData->what.property.Create_AGENT_DATA_TYPE_from_Ptr(value, &agentDataType) != AGENT_DATA_TYPES_OK)
01305                             {
01306                                 /* Codes_SRS_CODEFIRST_99_099:[If Create_AGENT_DATA_TYPE_from_Ptr fails, CodeFirst_SendAsync shall return CODEFIRST_AGENT_DATA_TYPE_ERROR.] */
01307                                 result = CODEFIRST_AGENT_DATA_TYPE_ERROR;
01308                                 LOG_CODEFIRST_ERROR;
01309                                 STRING_delete(valuePath);
01310                                 break;
01311                             }
01312                             else
01313                             {
01314                                 /* Codes_SRS_CODEFIRST_99_092:[CodeFirst shall publish each value by using Device_PublishTransacted.] */
01315                                 /* Codes_SRS_CODEFIRST_99_136:[CodeFirst_SendAsync shall build the full path for each property and then pass it to Device_PublishTransacted.] */
01316                                 if (Device_PublishTransacted(transaction, STRING_c_str(valuePath), &agentDataType) != DEVICE_OK)
01317                                 {
01318                                     Destroy_AGENT_DATA_TYPE(&agentDataType);
01319 
01320                                     /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */
01321                                     result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01322                                     LOG_CODEFIRST_ERROR;
01323                                     STRING_delete(valuePath);
01324                                     break;
01325                                 }
01326                                 else
01327                                 {
01328                                     STRING_delete(valuePath); /*anyway*/
01329                                 }
01330 
01331                                 Destroy_AGENT_DATA_TYPE(&agentDataType);
01332                             }
01333                         }
01334                     }
01335                 }
01336             }
01337         }
01338 
01339         if (i < numProperties)
01340         {
01341             if (transaction != NULL)
01342             {
01343                 (void)Device_CancelTransaction(transaction);
01344             }
01345         }
01346         /* Codes_SRS_CODEFIRST_99_093:[After all values have been published, Device_EndTransaction shall be called.] */
01347         else if (Device_EndTransaction(transaction, destination, destinationSize) != DEVICE_OK)
01348         {
01349             /* Codes_SRS_CODEFIRST_99_094:[If any Device API fail, CodeFirst_SendAsync shall return CODEFIRST_DEVICE_PUBLISH_FAILED.] */
01350             result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01351             LOG_CODEFIRST_ERROR;
01352         }
01353         else
01354         {
01355             /* Codes_SRS_CODEFIRST_99_117:[On success, CodeFirst_SendAsync shall return CODEFIRST_OK.] */
01356             result = CODEFIRST_OK;
01357         }
01358 
01359         va_end(ap);
01360 
01361     }
01362 
01363     return result;
01364 }
01365 
01366 CODEFIRST_RESULT CodeFirst_SendAsyncReported(unsigned char** destination, size_t* destinationSize, size_t numReportedProperties, ...)
01367 {
01368     CODEFIRST_RESULT result;
01369     if ((destination == NULL) || (destinationSize == NULL) || numReportedProperties == 0)
01370     {
01371         /*Codes_SRS_CODEFIRST_02_018: [ If parameter destination, destinationSize or any of the values passed through va_args is NULL then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/
01372         LogError("invalid argument unsigned char** destination=%p, size_t* destinationSize=%p, size_t numReportedProperties=%zu", destination, destinationSize, numReportedProperties);
01373         result = CODEFIRST_INVALID_ARG;
01374     }
01375     else
01376     {
01377         /*Codes_SRS_CODEFIRST_02_046: [ CodeFirst_SendAsyncReported shall call CodeFirst_Init, passing NULL for overrideSchemaNamespace. ]*/
01378         (void)CodeFirst_Init_impl(NULL, false);/*lazy init*/
01379 
01380         DEVICE_HEADER_DATA* deviceHeader = NULL;
01381         size_t i;
01382         REPORTED_PROPERTIES_TRANSACTION_HANDLE transaction = NULL;
01383         va_list ap;
01384         result = CODEFIRST_ACTION_EXECUTION_ERROR; /*this initialization squelches a false warning about result not being initialized*/
01385 
01386         va_start(ap, numReportedProperties);
01387 
01388         for (i = 0; i < numReportedProperties; i++)
01389         {
01390             void* value = (void*)va_arg(ap, void*);
01391             /*Codes_SRS_CODEFIRST_02_018: [ If parameter destination, destinationSize or any of the values passed through va_args is NULL then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/
01392             if (value == NULL)
01393             {
01394                 LogError("argument number %zu passed through variable arguments is NULL", i);
01395                 result = CODEFIRST_INVALID_ARG;
01396                 break;
01397             }
01398             else
01399             {
01400                 DEVICE_HEADER_DATA* currentValueDeviceHeader = FindDevice(value);
01401                 if (currentValueDeviceHeader == NULL)
01402                 {
01403                     result = CODEFIRST_INVALID_ARG;
01404                     LOG_CODEFIRST_ERROR;
01405                     break;
01406                 }
01407                 /*Codes_SRS_CODEFIRST_02_019: [ If values passed through va_args do not belong to the same device then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR. ]*/
01408                 else if ((deviceHeader != NULL) &&
01409                     (currentValueDeviceHeader != deviceHeader))
01410                 {
01411                     result = CODEFIRST_VALUES_FROM_DIFFERENT_DEVICES_ERROR;
01412                     LOG_CODEFIRST_ERROR;
01413                     break;
01414                 }
01415                 /*Codes_SRS_CODEFIRST_02_022: [ CodeFirst_SendAsyncReported shall start a transaction by calling Device_CreateTransaction_ReportedProperties. ]*/
01416                 else if ((deviceHeader == NULL) &&
01417                     ((transaction = Device_CreateTransaction_ReportedProperties(currentValueDeviceHeader->DeviceHandle)) == NULL))
01418                 {
01419                     result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01420                     LOG_CODEFIRST_ERROR;
01421                     break;
01422                 }
01423                 else
01424                 {
01425                     deviceHeader = currentValueDeviceHeader;
01426                     if (value == ((unsigned char*)deviceHeader->data))
01427                     {
01428                         /*Codes_SRS_CODEFIRST_02_021: [ If the value passed through va_args is a complete model instance, then CodeFirst_SendAsyncReported shall send all the reported properties of that device. ]*/
01429                         result = SendAllDeviceReportedProperties(deviceHeader, transaction);
01430                         if (result != CODEFIRST_OK)
01431                         {
01432                             LOG_CODEFIRST_ERROR;
01433                             break;
01434                         }
01435                     }
01436                     else
01437                     {
01438                         /*Codes_SRS_CODEFIRST_02_020: [ If values passed through va_args are not all of type REFLECTED_REPORTED_PROPERTY then CodeFirst_SendAsyncReported shall fail and return CODEFIRST_INVALID_ARG. ]*/
01439                         const REFLECTED_SOMETHING* propertyReflectedData;
01440                         const char* modelName;
01441 
01442                         STRING_HANDLE valuePath;
01443                         if ((valuePath = STRING_new()) == NULL)
01444                         {
01445                             result = CODEFIRST_ERROR;
01446                             LOG_CODEFIRST_ERROR;
01447                             break;
01448                         }
01449                         else
01450                         {
01451                             modelName = Schema_GetModelName(deviceHeader->ModelHandle);
01452 
01453                             /*Codes_SRS_CODEFIRST_02_025: [ CodeFirst_SendAsyncReported shall compute for every AGENT_DATA_TYPE the valuePath. ]*/
01454                             if ((propertyReflectedData = FindReportedProperty(deviceHeader, value, modelName, 0, valuePath)) == NULL)
01455                             {
01456                                 result = CODEFIRST_INVALID_ARG;
01457                                 LOG_CODEFIRST_ERROR;
01458                                 STRING_delete(valuePath);
01459                                 break;
01460                             }
01461                             else
01462                             {
01463                                 AGENT_DATA_TYPE agentDataType;
01464                                 /*Codes_SRS_CODEFIRST_02_023: [ CodeFirst_SendAsyncReported shall convert all REPORTED_PROPERTY model components to AGENT_DATA_TYPE. ]*/
01465                                 if (propertyReflectedData->what.reportedProperty.Create_AGENT_DATA_TYPE_from_Ptr(value, &agentDataType) != AGENT_DATA_TYPES_OK)
01466                                 {
01467                                     result = CODEFIRST_AGENT_DATA_TYPE_ERROR;
01468                                     LOG_CODEFIRST_ERROR;
01469                                     STRING_delete(valuePath);
01470                                     break;
01471                                 }
01472                                 else
01473                                 {
01474                                     /*Codes_SRS_CODEFIRST_02_024: [ CodeFirst_SendAsyncReported shall call Device_PublishTransacted_ReportedProperty for every AGENT_DATA_TYPE converted from REPORTED_PROPERTY. ]*/
01475                                     if (Device_PublishTransacted_ReportedProperty(transaction, STRING_c_str(valuePath), &agentDataType) != DEVICE_OK)
01476                                     {
01477                                         Destroy_AGENT_DATA_TYPE(&agentDataType);
01478                                         result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01479                                         LOG_CODEFIRST_ERROR;
01480                                         STRING_delete(valuePath);
01481                                         break;
01482                                     }
01483                                     else
01484                                     {
01485                                         STRING_delete(valuePath);
01486                                     }
01487                                     Destroy_AGENT_DATA_TYPE(&agentDataType);
01488                                 }
01489                             }
01490                         }
01491                     }
01492                 }
01493             }
01494         }
01495 
01496         /*Codes_SRS_CODEFIRST_02_027: [ If any error occurs, CodeFirst_SendAsyncReported shall fail and return CODEFIRST_ERROR. ]*/
01497         if (i < numReportedProperties)
01498         {
01499             if (transaction != NULL)
01500             {
01501                 Device_DestroyTransaction_ReportedProperties(transaction);
01502             }
01503         }
01504         /*Codes_SRS_CODEFIRST_02_026: [ CodeFirst_SendAsyncReported shall call Device_CommitTransaction_ReportedProperties to commit the transaction. ]*/
01505         else
01506         {
01507             if (Device_CommitTransaction_ReportedProperties(transaction, destination, destinationSize) != DEVICE_OK)
01508             {
01509                 result = CODEFIRST_DEVICE_PUBLISH_FAILED;
01510                 LOG_CODEFIRST_ERROR;
01511             }
01512             else
01513             {
01514                 /*Codes_SRS_CODEFIRST_02_028: [ CodeFirst_SendAsyncReported shall return CODEFIRST_OK when it succeeds. ]*/
01515                 result = CODEFIRST_OK;
01516             }
01517 
01518             /*Codes_SRS_CODEFIRST_02_029: [ CodeFirst_SendAsyncReported shall call Device_DestroyTransaction_ReportedProperties to destroy the transaction. ]*/
01519             Device_DestroyTransaction_ReportedProperties(transaction);
01520         }
01521 
01522         va_end(ap);
01523     }
01524     return result;
01525 }
01526 
01527 EXECUTE_COMMAND_RESULT CodeFirst_ExecuteCommand(void* device, const char* command)
01528 {
01529     EXECUTE_COMMAND_RESULT result;
01530     /*Codes_SRS_CODEFIRST_02_014: [If parameter device or command is NULL then CodeFirst_ExecuteCommand shall return EXECUTE_COMMAND_ERROR.] */
01531     if (
01532         (device == NULL) ||
01533         (command == NULL)
01534         )
01535     {
01536         result = EXECUTE_COMMAND_ERROR;
01537         LogError("invalid argument (NULL) passed to CodeFirst_ExecuteCommand void* device = %p, const char* command = %p", device, command);
01538     }
01539     else
01540     {
01541         /*Codes_SRS_CODEFIRST_02_015: [CodeFirst_ExecuteCommand shall find the device.]*/
01542         DEVICE_HEADER_DATA* deviceHeader = FindDevice(device);
01543         if(deviceHeader == NULL)
01544         {
01545             /*Codes_SRS_CODEFIRST_02_016: [If finding the device fails, then CodeFirst_ExecuteCommand shall return EXECUTE_COMMAND_ERROR.]*/
01546             result = EXECUTE_COMMAND_ERROR;
01547             LogError("unable to find the device given by address %p", device);
01548         }
01549         else
01550         {
01551             /*Codes_SRS_CODEFIRST_02_017: [Otherwise CodeFirst_ExecuteCommand shall call Device_ExecuteCommand and return what Device_ExecuteCommand is returning.] */
01552             result = Device_ExecuteCommand(deviceHeader->DeviceHandle, command);
01553         }
01554     }
01555     return result;
01556 }
01557 
01558 METHODRETURN_HANDLE CodeFirst_ExecuteMethod(void* device, const char* methodName, const char* methodPayload)
01559 {
01560     METHODRETURN_HANDLE result;
01561     if (
01562         (device == NULL) ||
01563         (methodName== NULL) /*methodPayload can be NULL*/
01564         )
01565     {
01566         result = NULL;
01567         LogError("invalid argument (NULL) passed to CodeFirst_ExecuteMethod void* device = %p, const char* methodName = %p", device, methodName);
01568     }
01569     else
01570     {
01571         DEVICE_HEADER_DATA* deviceHeader = FindDevice(device);
01572         if (deviceHeader == NULL)
01573         {
01574             result = NULL;
01575             LogError("unable to find the device given by address %p", device);
01576         }
01577         else
01578         {
01579             result = Device_ExecuteMethod(deviceHeader->DeviceHandle, methodName, methodPayload);
01580         }
01581     }
01582     return result;
01583 }
01584 
01585 CODEFIRST_RESULT CodeFirst_IngestDesiredProperties(void* device, const char* jsonPayload, bool parseDesiredNode)
01586 {
01587     CODEFIRST_RESULT result;
01588     /*Codes_SRS_CODEFIRST_02_030: [ If argument device is NULL then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_INVALID_ARG. ]*/
01589     /*Codes_SRS_CODEFIRST_02_031: [ If argument jsonPayload is NULL then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_INVALID_ARG. ]*/
01590     if (
01591         (device == NULL) ||
01592         (jsonPayload == NULL)
01593         )
01594     {
01595         LogError("invalid argument void* device=%p, const char* jsonPayload=%p", device, jsonPayload);
01596         result = CODEFIRST_INVALID_ARG;
01597     }
01598     else
01599     {
01600         /*Codes_SRS_CODEFIRST_02_032: [ CodeFirst_IngestDesiredProperties shall locate the device associated with device. ]*/
01601         DEVICE_HEADER_DATA* deviceHeader = FindDevice(device);
01602         if (deviceHeader == NULL)
01603         {
01604             /*Codes_SRS_CODEFIRST_02_034: [ If there is any failure, then CodeFirst_IngestDesiredProperties shall fail and return CODEFIRST_ERROR. ]*/
01605             LogError("unable to find a device having this memory address %p", device);
01606             result = CODEFIRST_ERROR;
01607         }
01608         else
01609         {
01610             /*Codes_SRS_CODEFIRST_02_033: [ CodeFirst_IngestDesiredProperties shall call Device_IngestDesiredProperties. ]*/
01611             if (Device_IngestDesiredProperties(device, deviceHeader->DeviceHandle, jsonPayload, parseDesiredNode) != DEVICE_OK)
01612             {
01613                 LogError("failure in Device_IngestDesiredProperties");
01614                 result = CODEFIRST_ERROR;
01615             }
01616             else
01617             {
01618                 /*Codes_SRS_CODEFIRST_02_035: [ Otherwise, CodeFirst_IngestDesiredProperties shall return CODEFIRST_OK. ]*/
01619                 result = CODEFIRST_OK;
01620             }
01621         }
01622     }
01623     return result;
01624 }
01625 
01626