added description of json file structure

This commit is contained in:
Bogdan Pilyugin 2022-09-12 16:10:52 +02:00
parent ee14a37ee4
commit 5f6c4d62b3

View File

@ -1,384 +1,396 @@
/*! Copyright 2022 Bogdan Pilyugin /*! Copyright 2022 Bogdan Pilyugin
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* *
* \file MQTTSysHandler.c * \file MQTTSysHandler.c
* \version 1.0 * \version 1.0
* \date 2022-08-19 * \date 2022-08-19
* \author Bogdan Pilyugin * \author Bogdan Pilyugin
* \brief * \brief
* \details * \details
* \copyright Apache License, Version 2.0 * \copyright Apache License, Version 2.0
*/ */
#include "MQTT.h" /*!
#include "jWrite.h" * Structure of JSON data for system MQTT API
#include "jRead.h" * {
#include "esp_log.h" * "messid":12345, //uint32_t message id for request/response context
#include "romfs.h" * "api":"2.0", //string of current API version
#include "HTTPServer.h" * "request":"GET", //string request type - "GET" or "POST" allowed
* "url":"iotronic.cloud", //string url of resource
#define MESSAGE_LENGTH 32 //base message length, mainly depended by radio requirements * "postdata":"param1=value&param2=value", //string of POST data payload (required if request is POST)
#define MAX_JSON_MESSAGE 256 //max size of mqtt message to publish * "reload":"true" //string "true" or "false" is needed reload page after POST request (required if request is POST)
#define MAX_FILE_PUBLISH 4096 //bufer for mqtt data publish * }
#define MAX_DYNVAR_LENTH 64 */
#define MAX_FILENAME_LENTH 15
#define MAX_ERROR_MESSAGE 32 #include "MQTT.h"
#define MAX_ERROR_JSON 256 #include "jWrite.h"
#define MAX_MESSAGE_ID 15 #include "jRead.h"
#include "esp_log.h"
#define MAX_POST_DATA_LENTH 512 #include "romfs.h"
#include "HTTPServer.h"
#define TAG "MQTTSysHandler"
#define MESSAGE_LENGTH 32 //base message length, mainly depended by radio requirements
const char apiver[] = "2.0"; #define MAX_JSON_MESSAGE 256 //max size of mqtt message to publish
const char tagGet[] = "GET"; #define MAX_FILE_PUBLISH 4096 //bufer for mqtt data publish
const char tagPost[] = "POST"; #define MAX_DYNVAR_LENTH 64
#define MAX_FILENAME_LENTH 15
const char* mqtt_app_err_descr[] = { #define MAX_ERROR_MESSAGE 32
"Operation OK", #define MAX_ERROR_JSON 256
"Internal error", #define MAX_MESSAGE_ID 15
"Wrong json format",
"Key 'idmess' not found", #define MAX_POST_DATA_LENTH 512
"Key 'idmess' value too long",
"Key 'api' not found", #define TAG "MQTTSysHandler"
"API version not supported",
"Key 'request' not found", const char apiver[] = "2.0";
"Unsupported HTTP method", const char tagGet[] = "GET";
"Key 'url' not found", const char tagPost[] = "POST";
"Key 'url' value too long",
"URL not found", const char* mqtt_app_err_descr[] = {
"Key 'postdata' not found", "Operation OK",
"Key 'postdata' too long", "Internal error",
"File size too big", "Wrong json format",
"File is empty", "Key 'idmess' not found",
"Unknown error" "Key 'idmess' value too long",
}; "Key 'api' not found",
"API version not supported",
const char* mqtt_app_err_breef[] = { "Key 'request' not found",
"OK", "Unsupported HTTP method",
"INTERNAL_ERR", "Key 'url' not found",
"WRONG_JSON_ERR", "Key 'url' value too long",
"NO_ID_ERR", "URL not found",
"ID_OVERSIZE_ERR", "Key 'postdata' not found",
"NO_API_ERR", "Key 'postdata' too long",
"VERSION_ERR", "File size too big",
"NO_REQUEST_ERR", "File is empty",
"UNSUPPORTED_METHOD_ERR", "Unknown error"
"NO_URL_ERR", };
"URL_OVERSIZE_ERR",
"URL_NOT_FOUND_ERR", const char* mqtt_app_err_breef[] = {
"NO_POSTDATA_ERR", "OK",
"POSTDATA_OVERSIZE_ERR", "INTERNAL_ERR",
"FILE_OVERSIZE_ERR", "WRONG_JSON_ERR",
"FILE_EMPTY_ERR", "NO_ID_ERR",
"UNKNOWN_ERR" "ID_OVERSIZE_ERR",
}; "NO_API_ERR",
"VERSION_ERR",
typedef enum "NO_REQUEST_ERR",
{ "UNSUPPORTED_METHOD_ERR",
UNSUPPORTED = 0, "NO_URL_ERR",
GET, "URL_OVERSIZE_ERR",
POST "URL_NOT_FOUND_ERR",
} mqtt_api_request_t; "NO_POSTDATA_ERR",
"POSTDATA_OVERSIZE_ERR",
static mqtt_app_err_t ResponceWithError(int idx, "FILE_OVERSIZE_ERR",
espfs_file_t *file, "FILE_EMPTY_ERR",
char *id, "UNKNOWN_ERR"
char *url, };
mqtt_app_err_t api_err)
{ typedef enum
espfs_fclose(file); {
char JSONErrorMess[MAX_ERROR_JSON]; UNSUPPORTED = 0,
jwOpen(JSONErrorMess, MAX_ERROR_JSON, JW_OBJECT, JW_PRETTY); GET,
if (id[0]) POST
jwObj_string("messid", (char*) id); } mqtt_api_request_t;
else
jwObj_string("messid", "ERROR"); static mqtt_app_err_t ResponceWithError(int idx,
jwObj_string("api", (char*) apiver); espfs_file_t *file,
jwObj_string("responce", (char*) mqtt_app_err_breef[api_err]); char *id,
jwObj_string("descript", (char*) mqtt_app_err_descr[api_err]); char *url,
if (url[0]) mqtt_app_err_t api_err)
jwObj_string("url", (char*) url); {
else espfs_fclose(file);
jwObj_string("url", "ERROR"); char JSONErrorMess[MAX_ERROR_JSON];
jwEnd(); jwOpen(JSONErrorMess, MAX_ERROR_JSON, JW_OBJECT, JW_PRETTY);
jwClose(); if (id[0])
char *buf = (char*) malloc(strlen(JSONErrorMess) + 1); jwObj_string("messid", (char*) id);
if (buf) else
{ jwObj_string("messid", "ERROR");
memcpy(buf, JSONErrorMess, strlen(JSONErrorMess)); jwObj_string("api", (char*) apiver);
DATA_SEND_STRUCT DSS; jwObj_string("responce", (char*) mqtt_app_err_breef[api_err]);
ComposeTopic(DSS.topic, jwObj_string("descript", (char*) mqtt_app_err_descr[api_err]);
GetSysConf()->mqttStation[idx].RootTopic, if (url[0])
"UPLINK", jwObj_string("url", (char*) url);
GetSysConf()->mqttStation[idx].ClientID, else
"SYSTEM"); jwObj_string("url", "ERROR");
DSS.raw_data_ptr = buf; jwEnd();
DSS.data_length = strlen(JSONErrorMess); jwClose();
if (xQueueSend(GetMQTTHandlesPool(idx)->mqtt_queue, &DSS, pdMS_TO_TICKS(1000)) == pdPASS) char *buf = (char*) malloc(strlen(JSONErrorMess) + 1);
return API_OK; if (buf)
else {
{ memcpy(buf, JSONErrorMess, strlen(JSONErrorMess));
free(buf); DATA_SEND_STRUCT DSS;
return API_INTERNAL_ERR; ComposeTopic(DSS.topic,
} GetSysConf()->mqttStation[idx].RootTopic,
} "UPLINK",
else GetSysConf()->mqttStation[idx].ClientID,
{ // ERR internal error on publish error "SYSTEM");
return API_INTERNAL_ERR; DSS.raw_data_ptr = buf;
} DSS.data_length = strlen(JSONErrorMess);
} if (xQueueSend(GetMQTTHandlesPool(idx)->mqtt_queue, &DSS, pdMS_TO_TICKS(1000)) == pdPASS)
return API_OK;
static mqtt_app_err_t ResponceWithFile(int idx, espfs_file_t *file, else
char *id, {
char *url) free(buf);
{ return API_INTERNAL_ERR;
struct espfs_stat_t stat; }
char *filebuf = NULL; }
char *outbuf = NULL; else
uint32_t fileLenth, filePtr, readBytes; { // ERR internal error on publish error
espfs_fstat(file, &stat); return API_INTERNAL_ERR;
fileLenth = stat.size; }
mqtt_app_err_t api_err = API_UNKNOWN_ERR; }
if (fileLenth > MAX_FILE_PUBLISH)
{ static mqtt_app_err_t ResponceWithFile(int idx, espfs_file_t *file,
ESP_LOGE(TAG, "File is too big"); char *id,
api_err = API_FILE_OVERSIZE_ERR; char *url)
goto file_send_err; {
} struct espfs_stat_t stat;
outbuf = (char*) malloc(MAX_FILE_PUBLISH + 256); char *filebuf = NULL;
filebuf = (char*) malloc(fileLenth); char *outbuf = NULL;
uint32_t fileLenth, filePtr, readBytes;
if (!outbuf || !filebuf) espfs_fstat(file, &stat);
{ fileLenth = stat.size;
ESP_LOGE(TAG, "Failed to allocate memory"); mqtt_app_err_t api_err = API_UNKNOWN_ERR;
api_err = API_INTERNAL_ERR; if (fileLenth > MAX_FILE_PUBLISH)
goto file_send_err; {
} ESP_LOGE(TAG, "File is too big");
api_err = API_FILE_OVERSIZE_ERR;
if (espfs_fread(file, filebuf, fileLenth) == 0) goto file_send_err;
{ }
ESP_LOGE(TAG, "Read no bytes from file"); outbuf = (char*) malloc(MAX_FILE_PUBLISH + 256);
api_err = API_FILE_EMPTY_ERR; filebuf = (char*) malloc(fileLenth);
goto file_send_err;
} if (!outbuf || !filebuf)
espfs_fclose(file); {
ESP_LOGE(TAG, "Failed to allocate memory");
jwOpen(outbuf, MAX_FILE_PUBLISH, JW_OBJECT, JW_PRETTY); api_err = API_INTERNAL_ERR;
jwObj_string("messid", (char*) id); goto file_send_err;
jwObj_string("api", (char*) apiver); }
jwObj_string("responce", (char*) mqtt_app_err_breef[API_OK]);
jwObj_string("descript", (char*) mqtt_app_err_descr[API_OK]); if (espfs_fread(file, filebuf, fileLenth) == 0)
jwObj_string("url", (char*) url); {
jwObj_string("body", "bodydata"); ESP_LOGE(TAG, "Read no bytes from file");
jwEnd(); api_err = API_FILE_EMPTY_ERR;
jwClose(); goto file_send_err;
}
char *fdata = memmem(outbuf, MAX_FILE_PUBLISH, "\"bodydata\"", strlen("\"bodydata\"")); espfs_fclose(file);
filePtr = 0;
readBytes = 0; jwOpen(outbuf, MAX_FILE_PUBLISH, JW_OBJECT, JW_PRETTY);
while (filePtr < fileLenth && readBytes < (MAX_FILE_PUBLISH - MAX_DYNVAR_LENTH)) jwObj_string("messid", (char*) id);
{ jwObj_string("api", (char*) apiver);
if (filebuf[filePtr] == '~') jwObj_string("responce", (char*) mqtt_app_err_breef[API_OK]);
{ jwObj_string("descript", (char*) mqtt_app_err_descr[API_OK]);
int k = 0; jwObj_string("url", (char*) url);
char DynVarName[16]; jwObj_string("body", "bodydata");
while (filePtr < fileLenth && k < 16 && (filebuf[++filePtr] != '~')) jwEnd();
DynVarName[k++] = filebuf[filePtr]; jwClose();
if (filebuf[filePtr] == '~')
{ //got valid dynamic variable name char *fdata = memmem(outbuf, MAX_FILE_PUBLISH, "\"bodydata\"", strlen("\"bodydata\""));
DynVarName[k] = 0x00; filePtr = 0;
readBytes += HTTPPrint(NULL, &fdata[readBytes], DynVarName); readBytes = 0;
filePtr++; while (filePtr < fileLenth && readBytes < (MAX_FILE_PUBLISH - MAX_DYNVAR_LENTH))
} {
} if (filebuf[filePtr] == '~')
else {
fdata[readBytes++] = filebuf[filePtr++]; int k = 0;
} char DynVarName[16];
const char tail[] = "}"; while (filePtr < fileLenth && k < 16 && (filebuf[++filePtr] != '~'))
strcat((fdata + readBytes), tail); DynVarName[k++] = filebuf[filePtr];
free(filebuf); if (filebuf[filePtr] == '~')
DATA_SEND_STRUCT DSS; { //got valid dynamic variable name
ComposeTopic(DSS.topic, DynVarName[k] = 0x00;
GetSysConf()->mqttStation[idx].RootTopic, readBytes += HTTPPrint(NULL, &fdata[readBytes], DynVarName);
"UPLINK", filePtr++;
GetSysConf()->mqttStation[idx].ClientID, }
"SYSTEM"); }
DSS.raw_data_ptr = outbuf; else
DSS.data_length = (fdata - outbuf) + readBytes + strlen(tail); fdata[readBytes++] = filebuf[filePtr++];
if (xQueueSend(GetMQTTHandlesPool(idx)->mqtt_queue, &DSS, pdMS_TO_TICKS(1000)) == pdPASS) }
return API_OK; const char tail[] = "}";
else strcat((fdata + readBytes), tail);
{ free(filebuf);
ESP_LOGE(TAG, "Failed to write mqtt queue"); DATA_SEND_STRUCT DSS;
api_err = API_INTERNAL_ERR; ComposeTopic(DSS.topic,
goto file_send_err; GetSysConf()->mqttStation[idx].RootTopic,
} "UPLINK",
file_send_err: GetSysConf()->mqttStation[idx].ClientID,
free(outbuf); "SYSTEM");
free(filebuf); DSS.raw_data_ptr = outbuf;
return api_err; DSS.data_length = (fdata - outbuf) + readBytes + strlen(tail);
} if (xQueueSend(GetMQTTHandlesPool(idx)->mqtt_queue, &DSS, pdMS_TO_TICKS(1000)) == pdPASS)
return API_OK;
void SystemDataHandler(char *data, uint32_t len, int idx) else
{ {
struct jReadElement result; ESP_LOGE(TAG, "Failed to write mqtt queue");
char URL[MAX_FILENAME_LENTH + 1]; api_err = API_INTERNAL_ERR;
char ID[MAX_MESSAGE_ID + 1]; goto file_send_err;
char POST_DATA[MAX_POST_DATA_LENTH + 1]; }
mqtt_api_request_t req = UNSUPPORTED; file_send_err:
mqtt_app_err_t api_err = API_UNKNOWN_ERR; free(outbuf);
ID[0] = 0x00; free(filebuf);
URL[0] = 0x00; return api_err;
espfs_file_t *file = NULL; }
jRead(data, "", &result); void SystemDataHandler(char *data, uint32_t len, int idx)
if (result.dataType == JREAD_OBJECT) {
{ struct jReadElement result;
/*Checking and get ID message*/ char URL[MAX_FILENAME_LENTH + 1];
jRead(data, "{'messid'", &result); char ID[MAX_MESSAGE_ID + 1];
if (result.elements == 1) char POST_DATA[MAX_POST_DATA_LENTH + 1];
{ mqtt_api_request_t req = UNSUPPORTED;
if (result.bytelen > MAX_MESSAGE_ID) mqtt_app_err_t api_err = API_UNKNOWN_ERR;
{ ID[0] = 0x00;
api_err = API_ID_OVERSIZE_ERR; URL[0] = 0x00;
goto api_json_err; espfs_file_t *file = NULL;
}
memcpy(ID, result.pValue, result.bytelen); jRead(data, "", &result);
ID[result.bytelen] = 0x00; if (result.dataType == JREAD_OBJECT)
} {
else /*Checking and get ID message*/
{ jRead(data, "{'messid'", &result);
api_err = API_NO_ID_ERR; if (result.elements == 1)
goto api_json_err; {
} if (result.bytelen > MAX_MESSAGE_ID)
{
/*Checking and get API version*/ api_err = API_ID_OVERSIZE_ERR;
jRead(data, "{'api'", &result); goto api_json_err;
if (result.elements == 1) }
{ memcpy(ID, result.pValue, result.bytelen);
if (memcmp(apiver, result.pValue, result.bytelen)) ID[result.bytelen] = 0x00;
{ }
api_err = API_VERSION_ERR; else
goto api_json_err; {
} api_err = API_NO_ID_ERR;
} goto api_json_err;
else }
{
api_err = API_NO_API_ERR; /*Checking and get API version*/
goto api_json_err; jRead(data, "{'api'", &result);
} if (result.elements == 1)
{
/*Checking and get request type POST, GET or UNSUPPORTED*/ if (memcmp(apiver, result.pValue, result.bytelen))
jRead(data, "{'request'", &result); {
if (result.elements == 1) api_err = API_VERSION_ERR;
{ goto api_json_err;
if (!memcmp(tagGet, result.pValue, result.bytelen)) }
req = GET; }
else if (!memcmp(tagPost, result.pValue, result.bytelen)) else
req = POST; {
} api_err = API_NO_API_ERR;
else goto api_json_err;
{ }
api_err = API_NO_REQUEST_ERR;
goto api_json_err; /*Checking and get request type POST, GET or UNSUPPORTED*/
} jRead(data, "{'request'", &result);
if (result.elements == 1)
/*Checking and get url*/ {
jRead(data, "{'url'", &result); if (!memcmp(tagGet, result.pValue, result.bytelen))
if (result.elements == 1) req = GET;
{ else if (!memcmp(tagPost, result.pValue, result.bytelen))
if (result.bytelen > MAX_FILENAME_LENTH) req = POST;
{ }
api_err = API_URL_OVERSIZE_ERR; else
goto api_json_err; {
} api_err = API_NO_REQUEST_ERR;
memcpy(URL, result.pValue, result.bytelen); goto api_json_err;
URL[result.bytelen] = 0x00; }
file = espfs_fopen(fs, URL);
if (!file) /*Checking and get url*/
{ jRead(data, "{'url'", &result);
api_err = API_URL_NOT_FOUND_ERR; if (result.elements == 1)
goto api_json_err; {
} if (result.bytelen > MAX_FILENAME_LENTH)
{
} api_err = API_URL_OVERSIZE_ERR;
else goto api_json_err;
{ }
api_err = API_NO_URL_ERR; memcpy(URL, result.pValue, result.bytelen);
goto api_json_err; URL[result.bytelen] = 0x00;
} file = espfs_fopen(fs, URL);
if (!file)
if (req == POST) {
{ api_err = API_URL_NOT_FOUND_ERR;
/*Checking and get POST DATA*/ goto api_json_err;
jRead(data, "{'postdata'", &result); }
if (result.elements == 1)
{ }
if (result.bytelen > MAX_POST_DATA_LENTH) else
{ {
api_err = API_POSTDATA_OVERSIZE_ERR; api_err = API_NO_URL_ERR;
goto api_json_err; goto api_json_err;
} }
memcpy(POST_DATA, result.pValue, result.bytelen);
POST_DATA[result.bytelen] = 0x00; if (req == POST)
} {
else /*Checking and get POST DATA*/
{ jRead(data, "{'postdata'", &result);
api_err = API_NO_POSTDATA_ERR; if (result.elements == 1)
goto api_json_err; {
} if (result.bytelen > MAX_POST_DATA_LENTH)
{
HTTPPostApp(NULL, URL, POST_DATA); api_err = API_POSTDATA_OVERSIZE_ERR;
goto api_json_err;
jRead(data, "{'reload'", &result); }
if (result.elements == 1 && !memcmp("true", result.pValue, result.bytelen)) memcpy(POST_DATA, result.pValue, result.bytelen);
{ POST_DATA[result.bytelen] = 0x00;
if (ResponceWithFile(idx, file, ID, URL) == API_OK) }
return; else
else {
goto api_json_err; api_err = API_NO_POSTDATA_ERR;
} goto api_json_err;
else }
{
if (ResponceWithError(idx, file, ID, URL, API_OK) != API_OK) HTTPPostApp(NULL, URL, POST_DATA);
ESP_LOGE(TAG, "Failed to allocate memory for file MQTT message");
} jRead(data, "{'reload'", &result);
return; if (result.elements == 1 && !memcmp("true", result.pValue, result.bytelen))
} {
else if (req == GET) if (ResponceWithFile(idx, file, ID, URL) == API_OK)
{ return;
//Here GET handler, send file wrapped into json else
if (ResponceWithFile(idx, file, ID, URL) == API_OK) goto api_json_err;
return; }
else else
goto api_json_err; {
} if (ResponceWithError(idx, file, ID, URL, API_OK) != API_OK)
else ESP_LOGE(TAG, "Failed to allocate memory for file MQTT message");
{ }
api_err = API_UNSUPPORTED_METHOD_ERR; return;
goto api_json_err; }
} else if (req == GET)
} {
else //Here GET handler, send file wrapped into json
{ //ERR no json format if (ResponceWithFile(idx, file, ID, URL) == API_OK)
api_err = API_WRONG_JSON_ERR; return;
goto api_json_err; else
} goto api_json_err;
}
api_json_err: else
ESP_LOGE(TAG, "ERROR:%s:%s", mqtt_app_err_breef[api_err], mqtt_app_err_descr[api_err]); {
if (ResponceWithError(idx, file, ID, URL, api_err) != API_OK) api_err = API_UNSUPPORTED_METHOD_ERR;
ESP_LOGE(TAG, "Failed to allocate memory for file MQTT message"); goto api_json_err;
} }
}
else
{ //ERR no json format
api_err = API_WRONG_JSON_ERR;
goto api_json_err;
}
api_json_err:
ESP_LOGE(TAG, "ERROR:%s:%s", mqtt_app_err_breef[api_err], mqtt_app_err_descr[api_err]);
if (ResponceWithError(idx, file, ID, URL, api_err) != API_OK)
ESP_LOGE(TAG, "Failed to allocate memory for file MQTT message");
}