Axeda Corp
/
AxedaGo-ubloxC027
Axeda demo software for u-blox C027 (GSM)
AMMP/axPlatform.c
- Committer:
- AxedaCorp
- Date:
- 2014-08-11
- Revision:
- 1:ff6d8adaf6b9
- Parent:
- 0:a725e8eab383
File content as of revision 1:ff6d8adaf6b9:
/************************************************************************************/ /* axPlatform.c */ /* 2013 Axeda Corporation */ /* */ /* Defines methods for interaction with end points on the Axeda Platform. This */ /* is a device independent implementation which can be applied to any device that */ /* supports ANSI C. */ /************************************************************************************/ #include "axPlatform.h" #include "axStatusCodes.h" #include <string.h> #include <stdlib.h> #include "axTransport.h" #include "axSerializer.h" #include "cJSON.h" #include "axConstants.h" #include "axHTTP.h" int handleResponse(HTTP_Transmission *response); static const char *endpoints[8]= { "ammp", "data", "assets", "files", " ", "packages", "status", " "}; static const char *contentTypes[]= { "application/json", "multipart/form-data"}; static const char *buffer_keywords[]= {"alarms", "events", "data", "locations" }; const char URL_SEP='/'; const char ID_SEP='!'; /************************************************************************************/ /*ax_platform_upload() */ /* */ /*file(required) : a pointer to a structure that the data will be stored in. */ /*name (required): a file name for the file. Can be arbitrarily assigned based on */ /* source of the data. */ /*hint(required): a string that allows you to tag the file for later retrieval */ /*size(optional): should be populated if the data does not have an EOF character at */ /* the end. If sending a memory block this will define the offset. */ /*data(required): a pointer to the data that will be sent as this file */ /* */ /* */ /************************************************************************************/ int ax_platform_upload(ax_platform *cloud, ax_deviceID *device, ax_file *file) { int retVal=AX_UNKNOWN; char *url=NULL; HTTP_Part fileDat; HTTP_Transmission response; fileDat.data=file->data; fileDat.data_sz=file->size; fileDat.file_name=file->name; fileDat.hint=file->hint; fileDat.next=NULL; url = getFileResource(url, device); int body_sz=strlen(file->hint)+12+strlen(file->name); char *body=malloc(sizeof(char)*body_sz); int bodyWrt=snprintf(body, body_sz, "name=\"hint\" \r\n %s",file->hint); if(bodyWrt>body_sz) { retVal=AX_GEN_STR_TRUNC; } printDebug(body); retVal=http_send_mpost(url, cloud->hostname, cloud->port, cloud->secure, NULL, NULL, 0, &fileDat, 1, &response); free(body); free(url); return retVal; } /************************************************************************************/ /* ax_platform_ping() */ /* */ /* This function creates a ping message which acts as the device heartbeat. */ /* */ /* */ /************************************************************************************/ int ax_platform_ping(ax_platform *cloud, ax_deviceID *device) { int retVal=AX_UNKNOWN; char *url=NULL; HTTP_Headers myHeader; HTTP_Transmission response; myHeader.contentType=getGeneralType(myHeader.contentType); url=getAgentResource(url, device); //Send the request retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, NULL, 0, &response); //Scan the response and act if there are egress messages. handleResponse(&response); free(url); return retVal; } /************************************************************************************/ /*ax_platform_download() */ /* */ /*This function wil request a file from the platform with a specific file ID. The */ /*fileID itself will be transmitted down on a response message from the platform. */ /* */ /* */ /************************************************************************************/ int ax_platform_download(ax_platform *cloud, ax_deviceID *device, ax_package_instruction *instr) { int retVal = AX_UNKNOWN; char *url=NULL; HTTP_Transmission response; url=getFileDownloadResource(url, device, instr->file_id); retVal=http_send_get(url, cloud->hostname, cloud->port, cloud->secure, &response); //TODO: revisit what to do with response in this case. scm_file_write(instr->filename, response.body, response.body_length, instr); free(url); return retVal; } /************************************************************************************/ /* ax_platform_register() */ /* */ /*This function creates a registration message to be sent to the cloud. The message */ /*contains information about the model, serial and expected ping interval. It also */ /*allows the cloud to mark the device as online. If using the standalone thread func*/ /*-tion this method will be called automatically until successful. */ /* */ /*cloud(required) : a pointer to a structure that contains data about the cloud */ /*device(required) : a pointer to a structure that contains the model and serial */ /*pingRate(required) : an integer in milliseconds which tells the cloud how often to*/ /* expect a heartbeat ping */ /* */ /*Returns: an integer value indicating whether or not it successfully registered */ /************************************************************************************/ int ax_platform_register(ax_platform *cloud, ax_deviceID *device) { int retVal=AX_UNKNOWN; char *url=NULL; char *fullRequest=NULL; cJSON *requestBody; HTTP_Headers myHeader; HTTP_Transmission response; myHeader.contentType=getGeneralType(myHeader.contentType); url = getRegistrationResource(url, device); int urlLength=strlen(url); url[urlLength]='\0'; requestBody=getRegistrationJSON(device, cloud->ping_rate); fullRequest=cJSON_PrintUnformatted(requestBody); int contentLength=strlen(fullRequest); retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, contentLength, &response); //Scan the response and act if there are egress messages. handleResponse(&response); //Clean up after ourselves free(fullRequest); free(url); cJSON_Delete(requestBody); return retVal; } /**************************************************************************************/ /*ax_platform_send() */ /* */ /*This function sends any data available in the structures directly to the cloud in */ /*an on demand fashion. */ /* */ /* */ /*cloud(required) : a pointer to a structure that contains the cloud service info */ /*device(required) : a pointer to a structure that contains device information */ /*dataItems(optional) : a pointer to an array of ax_dataItem pointers that will be sent*/ /*alarms(optional) : a pointer to an array of ax_alarm pointers that will be sent */ /*events(optional) : a pointer to an array of ax_event pointers that will be sent */ /*locations(optional) : a pointer to an array of ax_location pointers that will be sent*/ /* */ /*Returns: an integer value indicating whether or not it successfully sent the data */ /**************************************************************************************/ int ax_platform_send(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets, ax_alarm *alarms[], int numAlarms, ax_event *events[], int numEvents, ax_location *locations[], int numLocations) { int retVal=AX_UNKNOWN; HTTP_Headers myHeader; char *url=NULL; char *fullRequest=NULL; cJSON *rootNode=NULL; rootNode=cJSON_CreateObject(); HTTP_Transmission response; zeroOutHTTPXmission(&response); //add the data in the DataItems queue if passed if(dataItems){ cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_DATA], dataSet2JSON((ax_dataSet **)dataItems, numDataSets, terse_enable)); } //add all alarms to the transmission if passed if(alarms){ cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_ALARMS], AlarmsToJSON((ax_alarm **)alarms, numAlarms, terse_enable)); } if(events){ cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_EVENTS], eventsToJSON((ax_event **)events, numEvents, terse_enable)); } if(locations){ cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_LOCATIONS], locationsToJSON((ax_location **)locations, numLocations, terse_enable)); } myHeader.contentType=getGeneralType(myHeader.contentType); url = getDataResource(url, device); fullRequest=cJSON_PrintUnformatted(rootNode); retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, strlen(fullRequest), &response); //Scan the response and act if there are egress messages. handleResponse(&response); if(fullRequest) { free(fullRequest); } free(url); cJSON_Delete(rootNode); return retVal; } /**************************************************************************************/ /*ax_platform_sendData() */ /* */ /*This function immedately sends any data included in the parameter to the platform */ /*listed. It makes use of the ax_platform_send() function as a special case. */ /* */ /*cloud(required) : a pointer to a structure that contains the cloud service info */ /*device(required) : a pointer to a structure that contains device information */ /*dataItems(required) : pointer to an array of ax_dataItem pointers that will be sent */ /* */ /*Returns: an integer value indicating whether or not it successfully sent the data */ /**************************************************************************************/ int ax_platform_sendData(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets) { return ax_platform_send(cloud, device, dataItems, numDataSets, NULL, 0, NULL, 0, NULL, 0); } /**************************************************************************************/ /*ax_platform_sendAlarms() */ /* */ /*This function immedately sends any alarms included in the parameter to the platform */ /*listed. It makes use of the ax_platform_send() function as a special case. */ /* */ /*cloud(required) : a pointer to a structure that contains the cloud service info */ /*device(required) : a pointer to a structure that contains device information */ /*alarms(required) : a pointer to an array of ax_alarm pointers that will be sent */ /* */ /*Returns: an integer value indicating whether or not it successfully sent the data */ /**************************************************************************************/ int ax_platform_sendAlarms(ax_platform *cloud, ax_deviceID *device, ax_alarm *alarms[], int numAlarms) { return ax_platform_send(cloud, device, NULL, 0, alarms, numAlarms, NULL, 0, NULL, 0); } /**************************************************************************************/ /*ax_platform_sendEvents() */ /* */ /*This function immedately sends any events included in the parameter to the platform */ /*listed. It makes use of the ax_platform_send() function as a special case. */ /* */ /*cloud(required) : a pointer to a structure that contains the cloud service info */ /*device(required) : a pointer to a structure that contains device information */ /*events(required) : a pointer to an array of ax_event pointers that will be sent */ /* */ /*Returns: an integer value indicating whether or not it successfully sent the data */ /**************************************************************************************/ int ax_platform_sendEvents(ax_platform *cloud, ax_deviceID *device, ax_event *events[], int numEvents) { return ax_platform_send(cloud, device, NULL, 0, NULL, 0, events, numEvents, NULL, 0); } /**************************************************************************************/ /*ax_platform_sendLocations() */ /* */ /*This function immedately sends any events included in the parameter to the platform */ /*listed. It makes use of the ax_platform_send() function as a special case. */ /* */ /*cloud(required) : a pointer to a structure that contains the cloud service info */ /*device(required) : a pointer to a structure that contains device information */ /*locations(required) : a pointer to an array of ax_location poitners that will be sent*/ /* */ /*Returns: an integer value indicating whether or not it successfully sent the data */ /**************************************************************************************/ int ax_platform_sendLocations(ax_platform *cloud, ax_deviceID *device, ax_location *locations[], int numLocations) { return ax_platform_send(cloud, device, NULL, 0, NULL, 0, NULL, 0, locations, numLocations); } /**************************************************************************************/ /*ax_platform_setPkgStatus() */ /* */ /*Tells the platform what state the package is currently in. This is necessary for the*/ /*lifecycle of the packages in the Axeda System. */ /* */ /**************************************************************************************/ int ax_platform_setPkgStatus(ax_platform *cloud, ax_deviceID *device, char *pkgID, int status, char *errorMsg) { int retVal=AX_UNKNOWN; char *url=NULL; char *fullRequest=NULL; cJSON *requestBody=NULL; HTTP_Headers myHeader; HTTP_Transmission response; zeroOutHTTPXmission(&response); requestBody= getPKGStatusJSON(status, errorMsg, AX_NO_PRIORITY, 0, terse_enable); myHeader.contentType=getGeneralType(myHeader.contentType); url=getPackageUpdateResource(url, device, pkgID); int urlLength=strlen(url); url[urlLength]='\0'; fullRequest=cJSON_PrintUnformatted(requestBody); int contentLength=strlen(fullRequest); //Send the status retVal=http_send_put(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, contentLength, &response); //Scan the response and act if there are egress messages. handleResponse(&response); //Clean up after ourselves free(fullRequest); free(url); cJSON_Delete(requestBody); return retVal; } /**************************************************************************************/ /*ax_platform_setPkgStatus() */ /* */ /*Tells the platform what state the package is currently in. This is necessary for the*/ /*lifecycle of the packages in the Axeda System. */ /* */ /**************************************************************************************/ int ax_platform_setPkgStatusStarted(ax_platform *cloud, ax_deviceID *device, char *pkgID) { return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_STARTED, NULL); } /**************************************************************************************/ /*ax_platform_setPkgStatus() */ /* */ /*Tells the platform what state the package is currently in. This is necessary for the*/ /*lifecycle of the packages in the Axeda System. */ /* */ /**************************************************************************************/ int ax_platform_setPkgStatusQueued(ax_platform *cloud, ax_deviceID *device, char *pkgID) { return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_QUEUED, NULL); } /**************************************************************************************/ /*ax_platform_setPkgStatus() */ /* */ /*Tells the platform what state the package is currently in. This is necessary for the*/ /*lifecycle of the packages in the Axeda System. */ /* */ /**************************************************************************************/ int ax_platform_setPkgStatusSuccess(ax_platform *cloud, ax_deviceID *device, char *pkgID) { return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_SUCCESS, NULL); } /**************************************************************************************/ /*ax_platform_setPkgStatus() */ /* */ /*Tells the platform what state the package is currently in. This is necessary for the*/ /*lifecycle of the packages in the Axeda System. */ /* */ /**************************************************************************************/ int ax_platform_setPkgStatusFailed(ax_platform *cloud, ax_deviceID *device, char *pkgID, char *errorMsg) { return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_FAILURE, errorMsg); } /***************************************************************************************************************************************************************/ /***************************************************************************************************************************************************************/ /*The code in the following sections should never need to be called as it will be called from the preceeding functions. */ /***************************************************************************************************************************************************************/ /***************************************************************************************************************************************************************/ /************************************************************************************/ /* The following methods construct references to endpoints on the platform where the */ /*respective data will be sent. For the sake of convenience they all return a pointer*/ /*that is equal in value to that which was passed in. */ /* They all take the same arguments: */ /* */ /* endPointBuff = The pointer to where the endpoint will be stored. This pointer */ /* will be allocated and populated by the function */ /* ax_deviceID = A structure that contains the model and serial number of the current*/ /* device. This structure should be the same as the one constructed at */ /* startup based on the arguments. */ /************************************************************************************/ char *getDataResource(char *endPointBuff, ax_deviceID *device) { return getResource(endPointBuff, device, END_DATA); } char *getAgentResource(char *endPointBuff, ax_deviceID *device) { return getResource(endPointBuff, device, END_AGENTS); } char *getFileResource(char *endPointBuff, ax_deviceID *device) { return getResource(endPointBuff, device, END_FILES); } char *getRegistrationResource(char *endPointBuff, ax_deviceID *device) { return getResource(endPointBuff, device, END_REG); } char *getFileDownloadResource(char *endPointBuff, ax_deviceID *device, char *fileID) { //since this url is slightly different from all the others we need to override the standard passing. char *tempBuff=NULL; tempBuff=encodeAgentID(device, tempBuff); int len=strlen(endpoints[END_BASE])+strlen(endpoints[END_PACKAGES])+6+strlen(tempBuff)+strlen(fileID)+strlen(endpoints[END_FILES])+1+strlen(PROTOCOL_VERSION)+1; endPointBuff = (char *)calloc(len,sizeof(char)); // /ammp/packages/1/files/test_model!test_serial/12345 // , URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_FILED], URL_SEP, PROTOCOL_VERSION, URL_SEP, endpoints[END_FILES], URL_SEP, tempBuff=encodeAgentID(device, tempBuff), URL_SEP, fileID // %c%s%c%s%c%s%c%s%c%s%c%s snprintf(endPointBuff, len, "%c%s%c%s%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_PACKAGES], URL_SEP, PROTOCOL_VERSION, URL_SEP, endpoints[END_FILES], URL_SEP, tempBuff, URL_SEP, fileID ); free(tempBuff); return endPointBuff; } char *getPackageUpdateResource(char *endPointBuff, ax_deviceID *device, char *packageID){ //since this url is slightly different from all the others we need to override the standard passing. // "/ammp/packages/1/12345/status/test_model!test_serial" char *tempBuff=NULL; tempBuff=encodeAgentID(device, tempBuff); int len=6+strlen(endpoints[END_BASE])+strlen(endpoints[END_PACKAGES])+strlen(PROTOCOL_VERSION)+strlen(packageID)+strlen(endpoints[END_STATUS])+strlen(tempBuff)+1; endPointBuff = (char *)calloc(len, sizeof(char)); snprintf(endPointBuff, len, "%c%s%c%s%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_PACKAGES], URL_SEP, PROTOCOL_VERSION, URL_SEP, packageID, URL_SEP, endpoints[END_STATUS], URL_SEP, tempBuff); free(tempBuff); return endPointBuff; } char *getResource(char *endPointBuff, ax_deviceID *device, int resourceType) { //Check for bounds on the endpoints array. END_REG is currently the highest, if it's OOB return the agent default URL int resType=END_AGENTS; if((resourceType<0)||(resourceType>END_REG)) { resType=END_AGENTS; } else { resType=resourceType; } int len = strlen(endpoints[END_BASE])+strlen(endpoints[resType])+6+strlen(device->model)+strlen(device->serial); endPointBuff = (char *)calloc(len+1, sizeof(char)); char *tempBuff=NULL; switch(resType) { case END_REG: snprintf(endPointBuff, len+1, "%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_AGENTS], URL_SEP, PROTOCOL_VERSION); break; case END_FILED: break; default: snprintf(endPointBuff, len+1, "%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[resType], URL_SEP, PROTOCOL_VERSION, URL_SEP, tempBuff=encodeAgentID(device, tempBuff)); free(tempBuff); //only used for storing the deviceID it's work is done now. break; } return endPointBuff; } /**************************************************************************************/ /*encodeAgentID() */ /* */ /*A utility method to convert the ax_deviceID structure into a protocol compliant */ /*identifier string. Used mostly by the registration message */ /* */ /*device(required) : a pointer to a structure that contains the model, serial, tenant*/ /*buff(required) : a pointer to a char array where the output will be stored. */ /* */ /*Returns: a pointer to the char array, same as *buff */ /**************************************************************************************/ char* encodeAgentID(ax_deviceID *device, char *buff) { //TODO: Add logic to URL Encode output string if special characters are encountered... int len=0; if(strlen(device->tenant)>0) { len=strlen(device->model)+strlen(device->serial)+strlen(device->tenant)+4; buff = (char *)calloc(len, sizeof(char)); //buff[len]='\0'; snprintf(buff, len, "%s@%s!%s", device->model, device->tenant, device->serial ); } else { len=strlen(device->model)+strlen(device->serial)+3; buff = (char *)calloc(len, sizeof(char)); //buff[len]='\0'; snprintf(buff,len, "%s!%s", device->model, device->serial); } printDebug(buff); return buff; } /**************************************************************************************/ /*getContentType() */ /* */ /*A convenience method to allow different messages to have different media types. The */ /*media types are set forth in the protocol specification. The function will return a */ /*a pointer to a declared string, for which you do not need to free. */ /* */ /* */ /* */ /**************************************************************************************/ char *getContentType(char *contentBuff, int type) { switch(type) { case 0: contentBuff=(char *)contentTypes[MIME_JSON]; break; default: contentBuff=(char *)contentTypes[MIME_JSON]; break; } return contentBuff; } char *getGeneralType(char *contentBuff) { return getContentType(contentBuff, TRANS_TYPE_JSON); } /**************************************************************************************/ /*int handleResponse() */ /* */ /*A catch-all method that will handle any egress messages sent from the platform back */ /*to the device. If the JSON returned is valid it will dispatch the requests as needed*/ /* */ /* */ /* */ /**************************************************************************************/ //Possible returns: // AX_EGR_JSON_PARSE_FAIL -- A failure to parse the JSON that was returned int handleResponse(HTTP_Transmission *response){ cJSON *resp_int, *temp, *temp_child, *instructions, *instruction_child; int retVal=AX_OK; if(response->body==NULL) {return AX_OK;} //if there's no body, there's nothing to do here. resp_int=cJSON_Parse(response->body); if(resp_int==NULL) { return AX_GEN_PARSE_ERR; } printDebug("Creating software packages\n"); temp=cJSON_GetObjectItem(resp_int, "packages"); if(temp!=NULL) { //Handle a software package here int ctr=0, pkg_ctr2=0; int sz=cJSON_GetArraySize(temp); int instructionCt=0; ax_package *temp_package; for(ctr=0; ctr<sz; ctr++) { printDebug("Parsing package[]...\n"); temp_child=cJSON_GetArrayItem(temp, ctr); //Create an ax_package for this temp_package=(ax_package *)malloc(sizeof(ax_package)); char *pkgID=cJSON_GetObjectItem(temp_child, "id")->valuestring; retVal=ax_pkg_createPackage(temp_package, pkgID, 0, AX_NO_PRIORITY); temp_package->priority=cJSON_GetObjectItem(temp_child, "priority")->valueint; temp_package->time=cJSON_GetObjectItem(temp_child, "time")->valueint; //add Instructions to the package instructions=cJSON_GetObjectItem(temp_child, "instructions"); instructionCt=cJSON_GetArraySize(instructions); for(pkg_ctr2=0; pkg_ctr2<instructionCt; pkg_ctr2++) { //int instrType=-1; printDebug("Parsing instruction\n"); instruction_child=cJSON_GetArrayItem(instructions, pkg_ctr2); if(strcmp("down", cJSON_GetObjectItem(instruction_child, "@type")->valuestring)==0) { cJSON *id=cJSON_GetObjectItem(instruction_child, "id"); char *s_id = NULL; if(id != NULL){ s_id=id->valuestring; } char *s_path = NULL; //path is optional so we need to make sure it is here before trying to get the valuestring cJSON *path = cJSON_GetObjectItem(instruction_child, "fp"); if(path != NULL){ s_path=path->valuestring; } else{ path = cJSON_GetObjectItem(instruction_child, "path"); if(path != NULL){ s_path=path->valuestring; } } char *s_filename = NULL; cJSON *fn = cJSON_GetObjectItem(instruction_child, "fn"); if(fn != NULL){ s_filename=fn->valuestring; } else{ path = cJSON_GetObjectItem(instruction_child, "filename"); if(path != NULL){ s_path=path->valuestring; } } retVal=ax_pkg_addDLInstruction(temp_package, s_id, s_path, s_filename); } //if it's not a download instruction, we don't care; down was the only one available in AMMP1.0 } //Now we tell someone that we've got a software package scm_download_req(temp_package); } } int dictr1=0; //Set Data Item Commands will occur here. temp=NULL; temp_child=NULL; instructions=NULL; instruction_child=NULL; printDebug("Setting Data Item Commands\n"); temp=cJSON_GetObjectItem(resp_int, "set"); if(temp!=NULL){ int disz=cJSON_GetArraySize(temp); for(dictr1=0; dictr1<disz; dictr1++) { temp_child=cJSON_GetArrayItem(temp, dictr1); instructions=cJSON_GetObjectItem(temp_child, "dataItems"); instruction_child=instructions->child; while(instruction_child!=NULL){ if(instruction_child->type==cJSON_String) { data_item_write(instruction_child->string, instruction_child->valuestring, 0, AX_STRING); } else if(instruction_child->type==cJSON_False) { data_item_write(instruction_child->string, NULL, AX_FALSE, AX_DIGITAL); } else if(instruction_child->type==cJSON_True) { data_item_write(instruction_child->string, NULL, AX_TRUE, AX_DIGITAL); } else { data_item_write(instruction_child->string, NULL, instruction_child->valuedouble, AX_ANALOG); } instruction_child=instruction_child->next; } } } //Data Item Poll requests here. printDebug("Setting Data Item Poll requests\n"); int dictr2=0; temp=NULL; temp_child=NULL; instructions=NULL; instruction_child=NULL; temp=cJSON_GetObjectItem(resp_int, "send"); if(temp!=NULL){ int dissz=cJSON_GetArraySize(temp); for(dictr2=0; dictr2<dissz; dictr2++) { temp_child=cJSON_GetArrayItem(temp, dictr2); instructions=cJSON_GetObjectItem(temp_child, "dataItems"); instruction_child=instructions->child; while(instruction_child!=NULL){ data_item_request(instruction_child->valuestring); instruction_child=instruction_child->next; } } } cJSON_Delete(resp_int);//dispose of the cJSON object free(response->body); return retVal; }