From 2c601b2c0300bcea30b05bd021ad52bb0aebf4b7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 23 Aug 2025 23:26:05 +0300 Subject: [PATCH] input fix for mixed configs & repeat flag, comments --- build-flags/build_flags_mega2560-optiboot | 2 +- lighthub/candriver.cpp | 165 ++++++++++++++++++++++ lighthub/inputs.cpp | 91 ++++++------ lighthub/inputs.h | 2 +- lighthub/modules/out_modbus.cpp | 110 ++++++++++++++- 5 files changed, 326 insertions(+), 44 deletions(-) diff --git a/build-flags/build_flags_mega2560-optiboot b/build-flags/build_flags_mega2560-optiboot index 2886663..1b7da82 100644 --- a/build-flags/build_flags_mega2560-optiboot +++ b/build-flags/build_flags_mega2560-optiboot @@ -18,7 +18,7 @@ -DMULTIVENT_DISABLE #-DWiz5100 -DARDUINO_OTA_MDNS_DISABLE --DMDNS_ENABLE +#-DMDNS_ENABLE -DHSV_DISABLE -DPWM_DISABLE diff --git a/lighthub/candriver.cpp b/lighthub/candriver.cpp index 2c6a52b..ac45b3a 100644 --- a/lighthub/candriver.cpp +++ b/lighthub/candriver.cpp @@ -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) diff --git a/lighthub/inputs.cpp b/lighthub/inputs.cpp index 7f324cc..9b60bd1 100644 --- a/lighthub/inputs.cpp +++ b/lighthub/inputs.cpp @@ -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<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<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<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<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<") << "ON"<type == aJson_String) && strlen(scmd->valuestring)) + {mqttClient.publish(addrstr, scmd->valuestring, true);debugSerial<") << scmd->valuestring<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<") << "OFF"<type == aJson_String) && strlen(rcmd->valuestring)) {mqttClient.publish(addrstr, rcmd->valuestring, true);debugSerial<") << rcmd->valuestring<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); } } diff --git a/lighthub/inputs.h b/lighthub/inputs.h index 3e9b38c..2a2eaec 100644 --- a/lighthub/inputs.h +++ b/lighthub/inputs.h @@ -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(); diff --git a/lighthub/modules/out_modbus.cpp b/lighthub/modules/out_modbus.cpp index 8ea7c1d..82d662a 100644 --- a/lighthub/modules/out_modbus.cpp +++ b/lighthub/modules/out_modbus.cpp @@ -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; ichild; @@ -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<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;