input fix for mixed configs & repeat flag, comments

This commit is contained in:
2025-08-23 23:26:05 +03:00
parent 5294617455
commit 2c601b2c03
5 changed files with 326 additions and 44 deletions

View File

@@ -18,7 +18,7 @@
-DMULTIVENT_DISABLE
#-DWiz5100
-DARDUINO_OTA_MDNS_DISABLE
-DMDNS_ENABLE
#-DMDNS_ENABLE
-DHSV_DISABLE
-DPWM_DISABLE

View File

@@ -27,6 +27,12 @@ extern volatile int8_t configLocked;
extern bool configLoaded;
/**
* @brief Prints the data contained in a CAN frame.
*
* @param frame Pointer to the datagram_t structure containing the CAN frame.
* @param len Length of the data to print.
*/
void printFrame(datagram_t * frame, uint8_t len ) {
debugSerial.print(" Data:");
@@ -38,6 +44,12 @@ void printFrame(datagram_t * frame, uint8_t len ) {
}
/**
* @brief Sends the uptime metric to the CAN bus.
*
* @param ut Uptime value to send.
* @return true if the message was sent successfully, false otherwise.
*/
bool canDriver::upTime(uint32_t ut)
{
if (!controllerId) return false;
@@ -57,6 +69,12 @@ bool canDriver::upTime(uint32_t ut)
return write (id.id, &packet, 4);
}
/**
* @brief Sends the salt metric to the CAN bus.
*
* @param salt Salt value to send.
* @return true if the message was sent successfully, false otherwise.
*/
bool canDriver::salt(uint32_t salt)
{
if (!controllerId) return false;
@@ -77,6 +95,11 @@ bool canDriver::salt(uint32_t salt)
return write (id.id, &packet, 4);
}
/**
* @brief Looks up the MAC address and sends it over the CAN bus.
*
* @return true if the MAC address was sent successfully, false otherwise.
*/
bool canDriver::lookupMAC()
{
// return 0;
@@ -102,6 +125,14 @@ bool canDriver::lookupMAC()
return res;
}
/**
* @brief Requests a frame from the specified device on the CAN bus.
*
* @param devId Device ID to request the frame from.
* @param _payloadType Type of payload to request.
* @param seqNo Sequence number for the request.
* @return true if the request was successful, false otherwise.
*/
bool canDriver::requestFrame(uint8_t devId, payloadType _payloadType, uint16_t seqNo )
{
canid_t id;
@@ -127,6 +158,12 @@ packet.metric1 =0;
return res;
}
/**
* @brief Sends the remote ID of a device identified by its MAC address.
*
* @param mac MAC address of the device.
* @return true if the remote ID was sent successfully, false otherwise.
*/
bool canDriver::sendRemoteID(macAddress mac)
{
canid_t id;
@@ -154,6 +191,11 @@ return res;
}
/**
* @brief Initializes the CAN driver and sets up the CAN bus.
*
* @return true if initialization was successful, false otherwise.
*/
bool canDriver::begin()
{
if (root)
@@ -207,6 +249,11 @@ bool canDriver::begin()
return true;
}
/**
* @brief Reads a frame from the CAN bus.
*
* @return Length of the received frame, or -1 if no frame was received.
*/
int canDriver::readFrame()
{
if (!ready) return -1;
@@ -284,6 +331,9 @@ int canDriver::readFrame()
return -1;
}
/**
* @brief Polls the CAN bus for incoming frames and processes them.
*/
void canDriver::Poll()
{
@@ -355,6 +405,15 @@ switch (state)
}
}
/**
* @brief Processes a received CAN packet.
*
* @param id Identifier of the CAN packet.
* @param packet Pointer to the received datagram_t structure.
* @param len Length of the received packet.
* @param rtr Indicates if the packet is a remote transmission request.
* @return true if the packet was processed successfully, false otherwise.
*/
bool canDriver::processPacket(canid_t id, datagram_t *packet, uint8_t len, bool rtr)
{
@@ -557,6 +616,11 @@ else //Requests
return false;
}
/**
* @brief Retrieves the device ID of this CAN driver.
*
* @return The device ID, or 0 if not set.
*/
uint8_t canDriver::getMyId()
{
if (!canConfigObj) return 0;
@@ -565,6 +629,12 @@ if (addrObj && (addrObj->type == aJson_Int)) return addrObj->valueint;
return 0;
}
/**
* @brief Retrieves the configuration object for a device by its ID.
*
* @param devId Device ID to look up.
* @return Pointer to the configuration object, or NULL if not found.
*/
aJsonObject * canDriver::getConfbyID(uint8_t devId)
{
if (!canConfigObj) return NULL;
@@ -583,6 +653,13 @@ while (remoteConfObj)
return NULL;
}
/**
* @brief Finds a configuration object by device name.
*
* @param devName Name of the device to look for.
* @param devAddr Pointer to store the device address if found.
* @return Pointer to the configuration object, or NULL if not found.
*/
aJsonObject * canDriver::findConfbyName(char* devName, int * devAddr)
{
if (!canRemoteConfigObj || canRemoteConfigObj->type != aJson_Object || !devName ) return NULL;
@@ -611,6 +688,13 @@ return NULL;
#if not defined (NOIP)
extern PubSubClient mqttClient;
/**
* @brief Subscribes to MQTT topics based on the CAN configuration.
*
* @param root Pointer to the root topic string.
* @param buflen Length of the buffer for the topic string.
* @return true if subscription was successful, false otherwise.
*/
bool canDriver::subscribeTopics(char * root, size_t buflen)
{
if (!root) return false;
@@ -643,6 +727,12 @@ while (remoteConfObj)
}
#endif
/**
* @brief Retrieves the device ID associated with a given MAC address.
*
* @param mac MAC address to look up.
* @return The device ID, or 0 if not found.
*/
uint8_t canDriver::getIdByMac(macAddress mac)
{
char macStr[19];
@@ -675,6 +765,14 @@ if (addrObj && (addrObj->type == aJson_Int))
return 0;
}
/**
* @brief Sends a message over the CAN bus.
*
* @param msg_id Identifier for the message.
* @param buf Pointer to the datagram_t structure containing the message data.
* @param size Size of the message data.
* @return true if the message was sent successfully, false otherwise.
*/
bool canDriver::write(uint32_t msg_id, datagram_t * buf, uint8_t size)
{ //
if (!ready) {
@@ -724,12 +822,28 @@ bool canDriver::write(uint32_t msg_id, datagram_t * buf, uint8_t size)
/**
* @brief Sends the status of a specified item.
*
* @param itemNum Item number to send the status for.
* @param cmd Command structure containing the status information.
* @param subItem Sub-item identifier.
* @return true if the status was sent successfully, false otherwise.
*/
bool canDriver::sendStatus(uint16_t itemNum, itemCmd cmd, int subItem)
{
if (!itemNum || !controllerId) return false;
return sendCommand(controllerId, itemNum, cmd, true, subItem);
}
/**
* @brief Sends a command to a specified CAN device.
*
* @param can Pointer to the configuration object for the device.
* @param cmd Command structure containing the command information.
* @param status Indicates if the command is a status update.
* @return true if the command was sent successfully, false otherwise.
*/
bool canDriver::sendCommand(aJsonObject * can, itemCmd cmd, bool status)
{
if (can && (can->type == aJson_Array))
@@ -766,6 +880,16 @@ bool canDriver::sendCommand(aJsonObject * can, itemCmd cmd, bool status)
return false;
}
/**
* @brief Sends a command to a specified device by ID.
*
* @param devID Device ID to send the command to.
* @param itemID Item ID to send the command for.
* @param cmd Command structure containing the command information.
* @param status Indicates if the command is a status update.
* @param subItemID Sub-item identifier.
* @return true if the command was sent successfully, false otherwise.
*/
bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool status,int subItemID )
{
canid_t id;
@@ -801,6 +925,13 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
////////////////////////////// Steream //////////////////////////
/**
* @brief Sends data over the CAN stream.
*
* @param len Length of the data to send.
* @param _seqNo Sequence number for the data.
* @return 1 if the data was sent successfully, 0 otherwise.
*/
int canStream::send(uint8_t len, uint16_t _seqNo)
{
canid_t id;
@@ -823,6 +954,11 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
else return 0;
}
/**
* @brief Checks the state of the CAN stream and processes any incoming data.
*
* @return -1 on error, or the number of bytes available for reading.
*/
int canStream::checkState()
{
bool res = false;
@@ -918,6 +1054,11 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
// Stream methods
/**
* @brief Checks how many bytes are available for reading from the CAN stream.
*
* @return Number of bytes available, or -1 on error.
*/
int canStream::available()
{
if (!driver) return -1;
@@ -925,6 +1066,11 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
return avail;
};
/**
* @brief Reads a byte from the CAN stream.
*
* @return The byte read, or -1 on error.
*/
int canStream::read()
{
if (!driver) return -1;
@@ -939,6 +1085,11 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
else return -1;
};
/**
* @brief Peeks at the next byte in the CAN stream without removing it.
*
* @return The next byte, or -1 on error.
*/
int canStream::peek()
{
if (!driver) return -1;
@@ -954,6 +1105,12 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
/**
* @brief Writes a byte to the CAN stream.
*
* @param c The byte to write.
* @return The number of bytes written, or -1 on error.
*/
size_t canStream::write(uint8_t c)
{
//if ((state != canState::StreamOpenedWrite) || (state != canState::waitingConfirm)) return -1;
@@ -984,12 +1141,20 @@ bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool sta
}
return 1; };
/**
* @brief Flushes the CAN stream, sending any buffered data.
*/
void canStream::flush()
{
send(writePos,seqNo);
};
/**
* @brief Checks if the CAN stream is available for writing.
*
* @return 1 if available, 0 if waiting for confirmation.
*/
int canStream::availableForWrite()
{
switch (state)

View File

@@ -681,9 +681,9 @@ debugSerial << F("IN:") << pin << F(" DHT22 type. T=") << temp << F("°C H=") <<
return false;
}
// TODO Polling via timed interrupt with CHECK_INTERRUPT cause
bool Input::
changeState(uint8_t newState, short cause, aJsonObject * currentInputObject, bool contactState)
changeState(uint8_t newState, short cause, aJsonObject * currentInputObject, bool contactState, bool calledOnTimer)
{
if (!inputObj || !store) return false;
@@ -782,22 +782,30 @@ if (newState!=store->state && cause!=CHECK_INTERRUPT) debugSerial<<F("#")<<pin<<
cmd = aJson.getObjectItem(currentInputObject, "rpcmd3");
toggle=store->toggle3;
break;
case IS_NOP:
if (!calledOnTimer) break;
if (contactState)
cmd = aJson.getObjectItem(currentInputObject, "scmd");
else cmd = aJson.getObjectItem(currentInputObject, "rcmd");
break;
}
if (cause != CHECK_INTERRUPT)
if (!calledOnTimer || newState == IS_NOP)
{
onContactChanged(contactState);
store->delayedState=false;
}
else
{
store->delayedState=true;
store->lastValue = contactState;
store->reqState=newState;
if (cause != CHECK_INTERRUPT)
{
onContactChanged(contactState);
store->delayedState=false;
}
else
{
store->delayedState=true;
store->lastValue = contactState;
store->reqState=newState;
}
}
if (newState == IS_NOP) return true;
if ((newState == IS_NOP) && !calledOnTimer) return true;
aJsonObject *defaultItem = aJson.getObjectItem(currentInputObject, "item");
aJsonObject *defaultEmit = aJson.getObjectItem(currentInputObject, "emit");
@@ -807,7 +815,7 @@ if (newState!=store->state && cause!=CHECK_INTERRUPT) debugSerial<<F("#")<<pin<<
if (!cmd && !defCmd.isCommand())
{
store->state=newState;
if (newState !=IS_NOP) store->state=newState;
store->delayedState=false;
return true; //nothing to do
}
@@ -815,7 +823,7 @@ if (newState!=store->state && cause!=CHECK_INTERRUPT) debugSerial<<F("#")<<pin<<
if (cause != CHECK_INTERRUPT)
{
store->state=newState;
if (newState !=IS_NOP) store->state=newState;
store->delayedState=false;
checkInstructions(cmd);
executeCommand(cmd,toggle,defCmd,defaultItem,defaultEmit,defaultCan);
@@ -824,7 +832,7 @@ if (newState!=store->state && cause!=CHECK_INTERRUPT) debugSerial<<F("#")<<pin<<
else
{
//Postpone actual execution
if (newState != store->state)
if ((newState != store->state) && (newState !=IS_NOP))
{
store->reqState=newState;
store->delayedState=true;
@@ -880,15 +888,15 @@ switch (store->state) //Timer based transitions
case IS_PRESSED:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_LONG,0xFFFF))
{
if (!aJson.getObjectItem(inputObj, "lcmd") && !aJson.getObjectItem(currentInputObject, "rpcmd")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState);
else changeState(IS_LONG, cause,currentInputObject,currentInputState);
if (!aJson.getObjectItem(inputObj, "lcmd") && !aJson.getObjectItem(currentInputObject, "rpcmd")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState,true);
else changeState(IS_LONG, cause,currentInputObject,currentInputState,true);
}
break;
case IS_LONG:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -896,7 +904,7 @@ switch (store->state) //Timer based transitions
case IS_REPEAT:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -904,15 +912,15 @@ switch (store->state) //Timer based transitions
case IS_PRESSED2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_LONG,0xFFFF))
{
if (!aJson.getObjectItem(currentInputObject, "lcmd2") && !aJson.getObjectItem(currentInputObject, "rpcmd2")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState);
else changeState(IS_LONG2, cause,currentInputObject,currentInputState);
if (!aJson.getObjectItem(currentInputObject, "lcmd2") && !aJson.getObjectItem(currentInputObject, "rpcmd2")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState,true);
else changeState(IS_LONG2, cause,currentInputObject,currentInputState,true);
}
break;
case IS_LONG2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT2, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT2, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -920,7 +928,7 @@ switch (store->state) //Timer based transitions
case IS_REPEAT2:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT2, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT2, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -930,17 +938,17 @@ switch (store->state) //Timer based transitions
{
if (!aJson.getObjectItem(currentInputObject, "lcmd3") && !aJson.getObjectItem(currentInputObject, "rpcmd3")) //No longpress handlers
{
if (aJson.getObjectItem(currentInputObject, "scmd3")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState); //was used
else changeState(IS_PRESSED2, cause,currentInputObject,currentInputState); // completely empty trippleClick section - fallback to first click handler
if (aJson.getObjectItem(currentInputObject, "scmd3")) changeState(IS_WAITRELEASE, cause,currentInputObject,currentInputState,true); //was used
else changeState(IS_PRESSED2, cause,currentInputObject,currentInputState,true); // completely empty trippleClick section - fallback to first click handler
}
else changeState(IS_LONG3, cause,currentInputObject,currentInputState);
else changeState(IS_LONG3, cause,currentInputObject,currentInputState,true);
}
break;
case IS_LONG3:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT,0xFFFF))
{
changeState(IS_REPEAT3, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT3, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -948,7 +956,7 @@ switch (store->state) //Timer based transitions
case IS_REPEAT3:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_RPT_PULSE,0xFFFF))
{
changeState(IS_REPEAT3, cause,currentInputObject,currentInputState);
changeState(IS_REPEAT3, cause,currentInputObject,currentInputState,true);
store->timestamp16 = millis() & 0xFFFF;
}
break;
@@ -958,7 +966,7 @@ switch (store->state) //Timer based transitions
case IS_WAITPRESS:
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_IDLE,0xFFFF)) changeState(IS_IDLE, cause,currentInputObject,currentInputState);
if (isTimeOver(store->timestamp16,millis() & 0xFFFF,T_IDLE,0xFFFF)) changeState(IS_IDLE, cause,currentInputObject,currentInputState,true);
break;
} //switch
#ifdef ROTARYENCODER
@@ -1028,7 +1036,7 @@ if (re)
res = changeState(IS_PRESSED3, cause,currentInputObject,currentInputState);
break;
default:
res = changeState(IS_NOP, cause,currentInputObject,currentInputState);
res = changeState(IS_NOP, cause,currentInputObject,currentInputState,(currentInputState == store->lastValue));
}
else
@@ -1064,7 +1072,7 @@ if (re)
res = changeState(IS_IDLE, cause,currentInputObject,currentInputState);
break;
default:
res = changeState(IS_NOP, cause,currentInputObject,currentInputState);
res = changeState(IS_NOP, cause,currentInputObject,currentInputState, (currentInputState == store->lastValue));
}
if (res) { //State changed or postponed
// store->logicState = currentInputState;
@@ -1186,13 +1194,14 @@ strncpy(addrstr,emit->valuestring,sizeof(addrstr));
if (mqttClient.connected() && !ethernetIdleCount)
{
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
if (newValue) { //send set command
if (!scmd || scmd->type != aJson_String) mqttClient.publish(addrstr, "ON", true);
else if (strlen(scmd->valuestring))
mqttClient.publish(addrstr, scmd->valuestring, true);
if (!scmd) {mqttClient.publish(addrstr, "ON", true); debugSerial<<F("Emit:")<<addrstr<< F("->") << "ON"<<endl;}
else if ((scmd->type == aJson_String) && strlen(scmd->valuestring))
{mqttClient.publish(addrstr, scmd->valuestring, true);debugSerial<<F("Emit:")<<addrstr<< F("->") << scmd->valuestring<<endl;}
} else { //send reset command
if (!rcmd || rcmd->type != aJson_String) mqttClient.publish(addrstr, "OFF", true);
else if (strlen(rcmd->valuestring))mqttClient.publish(addrstr, rcmd->valuestring, true);
if (!rcmd) {mqttClient.publish(addrstr, "OFF", true);debugSerial<<F("Emit:")<<addrstr<< F("->") << "OFF"<<endl;}
else if ((rcmd->type == aJson_String) && strlen(rcmd->valuestring)) {mqttClient.publish(addrstr, rcmd->valuestring, true);debugSerial<<F("Emit:")<<addrstr<< F("->") << rcmd->valuestring<<endl;}
}
}
#endif //NOIP
@@ -1203,12 +1212,12 @@ if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestri
Item it(item->valuestring);
if (it.isValid()) {
if (newValue) { //send set command
if (!scmd || scmd->type != aJson_String) it.Ctrl(itemCmd(ST_VOID,CMD_ON));
else if (strlen(scmd->valuestring))
if (!scmd ) it.Ctrl(itemCmd(ST_VOID,CMD_ON));
else if ((scmd->type == aJson_String) && strlen(scmd->valuestring))
it.Ctrl(scmd->valuestring);
} else { //send reset command
if (!rcmd || rcmd->type != aJson_String) it.Ctrl(itemCmd(ST_VOID,CMD_OFF));
else if (strlen(rcmd->valuestring))
if (!rcmd ) it.Ctrl(itemCmd(ST_VOID,CMD_OFF));
else if ((rcmd->type == aJson_String) && strlen(rcmd->valuestring))
it.Ctrl(rcmd->valuestring);
}
}

View File

@@ -193,7 +193,7 @@ protected:
bool publishDataToDomoticz(int , aJsonObject *, const char *format, ...);
char* getIdxField();
bool changeState(uint8_t newState, short cause, aJsonObject * currentInputObject, bool contactState);
bool changeState(uint8_t newState, short cause, aJsonObject * currentInputObject, bool contactState, bool calledOnTimer = false);
void setupRotaryEncoder();
aJsonObject * getCurrentInput();

View File

@@ -60,8 +60,18 @@ const reg_t regSize_P[] PROGMEM =
} ;
#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++)
@@ -71,6 +81,12 @@ int str2regSize(char * str)
}
//TODO irs etc
/**
* @brief Получает имя параметра по номеру регистра.
* @param parameters JSON-объект с параметрами.
* @param regnum Номер регистра.
* @return Имя параметра или NULL.
*/
char * getParamNameByReg(aJsonObject * parameters, int regnum)
{
if (!parameters) return NULL;
@@ -89,6 +105,11 @@ char * getParamNameByReg(aJsonObject * parameters, int regnum)
return NULL;
}
/**
* @brief Проверяет наличие действия в JSON-объекте.
* @param execObj JSON-объект.
* @return true, если действие найдено, иначе false.
*/
bool haveAction(aJsonObject * execObj)
{
aJsonObject * j = execObj->child;
@@ -111,6 +132,10 @@ case aJson_Array:
return false;
}
/**
* @brief Загружает и применяет конфигурацию Modbus.
* @return true, если конфигурация успешно загружена, иначе false.
*/
bool out_Modbus::getConfig()
{
// Retrieve and store template values from global modbus settings
@@ -224,6 +249,11 @@ bool out_Modbus::getConfig()
}
/**
* @brief Создаёт поле для хранения последнего измеренного значения по имени параметра.
* @param name Имя параметра.
* @return true, если успешно, иначе false.
*/
int out_Modbus::createLastMeasured(char * name)
{
if (!name) return false;
@@ -231,6 +261,11 @@ 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;
@@ -255,6 +290,11 @@ int out_Modbus::createLastMeasured(aJsonObject * execObj)
return true;
}
/**
* @brief Получает объект последнего измеренного значения по имени параметра.
* @param name Имя параметра.
* @return JSON-объект или NULL.
*/
aJsonObject * out_Modbus::getLastMeasured(char * name)
{
if (!name) return NULL;
@@ -262,6 +302,11 @@ 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;
@@ -271,6 +316,10 @@ if (lastMeasured && lastMeasured->type == aJson_Int) return lastMeasured;
return NULL;
}
/**
* @brief Инициализирует канал Modbus и загружает конфигурацию.
* @return 1 при успехе, 0 при ошибке.
*/
int out_Modbus::Setup()
{
abstractOut::Setup();
@@ -295,6 +344,10 @@ else
}
/**
* @brief Останавливает работу канала Modbus и освобождает ресурсы.
* @return 1 при успехе.
*/
int out_Modbus::Stop()
{
debugSerial.print("MBUS: De-Init ");
@@ -309,6 +362,13 @@ 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;
@@ -336,6 +396,17 @@ 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;
@@ -627,7 +698,12 @@ return itemCmd();
}
void out_Modbus::pollModbus(aJsonObject * reg, int regType)
/**
* @brief Опрос Modbus-устройства по списку регистров.
* @param reg JSON-объект с регистрами.
* @param regType Тип регистра.
*/
void out_Modbus::pollModbus(aJsonObject * reg, int regType)
{
if (!reg) return;
reg=reg->child;
@@ -665,6 +741,9 @@ return itemCmd();
}
}
/**
* @brief Инициализирует линию связи Modbus.
*/
void out_Modbus::initLine()
{
//store->serialParam=(USARTClass::USARTModes) SERIAL_8N1;
@@ -683,6 +762,12 @@ void out_Modbus::initLine()
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;}
@@ -819,6 +904,11 @@ if ((res ==0) && (outValue->type == aJson_Int) && lastMeasured && (lastMeasured-
return ( res == 0);
}
/**
* @brief Осуществляет опрос и отправку команд Modbus.
* @param cause Причина вызова (например, медленный опрос).
* @return Интервал следующего опроса.
*/
int out_Modbus::Poll(short cause)
{
if (cause==POLLING_SLOW) return 0;
@@ -949,11 +1039,21 @@ if (store->pollingRegisters || store->pollingIrs || store->pollingCoils || store
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)
@@ -1050,6 +1150,14 @@ else return 0;
// 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;