mirror of
https://github.com/anklimov/lighthub
synced 2025-12-06 03:39:49 +03:00
1199 lines
56 KiB
C++
1199 lines
56 KiB
C++
#ifndef MBUS_DISABLE
|
||
|
||
#include "modules/out_modbus.h"
|
||
#include "Arduino.h"
|
||
#include "options.h"
|
||
#include "utils.h"
|
||
#include "Streaming.h"
|
||
|
||
#include "item.h"
|
||
#include <ModbusMaster.h>
|
||
#include "main.h"
|
||
#include <HardwareSerial.h>
|
||
|
||
extern aJsonObject *modbusObj;
|
||
extern ModbusMaster node;
|
||
extern short modbusBusy;
|
||
extern void modbusIdle(void) ;
|
||
uint32_t mbusSlenceTimer = 0;
|
||
|
||
struct reg_t
|
||
{
|
||
const char verb[4];
|
||
const uint8_t id;
|
||
};
|
||
|
||
/*
|
||
struct serial_t
|
||
{
|
||
const char verb[4];
|
||
const serialParamType mode;
|
||
};
|
||
*/
|
||
|
||
#define PAR_I16 1
|
||
#define PAR_I32 2
|
||
#define PAR_U16 3
|
||
#define PAR_U32 4
|
||
#define PAR_I8H 5
|
||
#define PAR_I8L 6
|
||
#define PAR_U8H 7
|
||
#define PAR_U8L 8
|
||
#define PAR_TENS 9
|
||
#define PAR_100 10
|
||
#define PAR_COIL 11
|
||
|
||
|
||
const reg_t regSize_P[] PROGMEM =
|
||
{
|
||
{ "i16", (uint8_t) PAR_I16 },
|
||
{ "i32", (uint8_t) PAR_I32 },
|
||
{ "u16", (uint8_t) PAR_U16 },
|
||
{ "u32", (uint8_t) PAR_U32 },
|
||
{ "i8h", (uint8_t) PAR_I8H },
|
||
{ "i8l", (uint8_t) PAR_I8L },
|
||
{ "u8h", (uint8_t) PAR_U8H },
|
||
{ "u8l", (uint8_t) PAR_U8L },
|
||
{ "x10", (uint8_t) PAR_TENS },
|
||
{ "100", (uint8_t) PAR_100 }
|
||
// { "bit", (uint8_t) PAR_COIL }
|
||
} ;
|
||
#define regSizeNum sizeof(regSize_P)/sizeof(reg_t)
|
||
|
||
/**
|
||
* @brief Меняет порядок байтов в 16-битном числе.
|
||
* @param x Входное число.
|
||
* @return Число с изменённым порядком байтов.
|
||
*/
|
||
uint16_t swap (uint16_t x) {return ((x & 0xff) << 8) | ((x & 0xff00) >> 8);}
|
||
|
||
/**
|
||
* @brief Преобразует строку в тип регистра.
|
||
* @param str Строка с типом регистра.
|
||
* @return Код типа регистра.
|
||
*/
|
||
int str2regSize(char * str)
|
||
{
|
||
for(uint8_t i=0; i<regSizeNum && str;i++)
|
||
if (strcmp_P(str, regSize_P[i].verb) == 0)
|
||
return pgm_read_byte_near(®Size_P[i].id);
|
||
return (int) PAR_I16;
|
||
}
|
||
|
||
//TODO irs etc
|
||
/**
|
||
* @brief Получает имя параметра по номеру регистра.
|
||
* @param parameters JSON-объект с параметрами.
|
||
* @param regnum Номер регистра.
|
||
* @return Имя параметра или NULL.
|
||
*/
|
||
char * getParamNameByReg(aJsonObject * parameters, int regnum)
|
||
{
|
||
if (!parameters) return NULL;
|
||
|
||
aJsonObject * i = parameters->child;
|
||
while (i)
|
||
{
|
||
aJsonObject * regObj = aJson.getObjectItem(i, "reg");
|
||
if (regObj && regObj->type == aJson_Int && regObj->valueint == regnum)
|
||
{
|
||
debugSerial<<F("MBUS: ")<<i->name<<F(" added by num ")<<regnum<<endl;
|
||
return i->name;
|
||
}
|
||
i=i->next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* @brief Проверяет наличие действия в JSON-объекте.
|
||
* @param execObj JSON-объект.
|
||
* @return true, если действие найдено, иначе false.
|
||
*/
|
||
bool haveAction(aJsonObject * execObj)
|
||
{
|
||
aJsonObject * j = execObj->child;
|
||
switch (execObj->type)
|
||
{
|
||
case aJson_Object:
|
||
while (j)
|
||
{
|
||
if (j->name && *j->name && (*j->name != '@')) return true;
|
||
j=j->next;
|
||
}
|
||
break;
|
||
case aJson_Array:
|
||
while (j)
|
||
{
|
||
if (haveAction(j)) return true;
|
||
j=j->next;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @brief Загружает и применяет конфигурацию Modbus.
|
||
* @return true, если конфигурация успешно загружена, иначе false.
|
||
*/
|
||
bool out_Modbus::getConfig()
|
||
{
|
||
// Retrieve and store template values from global modbus settings
|
||
if (!store || !item || !item->itemArg || (item->itemArg->type != aJson_Array) || aJson.getArraySize(item->itemArg)<2 || !modbusObj)
|
||
{
|
||
errorSerial<<F("MBUS: config failed:")<<(bool)store<<F(",")<<(bool)item<<F(",")<<(bool)item->itemArg<<F(",")<<(item->itemArg->type != aJson_Array)<<F(",")<< (aJson.getArraySize(item->itemArg)<2)<<endl;
|
||
return false;
|
||
}
|
||
aJsonObject * templateIdObj = aJson.getArrayItem(item->itemArg, 1);
|
||
if (templateIdObj->type != aJson_String || !templateIdObj->valuestring)
|
||
{
|
||
errorSerial<<F("MBUS: Invalid template.")<<endl;
|
||
return false;
|
||
}
|
||
aJsonObject * templateObj = aJson.getObjectItem(modbusObj, templateIdObj->valuestring);
|
||
if (! templateObj)
|
||
{
|
||
errorSerial<<F("MBUS: Modbus template not found: ")<<templateIdObj->valuestring<<endl;
|
||
return false;
|
||
}
|
||
|
||
aJsonObject * serialParamObj=aJson.getObjectItem(templateObj, "serial");
|
||
if (serialParamObj && serialParamObj->type == aJson_String) store->serialParam = str2SerialParam(serialParamObj->valuestring);
|
||
else store->serialParam = SERIAL_8N1;
|
||
|
||
aJsonObject * baudObj=aJson.getObjectItem(templateObj, "baud");
|
||
if (baudObj && baudObj->type == aJson_Int && baudObj->valueint) store->baud = baudObj->valueint;
|
||
else store->baud = 9600;
|
||
|
||
#if defined (__SAM3X8E__)
|
||
modbusSerial.begin(store->baud, static_cast <USARTClass::USARTModes> (store->serialParam));
|
||
#elif defined (ARDUINO_ARCH_ESP8266)
|
||
modbusSerial.begin(store->baud, static_cast <SerialConfig>(store->serialParam));
|
||
#elif defined (ARDUINO_ARCH_ESP32)
|
||
modbusSerial.begin(store->baud, (store->serialParam),MODBUS_UART_RX_PIN,MODBUS_UART_TX_PIN);
|
||
#else
|
||
modbusSerial.begin(store->baud, (store->serialParam));
|
||
#endif
|
||
|
||
aJsonObject * pollObj=aJson.getObjectItem(templateObj, "poll");
|
||
if (pollObj && pollObj->type == aJson_Object)
|
||
{
|
||
store->pollingRegisters=aJson.getObjectItem(pollObj, "regs");
|
||
store->pollingIrs=aJson.getObjectItem(pollObj, "irs");
|
||
store->pollingCoils=aJson.getObjectItem(pollObj, "coils");
|
||
store->poolingDiscreteIns=aJson.getObjectItem(pollObj, "dins");
|
||
aJsonObject * delayObj= aJson.getObjectItem(pollObj, "delay");
|
||
if (delayObj) store->pollingInterval = delayObj->valueint;
|
||
else store->pollingInterval = 1000;
|
||
|
||
}
|
||
else {store->pollingRegisters=NULL;store->pollingInterval = 1000;store->pollingIrs=NULL;}
|
||
|
||
store->parameters=aJson.getObjectItem(templateObj, "par");
|
||
|
||
// initializing @S where needed
|
||
|
||
if (store->parameters)
|
||
{
|
||
// Creating for parameters where prefetch required
|
||
debugSerial<<F("Adding prefetch regs:")<<endl;
|
||
aJsonObject * i = store->parameters->child;
|
||
while (i)
|
||
{
|
||
aJsonObject * prefetchObj = aJson.getObjectItem(i, "prefetch");
|
||
if (prefetchObj && prefetchObj->type == aJson_Boolean && prefetchObj->valuebool)
|
||
{
|
||
createLastMeasured(i->name);
|
||
}
|
||
i=i->next;
|
||
}
|
||
|
||
debugSerial<<F("Adding referred regs:")<<endl;
|
||
i = store->parameters->child;
|
||
// Creating for parameters used in references from another parameters
|
||
while (i)
|
||
{
|
||
aJsonObject * mapObj = aJson.getObjectItem(i, "map");
|
||
if (mapObj)
|
||
{
|
||
aJsonObject * defObj = aJson.getObjectItem(mapObj, "def");
|
||
if (defObj && defObj->type == aJson_Int)
|
||
{
|
||
createLastMeasured(getParamNameByReg(store->parameters,defObj->valueint));
|
||
}
|
||
|
||
if (defObj && defObj->type == aJson_String)
|
||
{
|
||
createLastMeasured(defObj->valuestring);
|
||
}
|
||
}
|
||
i=i->next;
|
||
}
|
||
debugSerial<<F("Adding regs with actions:")<<endl;
|
||
// Check - if action configured for object and create
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
if (itemParametersObj)
|
||
{
|
||
i = itemParametersObj->child;
|
||
while (i)
|
||
{
|
||
if (haveAction(i)) createLastMeasured(i);
|
||
i=i->next;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return true;
|
||
//store->addr=item->getArg(0);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Создаёт поле для хранения последнего измеренного значения по имени параметра.
|
||
* @param name Имя параметра.
|
||
* @return true, если успешно, иначе false.
|
||
*/
|
||
int out_Modbus::createLastMeasured(char * name)
|
||
{
|
||
if (!name) return false;
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
return createLastMeasured(aJson.getObjectItem(itemParametersObj,name));
|
||
}
|
||
|
||
/**
|
||
* @brief Создаёт поле для хранения последнего измеренного значения по JSON-объекту.
|
||
* @param execObj JSON-объект параметра.
|
||
* @return true, если успешно, иначе false.
|
||
*/
|
||
int out_Modbus::createLastMeasured(aJsonObject * execObj)
|
||
{
|
||
if (!execObj) return false;
|
||
|
||
aJsonObject * markObj = execObj;
|
||
if (execObj->type == aJson_Array)
|
||
{
|
||
markObj = execObj->child;
|
||
//storeLastValue = true;
|
||
}
|
||
if (!markObj) return false;
|
||
|
||
aJsonObject *lastMeasured = aJson.getObjectItem(markObj,"@S");
|
||
if (lastMeasured) return false;
|
||
|
||
debugSerial<<F("MBUS: Add @S: ")<<execObj->name<<endl;
|
||
aJson.addNumberToObject(markObj, "@S", (long) 0);
|
||
|
||
lastMeasured = aJson.getObjectItem(markObj,"@S");
|
||
if (!lastMeasured) return false;
|
||
lastMeasured->subtype |= MB_VALUE_OUTDATED;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @brief Получает объект последнего измеренного значения по имени параметра.
|
||
* @param name Имя параметра.
|
||
* @return JSON-объект или NULL.
|
||
*/
|
||
aJsonObject * out_Modbus::getLastMeasured(char * name)
|
||
{
|
||
if (!name) return NULL;
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
return getLastMeasured (aJson.getObjectItem(itemParametersObj,name));
|
||
}
|
||
|
||
/**
|
||
* @brief Получает объект последнего измеренного значения по JSON-объекту.
|
||
* @param execObj JSON-объект параметра.
|
||
* @return JSON-объект или NULL.
|
||
*/
|
||
aJsonObject * out_Modbus::getLastMeasured(aJsonObject * execObj)
|
||
{
|
||
if (!execObj) return NULL;
|
||
if (execObj->type == aJson_Array) execObj = execObj->child;
|
||
aJsonObject *lastMeasured = aJson.getObjectItem(execObj,"@S");
|
||
if (lastMeasured && lastMeasured->type == aJson_Int) return lastMeasured;
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* @brief Инициализирует канал Modbus и загружает конфигурацию.
|
||
* @return 1 при успехе, 0 при ошибке.
|
||
*/
|
||
int out_Modbus::Setup()
|
||
{
|
||
abstractOut::Setup();
|
||
if (!store) store= (mbPersistent *)item->setPersistent(new mbPersistent);
|
||
if (!store)
|
||
{ errorSerial<<F("MBUS: Out of memory")<<endl;
|
||
return 0;}
|
||
|
||
store->timestamp=millisNZ();
|
||
if (getConfig())
|
||
{
|
||
infoSerial<<F("MBUS: config loaded ")<< item->itemArr->name<<endl;
|
||
setStatus(CST_INITIALIZED);
|
||
return 1;
|
||
}
|
||
else
|
||
{ errorSerial<<F("MBUS: config error")<<endl;
|
||
setStatus(CST_FAILED);
|
||
Stop();
|
||
return 0;
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* @brief Останавливает работу канала Modbus и освобождает ресурсы.
|
||
* @return 1 при успехе.
|
||
*/
|
||
int out_Modbus::Stop()
|
||
{
|
||
debugSerial.print("MBUS: De-Init ");
|
||
debugSerial.println(item->itemArr->name);
|
||
|
||
if (store) delete store;
|
||
item->setPersistent(NULL);
|
||
store = NULL;
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
* @brief Читает данные из Modbus-устройства.
|
||
* @param reg Номер регистра.
|
||
* @param regType Тип регистра.
|
||
* @param count Количество регистров.
|
||
* @return true, если чтение успешно, иначе false.
|
||
*/
|
||
bool readModbus(uint16_t reg, int regType, int count)
|
||
{
|
||
uint8_t result;
|
||
switch (regType) {
|
||
case MODBUS_HOLDING_REG_TYPE:
|
||
result = node.readHoldingRegisters(reg, count);
|
||
break;
|
||
case MODBUS_COIL_REG_TYPE:
|
||
result = node.readCoils(reg, count);
|
||
break;
|
||
case MODBUS_DISCRETE_REG_TYPE:
|
||
result = node.readDiscreteInputs(reg, count);
|
||
break;
|
||
case MODBUS_INPUT_REG_TYPE:
|
||
result = node.readInputRegisters(reg, count);
|
||
break;
|
||
default:
|
||
debugSerial<<F("MBUS: Not supported reg type\n");
|
||
return false;
|
||
}
|
||
mbusSlenceTimer = millisNZ();
|
||
if (result != node.ku8MBSuccess) errorSerial<<F("MBUS: Polling error ")<<_HEX(result)<<endl;
|
||
return (result == node.ku8MBSuccess);
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @brief Находит и обрабатывает регистр Modbus, выполняет сопоставление и действия.
|
||
* @param registerNum Номер регистра.
|
||
* @param posInBuffer Позиция в буфере ответа.
|
||
* @param regType Тип регистра.
|
||
* @param registerFrom Начальный регистр диапазона.
|
||
* @param registerTo Конечный регистр диапазона.
|
||
* @param doExecution Выполнять ли действие.
|
||
* @param submitParam Флаг для подавления повторных действий.
|
||
* @return Команда itemCmd с результатом.
|
||
*/
|
||
itemCmd out_Modbus::findRegister(uint16_t registerNum, uint16_t posInBuffer, uint8_t regType, uint16_t registerFrom, uint16_t registerTo, bool doExecution, bool * submitParam)
|
||
{
|
||
aJsonObject * paramObj = store->parameters->child;
|
||
|
||
bool tmpSubmitParam;
|
||
if (!submitParam) submitParam=&tmpSubmitParam;
|
||
*submitParam=true;
|
||
|
||
//bool is8bit = false;
|
||
while (paramObj)
|
||
{
|
||
int8_t parType = PAR_I16;
|
||
aJsonObject *regObj=NULL;
|
||
switch (regType) {
|
||
case MODBUS_HOLDING_REG_TYPE:
|
||
regObj = aJson.getObjectItem(paramObj, "reg");
|
||
break;
|
||
case MODBUS_INPUT_REG_TYPE:
|
||
regObj = aJson.getObjectItem(paramObj, "ir");
|
||
break;
|
||
case MODBUS_COIL_REG_TYPE:
|
||
regObj = aJson.getObjectItem(paramObj, "coil");
|
||
parType = PAR_COIL;
|
||
break;
|
||
case MODBUS_DISCRETE_REG_TYPE:
|
||
regObj = aJson.getObjectItem(paramObj, "din");
|
||
parType = PAR_COIL;
|
||
}
|
||
|
||
if (regObj && regObj->valueint ==registerNum)
|
||
{
|
||
aJsonObject *idObj = aJson.getObjectItem(paramObj, "id");
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
uint16_t data = node.getResponseBuffer(posInBuffer);
|
||
bool executeWithoutCheck=false; //Afler recurrent check, all dublicatess and suppressing checked by recurrent
|
||
bool submitRecurrentOut = false; //false if recurrent check find duplicates
|
||
char buf[16];
|
||
|
||
uint32_t param =0;
|
||
itemCmd mappedParam;
|
||
aJsonObject *typeObj = aJson.getObjectItem(paramObj, "type");
|
||
aJsonObject *mapObj = aJson.getObjectItem(paramObj, "map");
|
||
|
||
if (typeObj && typeObj->type == aJson_String) parType=str2regSize(typeObj->valuestring);
|
||
switch(parType) {
|
||
|
||
case PAR_I16:
|
||
//isSigned=true;
|
||
param=data;
|
||
mappedParam.Int((int32_t)(int16_t)data);
|
||
break;
|
||
|
||
case PAR_U16:
|
||
param=data;
|
||
mappedParam.Int((uint32_t)data);
|
||
break;
|
||
|
||
case PAR_I32:
|
||
//isSigned=true;
|
||
param = swap(data ) | swap(node.getResponseBuffer(posInBuffer+1)<<16);
|
||
mappedParam.Int((int32_t)param);
|
||
break;
|
||
|
||
case PAR_U32:
|
||
param = swap(data )| swap (node.getResponseBuffer(posInBuffer+1)<<16 );
|
||
mappedParam.Int((uint32_t)param);
|
||
break;
|
||
|
||
case PAR_U8L:
|
||
//is8bit=true;
|
||
param = data & 0xFF;
|
||
mappedParam.Int((uint32_t)param);
|
||
break;
|
||
|
||
case PAR_U8H:
|
||
//is8bit=true;
|
||
param = (data & 0xFF00) >> 8;
|
||
mappedParam.Int((uint32_t)param);
|
||
break;
|
||
|
||
case PAR_TENS:
|
||
param=data;
|
||
mappedParam.Tens((int16_t) data);
|
||
break;
|
||
|
||
case PAR_100:
|
||
param=data;
|
||
mappedParam.Tens_raw((int16_t) data * (TENS_BASE/100));
|
||
mappedParam.Float((int32_t) (int16_t) data/100.);
|
||
break;
|
||
|
||
case PAR_COIL:
|
||
param = (node.getResponseBuffer(posInBuffer/16) >> (posInBuffer % 16)) & 1;
|
||
mappedParam.Int((uint32_t)param);
|
||
}
|
||
|
||
traceSerial << F("MBUSD: got ")<<mappedParam.toString(buf,sizeof(buf))<< F(" from type ")<<parType<<F(":")<<paramObj->name<<endl;
|
||
|
||
if (mapObj && (mapObj->type==aJson_Array || mapObj->type==aJson_Object))
|
||
{
|
||
mappedParam=mappedParam.doReverseMapping(mapObj);
|
||
if (!mappedParam.isCommand() && !mappedParam.isValue()) //Not mapped
|
||
{
|
||
aJsonObject *defMappingObj;
|
||
defMappingObj = aJson.getObjectItem(mapObj, "def");
|
||
if (defMappingObj)
|
||
{
|
||
switch (defMappingObj->type)
|
||
{
|
||
case aJson_Int: //register/coil/.. number
|
||
traceSerial<<F("MBUSD: Searching reg#")<<defMappingObj->valueint<<endl;
|
||
if ((defMappingObj->valueint>= registerFrom) && (defMappingObj->valueint<=registerTo))
|
||
{
|
||
mappedParam = findRegister(defMappingObj->valueint,defMappingObj->valueint-registerFrom,regType,registerFrom,registerTo,false,&submitRecurrentOut);
|
||
executeWithoutCheck=true;
|
||
traceSerial<<"MBUSD: recurrent check res: "<<"SRO:"<<submitRecurrentOut<<endl;
|
||
}
|
||
else
|
||
{
|
||
debugSerial<<F("def reg# ")<<defMappingObj->valueint<<F(" out of range buffer, fetching")<<endl;
|
||
////to prevent CORRUPTION if using same buffer
|
||
uint16_t localBuffer;
|
||
node.setResponseBuffer(&localBuffer,1);
|
||
if (readModbus(defMappingObj->valueint,regType,1))
|
||
{
|
||
mappedParam = findRegister(defMappingObj->valueint,0,regType,defMappingObj->valueint,defMappingObj->valueint,false,&submitRecurrentOut);
|
||
executeWithoutCheck=true;
|
||
}
|
||
node.setDefaultResponseBuffer();
|
||
}
|
||
|
||
break;
|
||
case aJson_String: // parameter name
|
||
traceSerial<<F("Searching reg: ")<<defMappingObj->valuestring<<endl;
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
//Searching item param for nested mapping
|
||
//Retrive previous data
|
||
aJsonObject *lastMeasured = getLastMeasured(defMappingObj->valuestring);
|
||
if (lastMeasured)
|
||
{
|
||
traceSerial<<F("LastKnown value: ")<<lastMeasured->valueint<<endl;
|
||
//Searching template param for nested mapping
|
||
aJsonObject * templateParObj = aJson.getObjectItem(store->parameters,defMappingObj->valuestring);
|
||
if (templateParObj)
|
||
{
|
||
int8_t nestedParType = PAR_I16;
|
||
|
||
aJsonObject * nestedTypeObj = aJson.getObjectItem(templateParObj, "type");
|
||
if (nestedTypeObj && nestedTypeObj->type == aJson_String) parType=str2regSize(nestedTypeObj->valuestring);
|
||
|
||
switch(nestedParType) {
|
||
case PAR_I16:
|
||
case PAR_I32:
|
||
mappedParam.Int((int32_t)lastMeasured->valueint);
|
||
break;
|
||
|
||
case PAR_U32:
|
||
case PAR_U16:
|
||
case PAR_U8L:
|
||
case PAR_U8H:
|
||
case PAR_COIL:
|
||
mappedParam.Int((uint32_t)lastMeasured->valueint);
|
||
break;
|
||
|
||
case PAR_TENS:
|
||
mappedParam.Tens((int16_t) data);
|
||
break;
|
||
|
||
case PAR_100:
|
||
mappedParam.Tens_raw((int16_t) lastMeasured->valueint * (TENS_BASE/100));
|
||
mappedParam.Float((int32_t) (int16_t) lastMeasured->valueint/100.);
|
||
break;
|
||
default: errorSerial<<F("Invalid regtype")<<endl;
|
||
}
|
||
|
||
aJsonObject * nestedMapObj = aJson.getObjectItem(templateParObj, "map");
|
||
if (nestedMapObj && (nestedMapObj->type==aJson_Array || nestedMapObj->type==aJson_Object)) mappedParam=mappedParam.doReverseMapping(nestedMapObj);
|
||
traceSerial << F("MBUSD: NestedMapped:")<<mappedParam.toString(buf,sizeof(buf))<<endl;
|
||
|
||
if (!(lastMeasured->subtype & MB_VALUE_OUTDATED))
|
||
{
|
||
executeWithoutCheck=true;
|
||
submitRecurrentOut=true;
|
||
lastMeasured->subtype|= MB_VALUE_OUTDATED;
|
||
}
|
||
|
||
}
|
||
} //nested have lastMeasured
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
traceSerial << F("MBUSD: Mapped:")<<mappedParam.toString(buf,sizeof(buf))<<endl;
|
||
} //mapping
|
||
|
||
if (doExecution && idObj && idObj->type==aJson_Int)
|
||
switch (idObj->valueint)
|
||
{
|
||
case S_CMD:
|
||
mappedParam.saveItem(item,FLAG_COMMAND);
|
||
break;
|
||
case S_SET:
|
||
mappedParam.saveItem(item,FLAG_PARAMETERS);
|
||
}
|
||
|
||
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
aJsonObject *execObj = aJson.getObjectItem(itemParametersObj,paramObj->name);
|
||
if (execObj)
|
||
{
|
||
// if (!doExecution || haveAction(execObj)) //if no action in execObj - do not save last value to avoid confuse further recurrent check
|
||
// {
|
||
//Retrive previous data
|
||
aJsonObject *lastMeasured = getLastMeasured(execObj);
|
||
if (lastMeasured)
|
||
{
|
||
if (lastMeasured->valueint == param)
|
||
{
|
||
//if recurrent call but value was readed before
|
||
if (!doExecution && !(lastMeasured->subtype & MB_VALUE_OUTDATED))
|
||
{
|
||
*submitParam=true; //never used
|
||
lastMeasured->subtype|=MB_VALUE_OUTDATED;
|
||
return mappedParam;
|
||
}
|
||
*submitParam=false; //supress repeating execution for same val
|
||
}
|
||
else
|
||
{
|
||
lastMeasured->valueint=param;
|
||
traceSerial<<"MBUS: Stored "<<param<<" to @S of "<<paramObj->name<<endl;
|
||
lastMeasured->subtype&=~MB_VALUE_OUTDATED;
|
||
}
|
||
}
|
||
// }
|
||
|
||
if (executeWithoutCheck)
|
||
{
|
||
|
||
if (doExecution && (submitRecurrentOut || *submitParam))
|
||
{
|
||
//debugSerial<<F("MBUS: exec ");mappedParam.debugOut();
|
||
executeCommand(execObj, -1, mappedParam);
|
||
*submitParam=true; //if requrrent check has submit smth - report it.
|
||
}
|
||
|
||
return mappedParam;
|
||
}
|
||
|
||
if (*submitParam && doExecution)
|
||
{
|
||
// Compare with last submitted val (if @V NOT marked as NULL in config)
|
||
aJsonObject * markObj = execObj;
|
||
if (execObj->type == aJson_Array)
|
||
{
|
||
markObj = execObj->child;
|
||
//storeLastValue = true;
|
||
}
|
||
aJsonObject *settedValue = aJson.getObjectItem(markObj,"@V");
|
||
if (settedValue && settedValue->type==aJson_Int && (settedValue->valueint == param))
|
||
{
|
||
traceSerial<<F("MBUSD: Ignored - equal with setted val")<<endl;
|
||
*submitParam=false;
|
||
}
|
||
else
|
||
{
|
||
if (doExecution)
|
||
{
|
||
//debugSerial<<F("MBUS: exec ");mappedParam.debugOut();
|
||
executeCommand(execObj, -1, mappedParam);
|
||
}
|
||
// if param updated by device and no new value queued to send - update @V to avoid "Ignored - equal with setted val"
|
||
if (settedValue && !(execObj->subtype & MB_NEED_SEND))
|
||
settedValue->valueint=param;
|
||
}
|
||
} //to be executed
|
||
} //ExecObj
|
||
} //item Parameters
|
||
|
||
return mappedParam;
|
||
} //reg == regNum
|
||
paramObj=paramObj->next;
|
||
} //while
|
||
return itemCmd();
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Опрос Modbus-устройства по списку регистров.
|
||
* @param reg JSON-объект с регистрами.
|
||
* @param regType Тип регистра.
|
||
*/
|
||
void out_Modbus::pollModbus(aJsonObject * reg, int regType)
|
||
{
|
||
if (!reg) return;
|
||
reg=reg->child;
|
||
while (reg)
|
||
{
|
||
switch (reg->type)
|
||
{
|
||
case aJson_Int:
|
||
{
|
||
int registerNum = reg->valueint;
|
||
if (readModbus(registerNum,regType,1))
|
||
{
|
||
findRegister(registerNum,0,regType,registerNum,registerNum);
|
||
}
|
||
}
|
||
break;
|
||
case aJson_Array:
|
||
if (aJson.getArraySize(reg)==2)
|
||
{
|
||
int registerFrom=aJson.getArrayItem(reg, 0)->valueint;
|
||
int registerTo=aJson.getArrayItem(reg, 1)->valueint;
|
||
|
||
if (readModbus(registerFrom,regType,registerTo-registerFrom+1))
|
||
{ traceSerial<<endl;
|
||
for(int i=registerFrom;i<=registerTo;i++)
|
||
{
|
||
findRegister(i,i-registerFrom,regType,registerFrom,registerTo);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
reg = reg->next;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Инициализирует линию связи Modbus.
|
||
*/
|
||
void out_Modbus::initLine()
|
||
{
|
||
//store->serialParam=(USARTClass::USARTModes) SERIAL_8N1;
|
||
#if defined (__SAM3X8E__)
|
||
modbusSerial.begin(store->baud, static_cast <USARTClass::USARTModes> (store->serialParam));
|
||
#elif defined (ARDUINO_ARCH_ESP8266)
|
||
modbusSerial.begin(store->baud, static_cast <SerialConfig>(store->serialParam));
|
||
#elif defined (ESP32)
|
||
//modbusSerial.begin(store->baud, store->serialParam);
|
||
//delay(100);
|
||
modbusSerial.updateBaudRate (store->baud); //Some terrible error in ESP32 core with uart reinit
|
||
#else
|
||
modbusSerial.begin(store->baud, (store->serialParam));
|
||
#endif
|
||
//debugSerial<< store->baud << F("---")<< store->serialParam<<endl;
|
||
node.begin(item->getArg(0), modbusSerial);
|
||
}
|
||
|
||
/**
|
||
* @brief Отправляет значение в Modbus-устройство.
|
||
* @param paramName Имя параметра.
|
||
* @param outValue JSON-объект с отправляемым значением.
|
||
* @return 0 при успехе, отрицательное значение при ошибке.
|
||
*/
|
||
int out_Modbus::sendModbus(char * paramName, aJsonObject * outValue)
|
||
{
|
||
if (!store) {errorSerial<<F(" internal send error - no store")<<endl; return -1;}
|
||
|
||
aJsonObject * templateParamObj = aJson.getObjectItem(store->parameters, paramName);
|
||
if (!templateParamObj) {errorSerial<<F(" internal send error - no template")<<endl; return -1;}
|
||
|
||
aJsonObject * regObj = aJson.getObjectItem(templateParamObj, "reg");
|
||
|
||
if (!regObj)
|
||
{
|
||
regObj = aJson.getObjectItem(templateParamObj, "coil");
|
||
if (!regObj) {errorSerial<<F(" internal send error - no reg/coil")<<endl; return -2;}
|
||
else outValue->subtype = PAR_COIL;
|
||
}
|
||
if (regObj->type != aJson_Int) {errorSerial<<F(" Reg/coil must be int")<<endl; return -2;}
|
||
|
||
aJsonObject * prefetchObj = aJson.getObjectItem(templateParamObj, "prefetch");
|
||
aJsonObject *lastMeasured = NULL;
|
||
|
||
int res = -1;
|
||
|
||
if (prefetchObj && (prefetchObj->type == aJson_Boolean) && prefetchObj->valuebool)
|
||
{
|
||
int modbusRegType = (outValue->subtype == PAR_COIL) ? MODBUS_COIL_REG_TYPE:MODBUS_HOLDING_REG_TYPE;
|
||
debugSerial<<F(" prefetch ")<<paramName<<F(" #") <<regObj->valueint << " type:" << modbusRegType << " ";
|
||
|
||
/// to prevent CORRUPTIOIN if using same buffer
|
||
uint16_t localBuffer;
|
||
node.setResponseBuffer(&localBuffer,1);
|
||
|
||
bool successRead = readModbus(regObj->valueint,modbusRegType,1);
|
||
mbusSlenceTimer = millisNZ();
|
||
|
||
|
||
if (successRead)
|
||
{
|
||
#ifdef PREFETCH_EXEC_IMMEDIALELLY
|
||
// option to execute if changed immediatelly
|
||
bool submited = false;
|
||
findRegister(regObj->valueint,0,modbusRegType,regObj->valueint,regObj->valueint,true,&submited);
|
||
node.setDefaultResponseBuffer();
|
||
|
||
if (submited)
|
||
{
|
||
debugSerial << F("MBUS:")<<paramName<< (" val changed. Write cancelled")<<endl;
|
||
return -3;
|
||
}
|
||
else debugSerial << F("MBUS:")<<paramName<< F(" val not changed. Continue")<<endl;
|
||
|
||
|
||
#else
|
||
// Option to skip writing if change, let next polling take of change
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
aJsonObject *execObj = aJson.getObjectItem(itemParametersObj,paramName);
|
||
if (execObj)
|
||
{
|
||
//aJsonObject * markObj = execObj;
|
||
//if (execObj->type == aJson_Array) markObj = execObj->child;
|
||
//Retrive previous data
|
||
lastMeasured = getLastMeasured(execObj);// aJson.getObjectItem(markObj,"@S");
|
||
if (lastMeasured)
|
||
{
|
||
//if (lastMeasured->type == aJson_Int)
|
||
// {
|
||
traceSerial<<F(" Last:")<<lastMeasured->valueint<< F(" Now:") << localBuffer<<endl;
|
||
|
||
if (lastMeasured->valueint != localBuffer)
|
||
{
|
||
debugSerial << F("MBUS:")<<paramName<< F(" val changed.")<<endl;
|
||
node.setDefaultResponseBuffer();
|
||
return -3;
|
||
}
|
||
else
|
||
{
|
||
if (outValue->valueint == localBuffer)
|
||
{
|
||
debugSerial << F("MBUS:")<<paramName<< F("=")<<localBuffer<<F(": equal targert.")<<endl;
|
||
node.setDefaultResponseBuffer();
|
||
return -4;
|
||
}
|
||
|
||
debugSerial << F("MBUS:")<<paramName<< F(" val not changed. Continue")<<endl;
|
||
}
|
||
//}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
node.setDefaultResponseBuffer();
|
||
|
||
|
||
}
|
||
|
||
|
||
switch(outValue->subtype) {
|
||
case PAR_U16:
|
||
case PAR_I16:
|
||
case PAR_TENS:
|
||
case PAR_100:
|
||
res = node.writeSingleRegister(regObj->valueint,outValue->valueint);
|
||
break;
|
||
|
||
break;
|
||
case PAR_I32:
|
||
case PAR_U32:
|
||
res = node.writeSingleRegister(regObj->valueint,swap(outValue->valueint & 0xFFFF));
|
||
res += node.writeSingleRegister(regObj->valueint+1,swap(outValue->valueint >> 16)) ;
|
||
break;
|
||
|
||
case PAR_U8L:
|
||
case PAR_I8L:
|
||
res = node.writeSingleRegister(regObj->valueint,outValue->valueint & 0xFF);
|
||
break;
|
||
|
||
case PAR_U8H:
|
||
case PAR_I8H:
|
||
res = node.writeSingleRegister(regObj->valueint,(outValue->valueint & 0xFFFF)>> 8);
|
||
break;
|
||
case PAR_COIL:
|
||
res = node.writeSingleCoil (regObj->valueint,outValue->valueint);
|
||
break;
|
||
}
|
||
mbusSlenceTimer = millisNZ();
|
||
debugSerial<<F("MBUS res: ")<<res<<F(" ")<<paramName<<" reg:"<<regObj->valueint<<F(" val:")<<outValue->valueint<<endl;
|
||
|
||
//If wrote - suppress action on poll
|
||
if ((res ==0) && (outValue->type == aJson_Int) && lastMeasured && (lastMeasured->type == aJson_Int)) lastMeasured->valueint = outValue->valueint;
|
||
|
||
|
||
return ( res == 0);
|
||
}
|
||
|
||
/**
|
||
* @brief Осуществляет опрос и отправку команд Modbus.
|
||
* @param cause Причина вызова (например, медленный опрос).
|
||
* @return Интервал следующего опроса.
|
||
*/
|
||
int out_Modbus::Poll(short cause)
|
||
{
|
||
if (cause==POLLING_SLOW) return 0;
|
||
bool lineInitialized = false;
|
||
|
||
if (modbusBusy || (Status() != CST_INITIALIZED) || ( mbusSlenceTimer && !isTimeOver(mbusSlenceTimer,millis(),200))) return 0;
|
||
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
aJsonObject *execObj = itemParametersObj->child;
|
||
bool onceSendOk=false;
|
||
while (execObj && ((execObj->type == aJson_Object) || (execObj->type == aJson_Array)) && !onceSendOk)
|
||
{
|
||
|
||
if ((execObj->subtype & MB_NEED_SEND) && !(execObj->subtype & MB_SEND_ERROR))
|
||
{
|
||
|
||
aJsonObject * markObj = execObj;
|
||
if (execObj->type == aJson_Array) markObj = execObj->child;
|
||
|
||
aJsonObject *outValue = aJson.getObjectItem(markObj,"@V");
|
||
if (outValue)
|
||
{
|
||
modbusBusy=1;
|
||
if (!lineInitialized)
|
||
{
|
||
lineInitialized=true;
|
||
initLine();
|
||
}
|
||
|
||
int sendRes;
|
||
int savedValue;
|
||
bool needResend;
|
||
do
|
||
{
|
||
savedValue = outValue->valueint;
|
||
debugSerial<<"MBUS: SEND "<<item->itemArr->name<<"/"<<execObj->name<<"="<<outValue->valueint<<endl;
|
||
sendRes = sendModbus(execObj->name,outValue);
|
||
needResend = (savedValue != outValue->valueint);
|
||
while(needResend && mbusSlenceTimer && !isTimeOver(mbusSlenceTimer,millis(),200)) modbusIdle();
|
||
}
|
||
while (needResend); //repeat sending if target value changed while we're waited for mbus responce
|
||
|
||
switch (sendRes)
|
||
{
|
||
case 1: //success
|
||
case -4: //equal tatget
|
||
//execObj->subtype&=~ MB_NEED_SEND;
|
||
execObj->subtype = 0;
|
||
onceSendOk=true;
|
||
///return 1; //relax
|
||
break;
|
||
case 0: //fault
|
||
execObj->subtype |= MB_SEND_ERROR;
|
||
errorSerial<<F("MBUS: ")<<item->itemArr->name<<"/"<<execObj->name<<F(" send error. ");
|
||
if ((execObj->subtype & 3) != MB_SEND_ATTEMPTS) execObj->subtype++;
|
||
errorSerial<<F("MBUS: ")<<item->itemArr->name<<"/"<<execObj->name<<" Attempt: "<< (execObj->subtype & 3) <<endl;
|
||
break;
|
||
case -3:
|
||
errorSerial<<F("MBUS: param ")<<item->itemArr->name<<"/"<<execObj->name<<F(" sending cancelled")<<endl;
|
||
//outValue->valueint=
|
||
//execObj->subtype&=~ MB_NEED_SEND;
|
||
execObj->subtype = 0;
|
||
break;
|
||
default: //param not found
|
||
errorSerial<<F("MBUS: param ")<<item->itemArr->name<<"/"<<execObj->name<<F(" not found")<<endl;
|
||
execObj->subtype&=~ MB_NEED_SEND;
|
||
}
|
||
}
|
||
}
|
||
execObj=execObj->next;
|
||
}
|
||
modbusBusy=0;
|
||
}
|
||
|
||
|
||
if (isTimeOver(store->timestamp,millis(),store->pollingInterval) && ( !mbusSlenceTimer || isTimeOver(mbusSlenceTimer,millis(),200)))
|
||
{
|
||
|
||
// Clean_up FLAG_SEND_ERROR flag
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
aJsonObject *execObj = itemParametersObj->child;
|
||
while (execObj && ((execObj->type == aJson_Object) || (execObj->type == aJson_Array)))
|
||
{
|
||
if (execObj->subtype & MB_SEND_ERROR) execObj->subtype&=~ MB_SEND_ERROR;
|
||
if ((execObj->subtype & 0x3) >= MB_SEND_ATTEMPTS)
|
||
{
|
||
//execObj->subtype&=~ MB_NEED_SEND;
|
||
//Clean ERROR, NEED, Attempts
|
||
errorSerial<<"MBUS: send failed "<<item->itemArr->name<<"/"<<execObj->name<<endl;
|
||
execObj->subtype = 0;
|
||
}
|
||
|
||
execObj=execObj->next;
|
||
}
|
||
}
|
||
|
||
// if some polling configured
|
||
if (store->pollingRegisters || store->pollingIrs || store->pollingCoils || store->poolingDiscreteIns)
|
||
{
|
||
traceSerial<<F("MBUSD: Poll ")<< item->itemArr->name << endl;
|
||
modbusBusy=1;
|
||
|
||
if (!lineInitialized)
|
||
{
|
||
lineInitialized=true;
|
||
initLine();
|
||
}
|
||
|
||
pollModbus(store->pollingRegisters,MODBUS_HOLDING_REG_TYPE);
|
||
pollModbus(store->pollingIrs,MODBUS_INPUT_REG_TYPE);
|
||
pollModbus(store->pollingCoils,MODBUS_COIL_REG_TYPE);
|
||
pollModbus(store->poolingDiscreteIns ,MODBUS_DISCRETE_REG_TYPE);
|
||
traceSerial<<F("MBUSD: endPoll ")<< item->itemArr->name << endl;
|
||
|
||
//Non blocking waiting to release line
|
||
uint32_t time = millis();
|
||
while (!isTimeOver(time,millis(),50))
|
||
modbusIdle();
|
||
|
||
modbusBusy =0;
|
||
}
|
||
store->timestamp=millisNZ();
|
||
}
|
||
|
||
return store->pollingInterval;
|
||
};
|
||
|
||
/**
|
||
* @brief Возвращает тип канала.
|
||
* @return CH_MBUS.
|
||
*/
|
||
int out_Modbus::getChanType()
|
||
{
|
||
return CH_MBUS;
|
||
}
|
||
|
||
/**
|
||
* @brief Отправляет команду itemCmd в Modbus по шаблону параметра.
|
||
* @param templateParamObj JSON-объект шаблона параметра.
|
||
* @param cmd Команда itemCmd.
|
||
* @return 1 при успехе, 0 при ошибке.
|
||
*/
|
||
int out_Modbus::sendItemCmd(aJsonObject *templateParamObj, itemCmd cmd)
|
||
{
|
||
if (templateParamObj)
|
||
{
|
||
int suffixCode = cmd.getSuffix();
|
||
//bool isCommand = cmd.isCommand();
|
||
if (!suffixCode) return 0;
|
||
|
||
char *suffixStr =templateParamObj->name;
|
||
// We have find template for suffix or suffixCode
|
||
itemCmd cmdValue = itemCmd(ST_VOID,CMD_VOID);
|
||
long Value = 0;
|
||
int8_t regType = PAR_I16;
|
||
aJsonObject * typeObj = aJson.getObjectItem(templateParamObj, "type");
|
||
aJsonObject * mapObj = aJson.getObjectItem(templateParamObj, "map");
|
||
|
||
|
||
if (typeObj && typeObj->type == aJson_String) regType=str2regSize(typeObj->valuestring);
|
||
switch(regType) {
|
||
case PAR_I16:
|
||
case PAR_U16:
|
||
case PAR_I32:
|
||
case PAR_U32:
|
||
case PAR_U8L:
|
||
case PAR_U8H:
|
||
case PAR_I8H:
|
||
case PAR_I8L:
|
||
|
||
cmdValue=cmd.doMapping(mapObj);
|
||
if (!cmdValue.isValue())return 0;
|
||
Value=cmdValue.getInt();
|
||
break;
|
||
case PAR_TENS:
|
||
cmdValue=cmd.doMapping(mapObj);
|
||
if (!cmdValue.isValue())return 0;
|
||
Value=cmdValue.getTens();
|
||
break;
|
||
case PAR_100:
|
||
cmdValue=cmd.doMapping(mapObj);
|
||
if (!cmdValue.isValue())return 0;
|
||
Value=cmdValue.getTens_raw()*(100/TENS_BASE);
|
||
}
|
||
|
||
traceSerial<<F("MBUSD: suffix:")<<suffixStr<< F(" Val: ")<<Value<<endl;
|
||
aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
|
||
if (itemParametersObj && itemParametersObj->type ==aJson_Object)
|
||
{
|
||
aJsonObject *execObj = aJson.getObjectItem(itemParametersObj,suffixStr);
|
||
if (execObj && ((execObj->type == aJson_Object) || (execObj->type == aJson_Array)))
|
||
{
|
||
|
||
|
||
aJsonObject * markObj = execObj;
|
||
if (execObj->type == aJson_Array) markObj = execObj->child;
|
||
|
||
//Schedule update
|
||
execObj->subtype |= MB_NEED_SEND;
|
||
|
||
aJsonObject *outValue = aJson.getObjectItem(markObj,"@V");
|
||
if (outValue) // Existant. Preserve original @type
|
||
{
|
||
outValue->valueint=Value;
|
||
outValue->subtype =regType & 0xF;
|
||
}
|
||
else //No container to store value yet
|
||
// If no @V in config - creating with INT type - normal behavior - no supress in-to-out
|
||
{
|
||
debugSerial<<F("Add @V: ")<<execObj->name<<endl;
|
||
aJson.addNumberToObject(markObj, "@V", Value);
|
||
outValue = aJson.getObjectItem(markObj,"@V");
|
||
if (outValue) outValue->subtype =regType & 0xF;
|
||
}
|
||
/* Conflict with pre-fetching
|
||
aJsonObject *polledValue = aJson.getObjectItem(markObj,"@S");
|
||
if (polledValue && outValue->type == aJson_Int)
|
||
{
|
||
traceSerial<<"MBUS: not Stored "<<Value<<" to @S of "<<item->itemArr->name<<":"<<templateParamObj->name<<endl;
|
||
polledValue->valueint=Value; //to pevent suppressing to change back to previously polled value if this occurs before next polling
|
||
polledValue->subtype&=~MB_VALUE_OUTDATED;
|
||
}
|
||
*/
|
||
|
||
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
else return 0;
|
||
}
|
||
|
||
//!Control unified Modbus item
|
||
// Priority of selection sub-items control to:
|
||
// 1. if defined standard suffix Code inside cmd
|
||
// 2. custom textual subItem
|
||
// 3. non-standard numeric suffix Code equal param id
|
||
|
||
/**
|
||
* @brief Унифицированное управление Modbus-каналом.
|
||
* @param cmd Команда itemCmd.
|
||
* @param subItem Имя подэлемента.
|
||
* @param toExecute Выполнять ли команду.
|
||
* @param authorized Авторизовано ли выполнение.
|
||
* @return Результат выполнения.
|
||
*/
|
||
int out_Modbus::Ctrl(itemCmd cmd, char* subItem, bool toExecute,bool authorized)
|
||
{
|
||
if (!store) return -1;
|
||
|
||
int suffixCode = cmd.getSuffix();
|
||
aJsonObject *templateParamObj = NULL;
|
||
int res = -1;
|
||
|
||
// trying to find parameter in template with name == subItem (NB!! standard suffixes dint working here)
|
||
if (subItem && strlen (subItem) && store && store->parameters)
|
||
|
||
{
|
||
templateParamObj = aJson.getObjectItem(store->parameters, subItem);
|
||
res= sendItemCmd(templateParamObj,cmd);
|
||
}
|
||
else
|
||
|
||
// No subitem, trying to find suffix with root item - (Trying to find template parameter where id == suffixCode)
|
||
{
|
||
if (store && store->parameters) templateParamObj = store->parameters->child;
|
||
bool suffixFinded = false;
|
||
while (templateParamObj)
|
||
{
|
||
aJsonObject *idObj = aJson.getObjectItem(templateParamObj, "id");
|
||
if (idObj && idObj->type==aJson_Int && idObj->valueint == suffixCode)
|
||
{
|
||
res= sendItemCmd(templateParamObj,cmd);
|
||
suffixFinded = true;
|
||
}
|
||
templateParamObj=templateParamObj->next;
|
||
}
|
||
if (!suffixFinded) errorSerial<<F("MBUS: No template for ")<<subItem<<F(" or suffix ")<<suffixCode<<endl;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
|
||
#endif
|