From ebcb260d5f59b30c5f69121ee09c099cd09826a5 Mon Sep 17 00:00:00 2001 From: john30 Date: Sat, 14 Jan 2017 18:01:37 +0100 Subject: [PATCH] use spaces instead of tab --- src/ebusd/bushandler.cpp | 1960 ++++++------- src/ebusd/bushandler.h | 854 +++--- src/ebusd/datahandler.cpp | 40 +- src/ebusd/datahandler.h | 116 +- src/ebusd/main.cpp | 1536 +++++----- src/ebusd/main.h | 64 +- src/ebusd/mainloop.cpp | 2776 +++++++++--------- src/ebusd/mainloop.h | 348 +-- src/ebusd/mqtthandler.cpp | 674 ++--- src/ebusd/mqtthandler.h | 118 +- src/ebusd/network.cpp | 396 +-- src/ebusd/network.h | 416 +-- src/lib/ebus/contrib/contrib.cpp | 4 +- src/lib/ebus/contrib/tem.cpp | 182 +- src/lib/ebus/contrib/tem.h | 34 +- src/lib/ebus/contrib/test/test_tem.cpp | 306 +- src/lib/ebus/data.cpp | 1754 +++++------ src/lib/ebus/data.h | 966 +++--- src/lib/ebus/datatype.cpp | 1868 ++++++------ src/lib/ebus/datatype.h | 682 ++--- src/lib/ebus/device.cpp | 436 +-- src/lib/ebus/device.h | 354 +-- src/lib/ebus/filereader.h | 596 ++-- src/lib/ebus/message.cpp | 3742 ++++++++++++------------ src/lib/ebus/message.h | 2160 +++++++------- src/lib/ebus/result.cpp | 66 +- src/lib/ebus/result.h | 52 +- src/lib/ebus/symbol.cpp | 356 +-- src/lib/ebus/symbol.h | 234 +- src/lib/ebus/test/test_data.cpp | 1162 ++++---- src/lib/ebus/test/test_device.cpp | 58 +- src/lib/ebus/test/test_filereader.cpp | 122 +- src/lib/ebus/test/test_message.cpp | 754 ++--- src/lib/ebus/test/test_symbol.cpp | 118 +- src/lib/utils/clock.cpp | 22 +- src/lib/utils/log.cpp | 202 +- src/lib/utils/log.h | 24 +- src/lib/utils/notify.h | 64 +- src/lib/utils/queue.h | 208 +- src/lib/utils/rotatefile.cpp | 98 +- src/lib/utils/rotatefile.h | 88 +- src/lib/utils/tcpsocket.cpp | 136 +- src/lib/utils/tcpsocket.h | 200 +- src/lib/utils/thread.cpp | 92 +- src/lib/utils/thread.h | 164 +- src/tools/ebusctl.cpp | 366 +-- src/tools/ebusfeed.cpp | 182 +- 47 files changed, 13575 insertions(+), 13575 deletions(-) diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp index 23ab2058..5fe7dbd2 100644 --- a/src/ebusd/bushandler.cpp +++ b/src/ebusd/bushandler.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "bushandler.h" @@ -50,1101 +50,1101 @@ using std::setw; * @return the string corresponding to the @a BusState. */ const char* getStateCode(BusState state) { - switch (state) { - case bs_noSignal: return "no signal"; - case bs_skip: return "skip"; - case bs_ready: return "ready"; - case bs_sendCmd: return "send command"; - case bs_recvCmdAck: return "receive command ACK"; - case bs_recvRes: return "receive response"; - case bs_sendResAck: return "send response ACK"; - case bs_recvCmd: return "receive command"; - case bs_recvResAck: return "receive response ACK"; - case bs_sendCmdAck: return "send command ACK"; - case bs_sendRes: return "send response"; - case bs_sendSyn: return "send SYN"; - default: return "unknown"; - } + switch (state) { + case bs_noSignal: return "no signal"; + case bs_skip: return "skip"; + case bs_ready: return "ready"; + case bs_sendCmd: return "send command"; + case bs_recvCmdAck: return "receive command ACK"; + case bs_recvRes: return "receive response"; + case bs_sendResAck: return "send response ACK"; + case bs_recvCmd: return "receive command"; + case bs_recvResAck: return "receive response ACK"; + case bs_sendCmdAck: return "send command ACK"; + case bs_sendRes: return "send response"; + case bs_sendSyn: return "send SYN"; + default: return "unknown"; + } } result_t PollRequest::prepare(unsigned char ownMasterAddress) { - istringstream input; - result_t result = m_message->prepareMaster(ownMasterAddress, m_master, input, UI_FIELD_SEPARATOR, SYN, m_index); - if (result == RESULT_OK) { - logInfo(lf_bus, "poll cmd: %s", m_master.getDataStr().c_str()); - } - return result; + istringstream input; + result_t result = m_message->prepareMaster(ownMasterAddress, m_master, input, UI_FIELD_SEPARATOR, SYN, m_index); + if (result == RESULT_OK) { + logInfo(lf_bus, "poll cmd: %s", m_master.getDataStr().c_str()); + } + return result; } bool PollRequest::notify(result_t result, SymbolString& slave) { - if (result == RESULT_OK) { - result = m_message->storeLastData(pt_slaveData, slave, m_index); - if (result >= RESULT_OK && m_index+1 < m_message->getCount()) { - m_index++; - result = prepare(m_master[0]); - if (result >= RESULT_OK) { - return true; - } - } - } - ostringstream output; - if (result == RESULT_OK) { - result = m_message->decodeLastData(output); // decode data - } - if (result < RESULT_OK) { - logError(lf_bus, "poll %s %s failed: %s", m_message->getCircuit().c_str(), m_message->getName().c_str(), getResultCode(result)); - } else { - logNotice(lf_bus, "poll %s %s: %s", m_message->getCircuit().c_str(), m_message->getName().c_str(), output.str().c_str()); - } - return false; + if (result == RESULT_OK) { + result = m_message->storeLastData(pt_slaveData, slave, m_index); + if (result >= RESULT_OK && m_index+1 < m_message->getCount()) { + m_index++; + result = prepare(m_master[0]); + if (result >= RESULT_OK) { + return true; + } + } + } + ostringstream output; + if (result == RESULT_OK) { + result = m_message->decodeLastData(output); // decode data + } + if (result < RESULT_OK) { + logError(lf_bus, "poll %s %s failed: %s", m_message->getCircuit().c_str(), m_message->getName().c_str(), getResultCode(result)); + } else { + logNotice(lf_bus, "poll %s %s: %s", m_message->getCircuit().c_str(), m_message->getName().c_str(), output.str().c_str()); + } + return false; } result_t ScanRequest::prepare(unsigned char ownMasterAddress) { - if (m_slaves.empty()) { - return RESULT_ERR_EOF; - } - unsigned char dstAddress = m_slaves.front(); - if (m_index == 0 && m_messages.size() == m_allMessages.size()) { // first message for this address - m_busHandler->setScanResult(dstAddress, ""); - } - istringstream input; - result_t result = m_message->prepareMaster(ownMasterAddress, m_master, input, UI_FIELD_SEPARATOR, dstAddress, m_index); - if (result >= RESULT_OK) { - logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getDataStr().c_str()); - } - return result; + if (m_slaves.empty()) { + return RESULT_ERR_EOF; + } + unsigned char dstAddress = m_slaves.front(); + if (m_index == 0 && m_messages.size() == m_allMessages.size()) { // first message for this address + m_busHandler->setScanResult(dstAddress, ""); + } + istringstream input; + result_t result = m_message->prepareMaster(ownMasterAddress, m_master, input, UI_FIELD_SEPARATOR, dstAddress, m_index); + if (result >= RESULT_OK) { + logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getDataStr().c_str()); + } + return result; } bool ScanRequest::notify(result_t result, SymbolString& slave) { - unsigned char dstAddress = m_master[1]; - if (result == RESULT_OK) { - if (m_message == m_messageMap->getScanMessage()) { - Message* message = m_messageMap->getScanMessage(dstAddress); - if (message != NULL) { - m_message = message; - m_message->storeLastData(pt_masterData, m_master, m_index); // expected to work since this is a clone - } - } else if (m_message->getDstAddress() == SYN) { - m_message = m_message->derive(dstAddress, true); - m_messageMap->add(m_message); - m_message->storeLastData(pt_masterData, m_master, m_index); // expected to work since this is a clone - } - result = m_message->storeLastData(pt_slaveData, slave, m_index); - if (result >= RESULT_OK && m_index+1 < m_message->getCount()) { - m_index++; - result = prepare(m_master[0]); - if (result >= RESULT_OK) { - return true; - } - } - if (result == RESULT_OK) { - result = m_message->decodeLastData(m_scanResult, 0, true); // decode data - } - } - if (result < RESULT_OK) { - if (!m_slaves.empty()) { - m_slaves.pop_front(); - } - if (result == RESULT_ERR_TIMEOUT) { - logNotice(lf_bus, "scan %2.2x timed out (%d slaves left)", dstAddress, m_slaves.size()); - } else { - logError(lf_bus, "scan %2.2x failed (%d slaves left): %s", dstAddress, m_slaves.size(), getResultCode(result)); - } - m_messages.clear(); // skip remaining secondary messages - } else if (m_messages.empty()) { - if (!m_slaves.empty()) { - m_slaves.pop_front(); - } - logNotice(lf_bus, "scan %2.2x completed (%d slaves left)", dstAddress, m_slaves.size()); - } - if (m_messages.empty()) { // last message for this address - m_busHandler->setScanResult(dstAddress, m_scanResult.str()); - } - if (m_slaves.empty()) { - logNotice(lf_bus, "scan finished"); - m_busHandler->setScanFinished(); - return false; - } - if (m_messages.empty()) { - m_messages = m_allMessages; - m_scanResult.str(""); - m_scanResult.clear(); - } - m_index = 0; - m_message = m_messages.front(); - m_messages.pop_front(); - if (prepare(m_master[0]) < RESULT_OK) { - m_busHandler->setScanFinished(); - return false; // give up - } - return true; + unsigned char dstAddress = m_master[1]; + if (result == RESULT_OK) { + if (m_message == m_messageMap->getScanMessage()) { + Message* message = m_messageMap->getScanMessage(dstAddress); + if (message != NULL) { + m_message = message; + m_message->storeLastData(pt_masterData, m_master, m_index); // expected to work since this is a clone + } + } else if (m_message->getDstAddress() == SYN) { + m_message = m_message->derive(dstAddress, true); + m_messageMap->add(m_message); + m_message->storeLastData(pt_masterData, m_master, m_index); // expected to work since this is a clone + } + result = m_message->storeLastData(pt_slaveData, slave, m_index); + if (result >= RESULT_OK && m_index+1 < m_message->getCount()) { + m_index++; + result = prepare(m_master[0]); + if (result >= RESULT_OK) { + return true; + } + } + if (result == RESULT_OK) { + result = m_message->decodeLastData(m_scanResult, 0, true); // decode data + } + } + if (result < RESULT_OK) { + if (!m_slaves.empty()) { + m_slaves.pop_front(); + } + if (result == RESULT_ERR_TIMEOUT) { + logNotice(lf_bus, "scan %2.2x timed out (%d slaves left)", dstAddress, m_slaves.size()); + } else { + logError(lf_bus, "scan %2.2x failed (%d slaves left): %s", dstAddress, m_slaves.size(), getResultCode(result)); + } + m_messages.clear(); // skip remaining secondary messages + } else if (m_messages.empty()) { + if (!m_slaves.empty()) { + m_slaves.pop_front(); + } + logNotice(lf_bus, "scan %2.2x completed (%d slaves left)", dstAddress, m_slaves.size()); + } + if (m_messages.empty()) { // last message for this address + m_busHandler->setScanResult(dstAddress, m_scanResult.str()); + } + if (m_slaves.empty()) { + logNotice(lf_bus, "scan finished"); + m_busHandler->setScanFinished(); + return false; + } + if (m_messages.empty()) { + m_messages = m_allMessages; + m_scanResult.str(""); + m_scanResult.clear(); + } + m_index = 0; + m_message = m_messages.front(); + m_messages.pop_front(); + if (prepare(m_master[0]) < RESULT_OK) { + m_busHandler->setScanFinished(); + return false; // give up + } + return true; } bool ActiveBusRequest::notify(result_t result, SymbolString& slave) { - if (result == RESULT_OK) { - logDebug(lf_bus, "read res: %s", slave.getDataStr().c_str()); - } - m_result = result; - m_slave.addAll(slave, true); - return false; + if (result == RESULT_OK) { + logDebug(lf_bus, "read res: %s", slave.getDataStr().c_str()); + } + m_result = result; + m_slave.addAll(slave, true); + return false; } void GrabbedMessage::setLastData(SymbolString& master, SymbolString& slave) { - m_lastMaster.clear(false); - m_lastMaster.addAll(master); - m_lastSlave.clear(false); - m_lastSlave.addAll(slave); - m_count++; + m_lastMaster.clear(false); + m_lastMaster.addAll(master); + m_lastSlave.clear(false); + m_lastSlave.addAll(slave); + m_count++; } bool GrabbedMessage::dump(const bool unknown, MessageMap* messages, bool first, ostringstream& output) { - Message* message = messages->find(m_lastMaster); - if (unknown && message) { - return false; - } - if (!first) { - output << endl; - } - unsigned char dstAddress = m_lastMaster[1]; - output << m_lastMaster.getDataStr(); - if (dstAddress != BROADCAST && !isMaster(dstAddress)) { - output << " / " << m_lastSlave.getDataStr(); - } - output << " = " << static_cast(m_count); - if (message) { - output << ": " << message->getCircuit() << " " << message->getName(); - } - return true; + Message* message = messages->find(m_lastMaster); + if (unknown && message) { + return false; + } + if (!first) { + output << endl; + } + unsigned char dstAddress = m_lastMaster[1]; + output << m_lastMaster.getDataStr(); + if (dstAddress != BROADCAST && !isMaster(dstAddress)) { + output << " / " << m_lastSlave.getDataStr(); + } + output << " = " << static_cast(m_count); + if (message) { + output << ": " << message->getCircuit() << " " << message->getName(); + } + return true; } void BusHandler::clear() { - memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); - m_masterCount = 1; - m_scanResults.clear(); + memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); + m_masterCount = 1; + m_scanResults.clear(); } result_t BusHandler::sendAndWait(SymbolString& master, SymbolString& slave) { - result_t result = RESULT_ERR_NO_SIGNAL; - slave.clear(); - ActiveBusRequest request(master, slave); - logInfo(lf_bus, "send message: %s", master.getDataStr().c_str()); + result_t result = RESULT_ERR_NO_SIGNAL; + slave.clear(); + ActiveBusRequest request(master, slave); + logInfo(lf_bus, "send message: %s", master.getDataStr().c_str()); - for (int sendRetries = m_failedSendRetries + 1; sendRetries >= 0; sendRetries--) { - m_nextRequests.push(&request); - bool success = m_finishedRequests.remove(&request, true); - result = success ? request.m_result : RESULT_ERR_TIMEOUT; - if (result == RESULT_OK) { - Message* message = m_messages->find(master); - if (message != NULL) { - m_messages->invalidateCache(message); - } - break; - } - if (!success || result == RESULT_ERR_NO_SIGNAL || result == RESULT_ERR_SEND || result == RESULT_ERR_DEVICE) { - logError(lf_bus, "send to %2.2x: %s, give up", master[1], getResultCode(result)); - break; - } - logError(lf_bus, "send to %2.2x: %s%s", master[1], getResultCode(result), sendRetries > 0 ? ", retry" : ""); - request.m_busLostRetries = 0; - } - return result; + for (int sendRetries = m_failedSendRetries + 1; sendRetries >= 0; sendRetries--) { + m_nextRequests.push(&request); + bool success = m_finishedRequests.remove(&request, true); + result = success ? request.m_result : RESULT_ERR_TIMEOUT; + if (result == RESULT_OK) { + Message* message = m_messages->find(master); + if (message != NULL) { + m_messages->invalidateCache(message); + } + break; + } + if (!success || result == RESULT_ERR_NO_SIGNAL || result == RESULT_ERR_SEND || result == RESULT_ERR_DEVICE) { + logError(lf_bus, "send to %2.2x: %s, give up", master[1], getResultCode(result)); + break; + } + logError(lf_bus, "send to %2.2x: %s%s", master[1], getResultCode(result), sendRetries > 0 ? ", retry" : ""); + request.m_busLostRetries = 0; + } + return result; } result_t BusHandler::readFromBus(Message* message, string inputStr, const unsigned char dstAddress) { - result_t ret = RESULT_EMPTY; - SymbolString master(true); - SymbolString slave(false); - for (unsigned char index = 0; index < message->getCount(); index++) { - istringstream input(inputStr); - ret = message->prepareMaster(m_ownMasterAddress, master, input, UI_FIELD_SEPARATOR, dstAddress, index); - if (ret != RESULT_OK) { - logError(lf_bus, "prepare message part %d: %s", index, getResultCode(ret)); - break; - } - // send message - ret = sendAndWait(master, slave); - if (ret != RESULT_OK) { - logError(lf_bus, "send message part %d: %s", index, getResultCode(ret)); - break; - } - ret = message->storeLastData(pt_slaveData, slave, index); - if (ret < RESULT_OK) { - logError(lf_bus, "store message part %d: %s", index, getResultCode(ret)); - break; - } - } - return ret; + result_t ret = RESULT_EMPTY; + SymbolString master(true); + SymbolString slave(false); + for (unsigned char index = 0; index < message->getCount(); index++) { + istringstream input(inputStr); + ret = message->prepareMaster(m_ownMasterAddress, master, input, UI_FIELD_SEPARATOR, dstAddress, index); + if (ret != RESULT_OK) { + logError(lf_bus, "prepare message part %d: %s", index, getResultCode(ret)); + break; + } + // send message + ret = sendAndWait(master, slave); + if (ret != RESULT_OK) { + logError(lf_bus, "send message part %d: %s", index, getResultCode(ret)); + break; + } + ret = message->storeLastData(pt_slaveData, slave, index); + if (ret < RESULT_OK) { + logError(lf_bus, "store message part %d: %s", index, getResultCode(ret)); + break; + } + } + return ret; } void BusHandler::run() { - unsigned int symCount = 0; - time_t lastTime; - time(&lastTime); - do { - if (m_device->isValid() && !m_reconnect) { - result_t result = handleSymbol(); - if (result != RESULT_ERR_TIMEOUT) { - symCount++; - } - time_t now; - time(&now); - if (now > lastTime) { - m_symPerSec = symCount / (unsigned int)(now-lastTime); - if (m_symPerSec > m_maxSymPerSec) { - m_maxSymPerSec = m_symPerSec; - if (m_maxSymPerSec > 100) { - logNotice(lf_bus, "max. symbols per second: %d", m_maxSymPerSec); - } - } - lastTime = now; - symCount = 0; - } - } else { - if (!Wait(10)) { - break; - } - m_reconnect = false; - result_t result = m_device->open(); - if (result == RESULT_OK) { - logNotice(lf_bus, "re-opened %s", m_device->getName()); - } else { - logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); - setState(bs_noSignal, result); - } - symCount = 0; - } - } while (isRunning()); + unsigned int symCount = 0; + time_t lastTime; + time(&lastTime); + do { + if (m_device->isValid() && !m_reconnect) { + result_t result = handleSymbol(); + if (result != RESULT_ERR_TIMEOUT) { + symCount++; + } + time_t now; + time(&now); + if (now > lastTime) { + m_symPerSec = symCount / (unsigned int)(now-lastTime); + if (m_symPerSec > m_maxSymPerSec) { + m_maxSymPerSec = m_symPerSec; + if (m_maxSymPerSec > 100) { + logNotice(lf_bus, "max. symbols per second: %d", m_maxSymPerSec); + } + } + lastTime = now; + symCount = 0; + } + } else { + if (!Wait(10)) { + break; + } + m_reconnect = false; + result_t result = m_device->open(); + if (result == RESULT_OK) { + logNotice(lf_bus, "re-opened %s", m_device->getName()); + } else { + logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); + setState(bs_noSignal, result); + } + symCount = 0; + } + } while (isRunning()); } result_t BusHandler::handleSymbol() { - unsigned int timeout = SYN_TIMEOUT; - unsigned char sendSymbol = ESC; - bool sending = false; - BusRequest* startRequest = NULL; + unsigned int timeout = SYN_TIMEOUT; + unsigned char sendSymbol = ESC; + bool sending = false; + BusRequest* startRequest = NULL; - // check if another symbol has to be sent and determine timeout for receive - switch (m_state) { - case bs_noSignal: - timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT; - break; + // check if another symbol has to be sent and determine timeout for receive + switch (m_state) { + case bs_noSignal: + timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT; + break; - case bs_skip: - timeout = SYN_TIMEOUT; - break; + case bs_skip: + timeout = SYN_TIMEOUT; + break; - case bs_ready: - if (m_currentRequest != NULL) { - setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up - } else if (m_remainLockCount == 0) { - startRequest = m_nextRequests.peek(); - if (startRequest == NULL && m_pollInterval > 0) { // check for poll/scan - time_t now; - time(&now); - if (m_lastPoll == 0 || difftime(now, m_lastPoll) > m_pollInterval) { - Message* message = m_messages->getNextPoll(); - if (message != NULL) { - m_lastPoll = now; - PollRequest* request = new PollRequest(message); - result_t ret = request->prepare(m_ownMasterAddress); - if (ret != RESULT_OK) { - logError(lf_bus, "prepare poll message: %s", getResultCode(ret)); - delete request; - } else { - startRequest = request; - m_nextRequests.push(request); - } - } - } - } - if (startRequest != NULL) { // initiate arbitration - sendSymbol = m_ownMasterAddress; - sending = true; - } - } - break; + case bs_ready: + if (m_currentRequest != NULL) { + setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up + } else if (m_remainLockCount == 0) { + startRequest = m_nextRequests.peek(); + if (startRequest == NULL && m_pollInterval > 0) { // check for poll/scan + time_t now; + time(&now); + if (m_lastPoll == 0 || difftime(now, m_lastPoll) > m_pollInterval) { + Message* message = m_messages->getNextPoll(); + if (message != NULL) { + m_lastPoll = now; + PollRequest* request = new PollRequest(message); + result_t ret = request->prepare(m_ownMasterAddress); + if (ret != RESULT_OK) { + logError(lf_bus, "prepare poll message: %s", getResultCode(ret)); + delete request; + } else { + startRequest = request; + m_nextRequests.push(request); + } + } + } + } + if (startRequest != NULL) { // initiate arbitration + sendSymbol = m_ownMasterAddress; + sending = true; + } + } + break; - case bs_recvCmd: - case bs_recvCmdAck: - timeout = m_slaveRecvTimeout; - break; + case bs_recvCmd: + case bs_recvCmdAck: + timeout = m_slaveRecvTimeout; + break; - case bs_recvRes: - if (m_response.size() > 0 || m_slaveRecvTimeout > SYN_TIMEOUT) { - timeout = m_slaveRecvTimeout; - } else { - timeout = SYN_TIMEOUT; - } - break; + case bs_recvRes: + if (m_response.size() > 0 || m_slaveRecvTimeout > SYN_TIMEOUT) { + timeout = m_slaveRecvTimeout; + } else { + timeout = SYN_TIMEOUT; + } + break; - case bs_recvResAck: - timeout = m_slaveRecvTimeout+m_transferLatency; - break; + case bs_recvResAck: + timeout = m_slaveRecvTimeout+m_transferLatency; + break; - case bs_sendCmd: - if (m_currentRequest != NULL) { - sendSymbol = m_currentRequest->m_master[m_nextSendPos]; // escaped command - sending = true; - } - break; + case bs_sendCmd: + if (m_currentRequest != NULL) { + sendSymbol = m_currentRequest->m_master[m_nextSendPos]; // escaped command + sending = true; + } + break; - case bs_sendResAck: - if (m_currentRequest != NULL) { - sendSymbol = m_responseCrcValid ? ACK : NAK; - sending = true; - } - break; + case bs_sendResAck: + if (m_currentRequest != NULL) { + sendSymbol = m_responseCrcValid ? ACK : NAK; + sending = true; + } + break; - case bs_sendCmdAck: - if (m_answer) { - sendSymbol = m_commandCrcValid ? ACK : NAK; - sending = true; - } - break; + case bs_sendCmdAck: + if (m_answer) { + sendSymbol = m_commandCrcValid ? ACK : NAK; + sending = true; + } + break; - case bs_sendRes: - if (m_answer) { - sendSymbol = m_response[m_nextSendPos]; // escaped response - sending = true; - } - break; + case bs_sendRes: + if (m_answer) { + sendSymbol = m_response[m_nextSendPos]; // escaped response + sending = true; + } + break; - case bs_sendSyn: - sendSymbol = SYN; - sending = true; - break; - } + case bs_sendSyn: + sendSymbol = SYN; + sending = true; + break; + } - // send symbol if necessary - result_t result; - if (sending) { - result = m_device->send(sendSymbol); - if (result == RESULT_OK) { - if (m_state == bs_ready) { - timeout = m_transferLatency+m_busAcquireTimeout; - } else { - timeout = m_transferLatency+SEND_TIMEOUT; - } - } else { - sending = false; - timeout = SYN_TIMEOUT; - if (startRequest != NULL && m_nextRequests.remove(startRequest)) { - m_currentRequest = startRequest; // force the failed request to be notified - } - setState(bs_skip, result); - } - } + // send symbol if necessary + result_t result; + if (sending) { + result = m_device->send(sendSymbol); + if (result == RESULT_OK) { + if (m_state == bs_ready) { + timeout = m_transferLatency+m_busAcquireTimeout; + } else { + timeout = m_transferLatency+SEND_TIMEOUT; + } + } else { + sending = false; + timeout = SYN_TIMEOUT; + if (startRequest != NULL && m_nextRequests.remove(startRequest)) { + m_currentRequest = startRequest; // force the failed request to be notified + } + setState(bs_skip, result); + } + } - // receive next symbol (optionally check reception of sent symbol) - unsigned char recvSymbol; - result = m_device->recv(timeout+m_transferLatency, recvSymbol); + // receive next symbol (optionally check reception of sent symbol) + unsigned char recvSymbol; + result = m_device->recv(timeout+m_transferLatency, recvSymbol); - if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 - && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { - // check if acting as AUTO-SYN generator is required - result = m_device->send(SYN); - if (result == RESULT_OK) { - recvSymbol = ESC; - result = m_device->recv(SEND_TIMEOUT, recvSymbol); - if (result == RESULT_ERR_TIMEOUT) { - return setState(bs_noSignal, result); - } - if (result != RESULT_OK) { - logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result)); - } else if (recvSymbol != SYN) { - logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol); - } else { - if (m_generateSynInterval != SYN_TIMEOUT) { - // received own AUTO-SYN symbol back again: act as AUTO-SYN generator now - m_generateSynInterval = SYN_TIMEOUT; - logNotice(lf_bus, "acting as AUTO-SYN generator"); - } - m_remainLockCount = 0; - return setState(bs_ready, result); - } - } - return setState(bs_skip, result); - } - time_t now; - time(&now); - if (result != RESULT_OK) { - if (sending && startRequest != NULL && m_nextRequests.remove(startRequest)) { - m_currentRequest = startRequest; // force the failed request to be notified - } - if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1) // at least one full second has passed since last received symbol - || m_state == bs_noSignal) { - return setState(bs_noSignal, result); - } - return setState(bs_skip, result); - } + if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 + && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { + // check if acting as AUTO-SYN generator is required + result = m_device->send(SYN); + if (result == RESULT_OK) { + recvSymbol = ESC; + result = m_device->recv(SEND_TIMEOUT, recvSymbol); + if (result == RESULT_ERR_TIMEOUT) { + return setState(bs_noSignal, result); + } + if (result != RESULT_OK) { + logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result)); + } else if (recvSymbol != SYN) { + logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol); + } else { + if (m_generateSynInterval != SYN_TIMEOUT) { + // received own AUTO-SYN symbol back again: act as AUTO-SYN generator now + m_generateSynInterval = SYN_TIMEOUT; + logNotice(lf_bus, "acting as AUTO-SYN generator"); + } + m_remainLockCount = 0; + return setState(bs_ready, result); + } + } + return setState(bs_skip, result); + } + time_t now; + time(&now); + if (result != RESULT_OK) { + if (sending && startRequest != NULL && m_nextRequests.remove(startRequest)) { + m_currentRequest = startRequest; // force the failed request to be notified + } + if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1) // at least one full second has passed since last received symbol + || m_state == bs_noSignal) { + return setState(bs_noSignal, result); + } + return setState(bs_skip, result); + } - m_lastReceive = now; - if ((recvSymbol == SYN) && (m_state != bs_sendSyn)) { - if (!sending && m_remainLockCount > 0 && m_command.size() != 1) { - m_remainLockCount--; - } else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) { - m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority) - } - return setState(bs_ready, m_state == bs_skip ? RESULT_OK : RESULT_ERR_SYN); - } + m_lastReceive = now; + if ((recvSymbol == SYN) && (m_state != bs_sendSyn)) { + if (!sending && m_remainLockCount > 0 && m_command.size() != 1) { + m_remainLockCount--; + } else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) { + m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority) + } + return setState(bs_ready, m_state == bs_skip ? RESULT_OK : RESULT_ERR_SYN); + } - unsigned int headerLen, crcPos; + unsigned int headerLen, crcPos; - switch (m_state) { - case bs_noSignal: - return setState(bs_skip, RESULT_OK); + switch (m_state) { + case bs_noSignal: + return setState(bs_skip, RESULT_OK); - case bs_skip: - return RESULT_OK; + case bs_skip: + return RESULT_OK; - case bs_ready: - if (startRequest != NULL && sending) { - if (!m_nextRequests.remove(startRequest)) { - // request already removed (e.g. due to timeout) - return setState(bs_skip, RESULT_ERR_TIMEOUT); - } - m_currentRequest = startRequest; - // check arbitration - if (recvSymbol == sendSymbol) { // arbitration successful - m_nextSendPos = 1; - m_repeat = false; - return setState(bs_sendCmd, RESULT_OK); - } - // arbitration lost. if same priority class found, try again after next AUTO-SYN - m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try - if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) { - // if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN) - m_remainLockCount = m_lockCount; - } - setState(m_state, RESULT_ERR_BUS_LOST); // try again later - } - result = m_command.push_back(recvSymbol, false); // expect no escaping for master address - if (result < RESULT_OK) { - return setState(bs_skip, result); - } - m_repeat = false; - return setState(bs_recvCmd, RESULT_OK); + case bs_ready: + if (startRequest != NULL && sending) { + if (!m_nextRequests.remove(startRequest)) { + // request already removed (e.g. due to timeout) + return setState(bs_skip, RESULT_ERR_TIMEOUT); + } + m_currentRequest = startRequest; + // check arbitration + if (recvSymbol == sendSymbol) { // arbitration successful + m_nextSendPos = 1; + m_repeat = false; + return setState(bs_sendCmd, RESULT_OK); + } + // arbitration lost. if same priority class found, try again after next AUTO-SYN + m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try + if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) { + // if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN) + m_remainLockCount = m_lockCount; + } + setState(m_state, RESULT_ERR_BUS_LOST); // try again later + } + result = m_command.push_back(recvSymbol, false); // expect no escaping for master address + if (result < RESULT_OK) { + return setState(bs_skip, result); + } + m_repeat = false; + return setState(bs_recvCmd, RESULT_OK); - case bs_recvCmd: - headerLen = 4; - crcPos = m_command.size() > headerLen ? headerLen + 1 + m_command[headerLen] : 0xff; // header symbols are never escaped - result = m_command.push_back(recvSymbol, true, m_command.size() < crcPos); - if (result < RESULT_OK) { - return setState(bs_skip, result); - } - if (result == RESULT_OK && crcPos != 0xff && m_command.size() == crcPos + 1) { // CRC received - unsigned char dstAddress = m_command[1]; - m_commandCrcValid = m_command[headerLen + 1 + m_command[headerLen]] == m_command.getCRC(); // header symbols are never escaped - if (m_commandCrcValid) { - if (dstAddress == BROADCAST) { - receiveCompleted(); - return setState(bs_skip, RESULT_OK); - } - addSeenAddress(m_command[0]); - if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) { - return setState(bs_sendCmdAck, RESULT_OK); - } - return setState(bs_recvCmdAck, RESULT_OK); - } - if (dstAddress == BROADCAST) { - return setState(bs_skip, RESULT_ERR_CRC); - } - if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) { - return setState(bs_sendCmdAck, RESULT_ERR_CRC); - } - if (m_repeat) { - return setState(bs_skip, RESULT_ERR_CRC); - } - return setState(bs_recvCmdAck, RESULT_ERR_CRC); - } - return RESULT_OK; + case bs_recvCmd: + headerLen = 4; + crcPos = m_command.size() > headerLen ? headerLen + 1 + m_command[headerLen] : 0xff; // header symbols are never escaped + result = m_command.push_back(recvSymbol, true, m_command.size() < crcPos); + if (result < RESULT_OK) { + return setState(bs_skip, result); + } + if (result == RESULT_OK && crcPos != 0xff && m_command.size() == crcPos + 1) { // CRC received + unsigned char dstAddress = m_command[1]; + m_commandCrcValid = m_command[headerLen + 1 + m_command[headerLen]] == m_command.getCRC(); // header symbols are never escaped + if (m_commandCrcValid) { + if (dstAddress == BROADCAST) { + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } + addSeenAddress(m_command[0]); + if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) { + return setState(bs_sendCmdAck, RESULT_OK); + } + return setState(bs_recvCmdAck, RESULT_OK); + } + if (dstAddress == BROADCAST) { + return setState(bs_skip, RESULT_ERR_CRC); + } + if (m_answer && (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress)) { + return setState(bs_sendCmdAck, RESULT_ERR_CRC); + } + if (m_repeat) { + return setState(bs_skip, RESULT_ERR_CRC); + } + return setState(bs_recvCmdAck, RESULT_ERR_CRC); + } + return RESULT_OK; - case bs_recvCmdAck: - if (recvSymbol == ACK) { - if (!m_commandCrcValid) { - return setState(bs_skip, RESULT_ERR_ACK); - } - if (m_currentRequest != NULL) { - if (isMaster(m_currentRequest->m_master[1])) { - return setState(bs_sendSyn, RESULT_OK); - } - } else if (isMaster(m_command[1])) { // header symbols are never escaped - receiveCompleted(); - return setState(bs_skip, RESULT_OK); - } + case bs_recvCmdAck: + if (recvSymbol == ACK) { + if (!m_commandCrcValid) { + return setState(bs_skip, RESULT_ERR_ACK); + } + if (m_currentRequest != NULL) { + if (isMaster(m_currentRequest->m_master[1])) { + return setState(bs_sendSyn, RESULT_OK); + } + } else if (isMaster(m_command[1])) { // header symbols are never escaped + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } - m_repeat = false; - return setState(bs_recvRes, RESULT_OK); - } - if (recvSymbol == NAK) { - if (!m_repeat) { - m_repeat = true; - m_nextSendPos = 0; - m_command.clear(); - if (m_currentRequest != NULL) { - return setState(bs_sendCmd, RESULT_ERR_NAK, true); - } - return setState(bs_recvCmd, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_ACK); + m_repeat = false; + return setState(bs_recvRes, RESULT_OK); + } + if (recvSymbol == NAK) { + if (!m_repeat) { + m_repeat = true; + m_nextSendPos = 0; + m_command.clear(); + if (m_currentRequest != NULL) { + return setState(bs_sendCmd, RESULT_ERR_NAK, true); + } + return setState(bs_recvCmd, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_ACK); - case bs_recvRes: - headerLen = 0; - crcPos = m_response.size() > headerLen ? headerLen + 1 + m_response[headerLen] : 0xff; - result = m_response.push_back(recvSymbol, true, m_response.size() < crcPos); - if (result < RESULT_OK) { - return setState(bs_skip, result); - } - if (result == RESULT_OK && crcPos != 0xff && m_response.size() == crcPos + 1) { // CRC received - m_responseCrcValid = m_response[headerLen + 1 + m_response[headerLen]] == m_response.getCRC(); - if (m_responseCrcValid) { - if (m_currentRequest != NULL) { - return setState(bs_sendResAck, RESULT_OK); - } - return setState(bs_recvResAck, RESULT_OK); - } - if (m_repeat) { - if (m_currentRequest != NULL) { - return setState(bs_sendSyn, RESULT_ERR_CRC); - } - return setState(bs_skip, RESULT_ERR_CRC); - } - if (m_currentRequest != NULL) { - return setState(bs_sendResAck, RESULT_ERR_CRC); - } - return setState(bs_recvResAck, RESULT_ERR_CRC); - } - return RESULT_OK; + case bs_recvRes: + headerLen = 0; + crcPos = m_response.size() > headerLen ? headerLen + 1 + m_response[headerLen] : 0xff; + result = m_response.push_back(recvSymbol, true, m_response.size() < crcPos); + if (result < RESULT_OK) { + return setState(bs_skip, result); + } + if (result == RESULT_OK && crcPos != 0xff && m_response.size() == crcPos + 1) { // CRC received + m_responseCrcValid = m_response[headerLen + 1 + m_response[headerLen]] == m_response.getCRC(); + if (m_responseCrcValid) { + if (m_currentRequest != NULL) { + return setState(bs_sendResAck, RESULT_OK); + } + return setState(bs_recvResAck, RESULT_OK); + } + if (m_repeat) { + if (m_currentRequest != NULL) { + return setState(bs_sendSyn, RESULT_ERR_CRC); + } + return setState(bs_skip, RESULT_ERR_CRC); + } + if (m_currentRequest != NULL) { + return setState(bs_sendResAck, RESULT_ERR_CRC); + } + return setState(bs_recvResAck, RESULT_ERR_CRC); + } + return RESULT_OK; - case bs_recvResAck: - if (recvSymbol == ACK) { - if (!m_responseCrcValid) { - return setState(bs_skip, RESULT_ERR_ACK); - } - receiveCompleted(); - return setState(bs_skip, RESULT_OK); - } - if (recvSymbol == NAK) { - if (!m_repeat) { - m_repeat = true; - m_response.clear(); - return setState(bs_recvRes, RESULT_ERR_NAK, true); - } - return setState(bs_skip, RESULT_ERR_NAK); - } - return setState(bs_skip, RESULT_ERR_ACK); + case bs_recvResAck: + if (recvSymbol == ACK) { + if (!m_responseCrcValid) { + return setState(bs_skip, RESULT_ERR_ACK); + } + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } + if (recvSymbol == NAK) { + if (!m_repeat) { + m_repeat = true; + m_response.clear(); + return setState(bs_recvRes, RESULT_ERR_NAK, true); + } + return setState(bs_skip, RESULT_ERR_NAK); + } + return setState(bs_skip, RESULT_ERR_ACK); - case bs_sendCmd: - if (m_currentRequest != NULL && sending && recvSymbol == sendSymbol) { - // successfully sent - m_nextSendPos++; - if (m_nextSendPos >= m_currentRequest->m_master.size()) { - // master data completely sent - if (m_currentRequest->m_master[1] == BROADCAST) { - return setState(bs_sendSyn, RESULT_OK); - } - m_commandCrcValid = true; - return setState(bs_recvCmdAck, RESULT_OK); - } - return RESULT_OK; - } - return setState(bs_skip, RESULT_ERR_INVALID_ARG); + case bs_sendCmd: + if (m_currentRequest != NULL && sending && recvSymbol == sendSymbol) { + // successfully sent + m_nextSendPos++; + if (m_nextSendPos >= m_currentRequest->m_master.size()) { + // master data completely sent + if (m_currentRequest->m_master[1] == BROADCAST) { + return setState(bs_sendSyn, RESULT_OK); + } + m_commandCrcValid = true; + return setState(bs_recvCmdAck, RESULT_OK); + } + return RESULT_OK; + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); - case bs_sendResAck: - if (m_currentRequest != NULL && sending && recvSymbol == sendSymbol) { - // successfully sent - if (!m_responseCrcValid) { - if (!m_repeat) { - m_repeat = true; - m_response.clear(); - return setState(bs_recvRes, RESULT_ERR_NAK, true); - } - return setState(bs_sendSyn, RESULT_ERR_ACK); - } - return setState(bs_sendSyn, RESULT_OK); - } - return setState(bs_skip, RESULT_ERR_INVALID_ARG); + case bs_sendResAck: + if (m_currentRequest != NULL && sending && recvSymbol == sendSymbol) { + // successfully sent + if (!m_responseCrcValid) { + if (!m_repeat) { + m_repeat = true; + m_response.clear(); + return setState(bs_recvRes, RESULT_ERR_NAK, true); + } + return setState(bs_sendSyn, RESULT_ERR_ACK); + } + return setState(bs_sendSyn, RESULT_OK); + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); - case bs_sendCmdAck: - if (sending && m_answer && recvSymbol == sendSymbol) { - // successfully sent - if (!m_commandCrcValid) { - if (!m_repeat) { - m_repeat = true; - m_command.clear(); - return setState(bs_recvCmd, RESULT_ERR_NAK, true); - } - return setState(bs_skip, RESULT_ERR_ACK); - } - if (isMaster(m_command[1])) { - receiveCompleted(); // decode command and store value - return setState(bs_skip, RESULT_OK); - } + case bs_sendCmdAck: + if (sending && m_answer && recvSymbol == sendSymbol) { + // successfully sent + if (!m_commandCrcValid) { + if (!m_repeat) { + m_repeat = true; + m_command.clear(); + return setState(bs_recvCmd, RESULT_ERR_NAK, true); + } + return setState(bs_skip, RESULT_ERR_ACK); + } + if (isMaster(m_command[1])) { + receiveCompleted(); // decode command and store value + return setState(bs_skip, RESULT_OK); + } - m_nextSendPos = 0; - m_repeat = false; - Message* message; - istringstream input; // TODO create input from database of internal variables - message = m_messages->find(m_command); - if (message == NULL) { - message = m_messages->find(m_command, true); - if (message != NULL && message->getSrcAddress() != SYN) { - message = NULL; - } - } - if (message == NULL || message->isWrite()) { - return setState(bs_skip, RESULT_ERR_INVALID_ARG); // don't know this request or definition has wrong direction, deny - } - if (message == m_messages->getScanMessage(m_ownSlaveAddress)) { - input.str(SCAN_ANSWER); - } - // build response and store in m_response for sending back to requesting master - m_response.clear(true); // escape while sending response - result = message->prepareSlave(input, m_response); - if (result != RESULT_OK) { - return setState(bs_skip, result); - } - return setState(bs_sendRes, RESULT_OK); - } - return setState(bs_skip, RESULT_ERR_INVALID_ARG); + m_nextSendPos = 0; + m_repeat = false; + Message* message; + istringstream input; // TODO create input from database of internal variables + message = m_messages->find(m_command); + if (message == NULL) { + message = m_messages->find(m_command, true); + if (message != NULL && message->getSrcAddress() != SYN) { + message = NULL; + } + } + if (message == NULL || message->isWrite()) { + return setState(bs_skip, RESULT_ERR_INVALID_ARG); // don't know this request or definition has wrong direction, deny + } + if (message == m_messages->getScanMessage(m_ownSlaveAddress)) { + input.str(SCAN_ANSWER); + } + // build response and store in m_response for sending back to requesting master + m_response.clear(true); // escape while sending response + result = message->prepareSlave(input, m_response); + if (result != RESULT_OK) { + return setState(bs_skip, result); + } + return setState(bs_sendRes, RESULT_OK); + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); - case bs_sendRes: - if (sending && m_answer && recvSymbol == sendSymbol) { - // successfully sent - m_nextSendPos++; - if (m_nextSendPos >= m_response.size()) { - // slave data completely sent - return setState(bs_recvResAck, RESULT_OK); - } - return RESULT_OK; - } - return setState(bs_skip, RESULT_ERR_INVALID_ARG); + case bs_sendRes: + if (sending && m_answer && recvSymbol == sendSymbol) { + // successfully sent + m_nextSendPos++; + if (m_nextSendPos >= m_response.size()) { + // slave data completely sent + return setState(bs_recvResAck, RESULT_OK); + } + return RESULT_OK; + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); - case bs_sendSyn: - if (sending && recvSymbol == sendSymbol) { - // successfully sent - return setState(bs_skip, RESULT_OK); - } - return setState(bs_skip, RESULT_ERR_INVALID_ARG); - } - return RESULT_OK; + case bs_sendSyn: + if (sending && recvSymbol == sendSymbol) { + // successfully sent + return setState(bs_skip, RESULT_OK); + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + } + return RESULT_OK; } result_t BusHandler::setState(BusState state, result_t result, bool firstRepetition) { - if (m_currentRequest != NULL) { - if (result == RESULT_ERR_BUS_LOST && m_currentRequest->m_busLostRetries < m_busLostRetries) { - logDebug(lf_bus, "%s during %s, retry", getResultCode(result), getStateCode(m_state)); - m_currentRequest->m_busLostRetries++; - m_nextRequests.push(m_currentRequest); // repeat - m_currentRequest = NULL; - } else if (state == bs_sendSyn || (result != RESULT_OK && !firstRepetition)) { - logDebug(lf_bus, "notify request: %s", getResultCode(result)); - unsigned char dstAddress = m_currentRequest->m_master[1]; - if (result == RESULT_OK) { - addSeenAddress(dstAddress); - } - bool restart = m_currentRequest->notify( - result == RESULT_ERR_SYN && (m_state == bs_recvCmdAck || m_state == bs_recvRes) ? RESULT_ERR_TIMEOUT : result, m_response); - if (restart) { - m_currentRequest->m_busLostRetries = 0; - m_nextRequests.push(m_currentRequest); - } else if (m_currentRequest->m_deleteOnFinish) { - delete m_currentRequest; - } else { - m_finishedRequests.push(m_currentRequest); - } - m_currentRequest = NULL; - } - } + if (m_currentRequest != NULL) { + if (result == RESULT_ERR_BUS_LOST && m_currentRequest->m_busLostRetries < m_busLostRetries) { + logDebug(lf_bus, "%s during %s, retry", getResultCode(result), getStateCode(m_state)); + m_currentRequest->m_busLostRetries++; + m_nextRequests.push(m_currentRequest); // repeat + m_currentRequest = NULL; + } else if (state == bs_sendSyn || (result != RESULT_OK && !firstRepetition)) { + logDebug(lf_bus, "notify request: %s", getResultCode(result)); + unsigned char dstAddress = m_currentRequest->m_master[1]; + if (result == RESULT_OK) { + addSeenAddress(dstAddress); + } + bool restart = m_currentRequest->notify( + result == RESULT_ERR_SYN && (m_state == bs_recvCmdAck || m_state == bs_recvRes) ? RESULT_ERR_TIMEOUT : result, m_response); + if (restart) { + m_currentRequest->m_busLostRetries = 0; + m_nextRequests.push(m_currentRequest); + } else if (m_currentRequest->m_deleteOnFinish) { + delete m_currentRequest; + } else { + m_finishedRequests.push(m_currentRequest); + } + m_currentRequest = NULL; + } + } - if (state == bs_noSignal) { // notify all requests - m_response.clear(false); // notify with empty response - while ((m_currentRequest = m_nextRequests.pop()) != NULL) { - bool restart = m_currentRequest->notify(RESULT_ERR_NO_SIGNAL, m_response); - if (restart) { // should not occur with no signal - m_currentRequest->m_busLostRetries = 0; - m_nextRequests.push(m_currentRequest); - } else if (m_currentRequest->m_deleteOnFinish) { - delete m_currentRequest; - } else { - m_finishedRequests.push(m_currentRequest); - } - } - } + if (state == bs_noSignal) { // notify all requests + m_response.clear(false); // notify with empty response + while ((m_currentRequest = m_nextRequests.pop()) != NULL) { + bool restart = m_currentRequest->notify(RESULT_ERR_NO_SIGNAL, m_response); + if (restart) { // should not occur with no signal + m_currentRequest->m_busLostRetries = 0; + m_nextRequests.push(m_currentRequest); + } else if (m_currentRequest->m_deleteOnFinish) { + delete m_currentRequest; + } else { + m_finishedRequests.push(m_currentRequest); + } + } + } - if (state == m_state) { - return result; - } - if (result < RESULT_OK || (result != RESULT_OK && state == bs_skip)) { - logDebug(lf_bus, "%s during %s, switching to %s", getResultCode(result), getStateCode(m_state), getStateCode(state)); - } else if (m_currentRequest != NULL || state == bs_sendCmd || state == bs_sendResAck || state == bs_sendSyn) { - logDebug(lf_bus, "switching from %s to %s", getStateCode(m_state), getStateCode(state)); - } - if (state == bs_noSignal) { - logError(lf_bus, "signal lost"); - } else if (m_state == bs_noSignal) { - logNotice(lf_bus, "signal acquired"); - } - m_state = state; + if (state == m_state) { + return result; + } + if (result < RESULT_OK || (result != RESULT_OK && state == bs_skip)) { + logDebug(lf_bus, "%s during %s, switching to %s", getResultCode(result), getStateCode(m_state), getStateCode(state)); + } else if (m_currentRequest != NULL || state == bs_sendCmd || state == bs_sendResAck || state == bs_sendSyn) { + logDebug(lf_bus, "switching from %s to %s", getStateCode(m_state), getStateCode(state)); + } + if (state == bs_noSignal) { + logError(lf_bus, "signal lost"); + } else if (m_state == bs_noSignal) { + logNotice(lf_bus, "signal acquired"); + } + m_state = state; - if (state == bs_ready || state == bs_skip) { - m_command.clear(); - m_commandCrcValid = false; - m_response.clear(false); // unescape while receiving response - m_responseCrcValid = false; - m_nextSendPos = 0; - } + if (state == bs_ready || state == bs_skip) { + m_command.clear(); + m_commandCrcValid = false; + m_response.clear(false); // unescape while receiving response + m_responseCrcValid = false; + m_nextSendPos = 0; + } - return result; + return result; } void BusHandler::addSeenAddress(unsigned char address) { - if (!isValidAddress(address, false)) { - return; - } - if (!isMaster(address)) { - if (!m_device->isReadOnly() && address == m_ownSlaveAddress) { - if (!m_addressConflict) { - m_addressConflict = true; - logError(lf_bus, "own slave address %2.2x is used by another participant", address); - } - } - m_seenAddresses[address] |= SEEN; - address = getMasterAddress(address); - if (address == SYN) { - return; - } - } - if ((m_seenAddresses[address]&SEEN) == 0) { - if (!m_device->isReadOnly() && address == m_ownMasterAddress) { - if (!m_addressConflict) { - m_addressConflict = true; - logError(lf_bus, "own master address %2.2x is used by another participant", address); - } - } else { - m_masterCount++; - if (m_autoLockCount && m_masterCount > m_lockCount) { - m_lockCount = m_masterCount; - } - logNotice(lf_bus, "new master %2.2x, master count %d", address, m_masterCount); - } - m_seenAddresses[address] |= SEEN; - } + if (!isValidAddress(address, false)) { + return; + } + if (!isMaster(address)) { + if (!m_device->isReadOnly() && address == m_ownSlaveAddress) { + if (!m_addressConflict) { + m_addressConflict = true; + logError(lf_bus, "own slave address %2.2x is used by another participant", address); + } + } + m_seenAddresses[address] |= SEEN; + address = getMasterAddress(address); + if (address == SYN) { + return; + } + } + if ((m_seenAddresses[address]&SEEN) == 0) { + if (!m_device->isReadOnly() && address == m_ownMasterAddress) { + if (!m_addressConflict) { + m_addressConflict = true; + logError(lf_bus, "own master address %2.2x is used by another participant", address); + } + } else { + m_masterCount++; + if (m_autoLockCount && m_masterCount > m_lockCount) { + m_lockCount = m_masterCount; + } + logNotice(lf_bus, "new master %2.2x, master count %d", address, m_masterCount); + } + m_seenAddresses[address] |= SEEN; + } } void BusHandler::receiveCompleted() { - unsigned char srcAddress = m_command[0], dstAddress = m_command[1]; - if (srcAddress == dstAddress) { - logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); - return; - } - addSeenAddress(srcAddress); - addSeenAddress(dstAddress); + unsigned char srcAddress = m_command[0], dstAddress = m_command[1]; + if (srcAddress == dstAddress) { + logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress); + return; + } + addSeenAddress(srcAddress); + addSeenAddress(dstAddress); - bool master = isMaster(dstAddress); - if (dstAddress == BROADCAST) { - logInfo(lf_update, "update BC cmd: %s", m_command.getDataStr().c_str()); - if (m_command.size() >= 5+9 && m_command[2] == 0x07 && m_command[3] == 0x04) { - unsigned char slaveAddress = (unsigned char)((srcAddress+5)&0xff); - addSeenAddress(slaveAddress); - Message* message = m_messages->getScanMessage(slaveAddress); - if (message && (message->getLastUpdateTime() == 0 || message->getLastSlaveData().size() < 10)) { - // e.g. 10fe07040a b5564149303001248901 - m_seenAddresses[slaveAddress] |= SCAN_INIT; - SymbolString idData; - istringstream input; - result_t result = message->prepareMaster(m_ownMasterAddress, idData, input); - if (result == RESULT_OK) { - idData.clear(); - idData.push_back(9); - for (size_t i = 5; i <= 5+9; i++) { - idData.push_back(m_command[i]); - } - result = message->storeLastData(pt_slaveData, idData, 0); - } - if (result == RESULT_OK) { - m_seenAddresses[slaveAddress] |= SCAN_DONE; - } - logNotice(lf_update, "store BC ident: %s", getResultCode(result)); - } - } - } else if (master) { - logInfo(lf_update, "update MM cmd: %s", m_command.getDataStr().c_str()); - } else { - logInfo(lf_update, "update MS cmd: %s / %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str()); - } - Message* message = m_messages->find(m_command); - if (m_grabMessages) { - uint64_t key; - if (message) { - key = message->getKey(); - } else { - key = Message::createKey(m_command, 4); // up to 4 DD bytes - } - m_grabbedMessages[key].setLastData(m_command, m_response); - } - if (message == NULL) { - if (dstAddress == BROADCAST) { - logNotice(lf_update, "unknown BC cmd: %s", m_command.getDataStr().c_str()); - } else if (master) { - logNotice(lf_update, "unknown MM cmd: %s", m_command.getDataStr().c_str()); - } else { - logNotice(lf_update, "unknown MS cmd: %s / %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str()); - } - } else { - m_messages->invalidateCache(message); - string circuit = message->getCircuit(); - string name = message->getName(); - result_t result = message->storeLastData(m_command, m_response); - ostringstream output; - if (result == RESULT_OK) { - result = message->decodeLastData(output); - } - if (result < RESULT_OK) { - logError(lf_update, "unable to parse %s %s from %s / %s: %s", circuit.c_str(), name.c_str(), m_command.getDataStr().c_str(), m_response.getDataStr().c_str(), getResultCode(result)); - } else { - string data = output.str(); - if (m_answer && dstAddress == (master ? m_ownMasterAddress : m_ownSlaveAddress)) { - logNotice(lf_update, "self-update %s %s QQ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, data.c_str()); // TODO store in database of internal variables - } else if (message->getDstAddress() == SYN) { // any destination - if (message->getSrcAddress() == SYN) { // any destination and any source - logNotice(lf_update, "update %s %s QQ=%2.2x ZZ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, dstAddress, data.c_str()); - } else { - logNotice(lf_update, "update %s %s ZZ=%2.2x: %s", circuit.c_str(), name.c_str(), dstAddress, data.c_str()); - } - } else if (message->getSrcAddress() == SYN) { // any source - logNotice(lf_update, "update %s %s QQ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, data.c_str()); - } else { - logNotice(lf_update, "update %s %s: %s", circuit.c_str(), name.c_str(), data.c_str()); - } - } - } + bool master = isMaster(dstAddress); + if (dstAddress == BROADCAST) { + logInfo(lf_update, "update BC cmd: %s", m_command.getDataStr().c_str()); + if (m_command.size() >= 5+9 && m_command[2] == 0x07 && m_command[3] == 0x04) { + unsigned char slaveAddress = (unsigned char)((srcAddress+5)&0xff); + addSeenAddress(slaveAddress); + Message* message = m_messages->getScanMessage(slaveAddress); + if (message && (message->getLastUpdateTime() == 0 || message->getLastSlaveData().size() < 10)) { + // e.g. 10fe07040a b5564149303001248901 + m_seenAddresses[slaveAddress] |= SCAN_INIT; + SymbolString idData; + istringstream input; + result_t result = message->prepareMaster(m_ownMasterAddress, idData, input); + if (result == RESULT_OK) { + idData.clear(); + idData.push_back(9); + for (size_t i = 5; i <= 5+9; i++) { + idData.push_back(m_command[i]); + } + result = message->storeLastData(pt_slaveData, idData, 0); + } + if (result == RESULT_OK) { + m_seenAddresses[slaveAddress] |= SCAN_DONE; + } + logNotice(lf_update, "store BC ident: %s", getResultCode(result)); + } + } + } else if (master) { + logInfo(lf_update, "update MM cmd: %s", m_command.getDataStr().c_str()); + } else { + logInfo(lf_update, "update MS cmd: %s / %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str()); + } + Message* message = m_messages->find(m_command); + if (m_grabMessages) { + uint64_t key; + if (message) { + key = message->getKey(); + } else { + key = Message::createKey(m_command, 4); // up to 4 DD bytes + } + m_grabbedMessages[key].setLastData(m_command, m_response); + } + if (message == NULL) { + if (dstAddress == BROADCAST) { + logNotice(lf_update, "unknown BC cmd: %s", m_command.getDataStr().c_str()); + } else if (master) { + logNotice(lf_update, "unknown MM cmd: %s", m_command.getDataStr().c_str()); + } else { + logNotice(lf_update, "unknown MS cmd: %s / %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str()); + } + } else { + m_messages->invalidateCache(message); + string circuit = message->getCircuit(); + string name = message->getName(); + result_t result = message->storeLastData(m_command, m_response); + ostringstream output; + if (result == RESULT_OK) { + result = message->decodeLastData(output); + } + if (result < RESULT_OK) { + logError(lf_update, "unable to parse %s %s from %s / %s: %s", circuit.c_str(), name.c_str(), m_command.getDataStr().c_str(), m_response.getDataStr().c_str(), getResultCode(result)); + } else { + string data = output.str(); + if (m_answer && dstAddress == (master ? m_ownMasterAddress : m_ownSlaveAddress)) { + logNotice(lf_update, "self-update %s %s QQ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, data.c_str()); // TODO store in database of internal variables + } else if (message->getDstAddress() == SYN) { // any destination + if (message->getSrcAddress() == SYN) { // any destination and any source + logNotice(lf_update, "update %s %s QQ=%2.2x ZZ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, dstAddress, data.c_str()); + } else { + logNotice(lf_update, "update %s %s ZZ=%2.2x: %s", circuit.c_str(), name.c_str(), dstAddress, data.c_str()); + } + } else if (message->getSrcAddress() == SYN) { // any source + logNotice(lf_update, "update %s %s QQ=%2.2x: %s", circuit.c_str(), name.c_str(), srcAddress, data.c_str()); + } else { + logNotice(lf_update, "update %s %s: %s", circuit.c_str(), name.c_str(), data.c_str()); + } + } + } } result_t BusHandler::startScan(bool full) { - deque messages = m_messages->findAll("scan", "", true); - for (deque::iterator it = messages.begin(); it < messages.end(); it++) { - Message* message = *it; - if (message->getPrimaryCommand() == 0x07 && message->getSecondaryCommand() == 0x04) { - messages.erase(it--); // query pb 0x07 / sb 0x04 only once - } - } + deque messages = m_messages->findAll("scan", "", true); + for (deque::iterator it = messages.begin(); it < messages.end(); it++) { + Message* message = *it; + if (message->getPrimaryCommand() == 0x07 && message->getSecondaryCommand() == 0x04) { + messages.erase(it--); // query pb 0x07 / sb 0x04 only once + } + } - Message* scanMessage = m_messages->getScanMessage(); - if (scanMessage == NULL) { - return RESULT_ERR_NOTFOUND; - } - m_scanResults.clear(); + Message* scanMessage = m_messages->getScanMessage(); + if (scanMessage == NULL) { + return RESULT_ERR_NOTFOUND; + } + m_scanResults.clear(); - deque slaves; - for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master - if (!isValidAddress(slave, false) || isMaster(slave)) { - continue; - } - if (!full && (m_seenAddresses[slave]&SEEN) == 0) { - unsigned char master = getMasterAddress(slave); // check if we saw the corresponding master already - if (master == SYN || (m_seenAddresses[master]&SEEN) == 0) { - continue; - } - } - slaves.push_back(slave); - } - messages.push_front(scanMessage); - ScanRequest* request = new ScanRequest(m_messages, messages, slaves, this); - result_t result = request->prepare(m_ownMasterAddress); - if (result < RESULT_OK) { - delete request; - return result == RESULT_ERR_EOF ? RESULT_EMPTY : result; - } - m_runningScans++; - m_nextRequests.push(request); - return RESULT_OK; + deque slaves; + for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master + if (!isValidAddress(slave, false) || isMaster(slave)) { + continue; + } + if (!full && (m_seenAddresses[slave]&SEEN) == 0) { + unsigned char master = getMasterAddress(slave); // check if we saw the corresponding master already + if (master == SYN || (m_seenAddresses[master]&SEEN) == 0) { + continue; + } + } + slaves.push_back(slave); + } + messages.push_front(scanMessage); + ScanRequest* request = new ScanRequest(m_messages, messages, slaves, this); + result_t result = request->prepare(m_ownMasterAddress); + if (result < RESULT_OK) { + delete request; + return result == RESULT_ERR_EOF ? RESULT_EMPTY : result; + } + m_runningScans++; + m_nextRequests.push(request); + return RESULT_OK; } void BusHandler::setScanResult(unsigned char dstAddress, string str) { - m_seenAddresses[dstAddress] |= SCAN_INIT; - if (str.length() > 0) { - m_seenAddresses[dstAddress] |= SCAN_DONE; - m_scanResults[dstAddress] = str; - logNotice(lf_bus, "scan %2.2x: %s", dstAddress, str.c_str()); - } + m_seenAddresses[dstAddress] |= SCAN_INIT; + if (str.length() > 0) { + m_seenAddresses[dstAddress] |= SCAN_DONE; + m_scanResults[dstAddress] = str; + logNotice(lf_bus, "scan %2.2x: %s", dstAddress, str.c_str()); + } } void BusHandler::setScanFinished() { - if (m_runningScans > 0) { - m_runningScans--; - } + if (m_runningScans > 0) { + m_runningScans--; + } } void BusHandler::formatScanResult(ostringstream& output) { - if (m_runningScans > 0) { - output << static_cast(m_runningScans) << " scan(s) still running" << endl; - } - bool first = true; - for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master - map::iterator it = m_scanResults.find(slave); - if (it != m_scanResults.end()) { - if (first) { - first = false; - } else { - output << endl; - } - output << hex << setw(2) << setfill('0') << static_cast(slave) << it->second; - } - } - if (first) { - // fallback to autoscan results - for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master - if (isValidAddress(slave, false) && !isMaster(slave) && (m_seenAddresses[slave]&SCAN_DONE) != 0) { - Message* message = m_messages->getScanMessage(slave); - if (message != NULL && message->getLastUpdateTime() > 0) { - if (first) { - first = false; - } else { - output << endl; - } - output << hex << setw(2) << setfill('0') << static_cast(slave); - message->decodeLastData(output, 0, true); - } - } - } - } + if (m_runningScans > 0) { + output << static_cast(m_runningScans) << " scan(s) still running" << endl; + } + bool first = true; + for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master + map::iterator it = m_scanResults.find(slave); + if (it != m_scanResults.end()) { + if (first) { + first = false; + } else { + output << endl; + } + output << hex << setw(2) << setfill('0') << static_cast(slave) << it->second; + } + } + if (first) { + // fallback to autoscan results + for (unsigned char slave = 1; slave != 0; slave++) { // 0 is known to be a master + if (isValidAddress(slave, false) && !isMaster(slave) && (m_seenAddresses[slave]&SCAN_DONE) != 0) { + Message* message = m_messages->getScanMessage(slave); + if (message != NULL && message->getLastUpdateTime() > 0) { + if (first) { + first = false; + } else { + output << endl; + } + output << hex << setw(2) << setfill('0') << static_cast(slave); + message->decodeLastData(output, 0, true); + } + } + } + } } void BusHandler::formatSeenInfo(ostringstream& output) { - unsigned char address = 0; - for (int index = 0; index < 256; index++, address++) { - if (isValidAddress(address, false) - && ((m_seenAddresses[address]&SEEN) != 0 || (!m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress)))) { - output << endl << "address " << setfill('0') << setw(2) << hex << static_cast(address); - unsigned char master; - if (isMaster(address)) { - output << ": master"; - master = address; - } else { - output << ": slave"; - master = getMasterAddress(address); - } - if (master != SYN) { - output << " #" << setw(0) << dec << static_cast(getMasterNumber(master)); - } - if (!m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress)) { - output << ", ebusd"; - if (m_answer) { - output << " (answering)"; - } - if (m_addressConflict && (m_seenAddresses[address]&SEEN) != 0) { - output << ", conflict"; - } - } - if ((m_seenAddresses[address]&SCAN_DONE) != 0) { - output << ", scanned"; - Message* message = m_messages->getScanMessage(address); - if (message != NULL && message->getLastUpdateTime() > 0) { - // add detailed scan info: Manufacturer ID SW HW - output << " \""; - result_t result = message->decodeLastData(output, OF_NAMES); - if (result != RESULT_OK) { - output << "\" error: " << getResultCode(result); - } else { - output << "\""; - } - } - } - string loadedFiles = m_messages->getLoadedFiles(address); - if (!loadedFiles.empty()) { - output << ", loaded " << loadedFiles; - } - } - } + unsigned char address = 0; + for (int index = 0; index < 256; index++, address++) { + if (isValidAddress(address, false) + && ((m_seenAddresses[address]&SEEN) != 0 || (!m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress)))) { + output << endl << "address " << setfill('0') << setw(2) << hex << static_cast(address); + unsigned char master; + if (isMaster(address)) { + output << ": master"; + master = address; + } else { + output << ": slave"; + master = getMasterAddress(address); + } + if (master != SYN) { + output << " #" << setw(0) << dec << static_cast(getMasterNumber(master)); + } + if (!m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress)) { + output << ", ebusd"; + if (m_answer) { + output << " (answering)"; + } + if (m_addressConflict && (m_seenAddresses[address]&SEEN) != 0) { + output << ", conflict"; + } + } + if ((m_seenAddresses[address]&SCAN_DONE) != 0) { + output << ", scanned"; + Message* message = m_messages->getScanMessage(address); + if (message != NULL && message->getLastUpdateTime() > 0) { + // add detailed scan info: Manufacturer ID SW HW + output << " \""; + result_t result = message->decodeLastData(output, OF_NAMES); + if (result != RESULT_OK) { + output << "\" error: " << getResultCode(result); + } else { + output << "\""; + } + } + } + string loadedFiles = m_messages->getLoadedFiles(address); + if (!loadedFiles.empty()) { + output << ", loaded " << loadedFiles; + } + } + } } result_t BusHandler::scanAndWait(unsigned char dstAddress, SymbolString& slave) { - if (!isValidAddress(dstAddress) || isMaster(dstAddress)) { - return RESULT_ERR_INVALID_ADDR; - } - m_seenAddresses[dstAddress] |= SCAN_INIT; - Message* scanMessage = m_messages->getScanMessage(); - if (scanMessage == NULL) { - return RESULT_ERR_NOTFOUND; - } - istringstream input; - SymbolString master; - result_t result = scanMessage->prepareMaster(m_ownMasterAddress, master, input, UI_FIELD_SEPARATOR, dstAddress); - if (result == RESULT_OK) { - result = sendAndWait(master, slave); - if (result == RESULT_OK) { - Message* message = m_messages->getScanMessage(dstAddress); - if (message != NULL && message != scanMessage) { - scanMessage = message; - scanMessage->storeLastData(pt_masterData, master, 0); // update the cache, expected to work since this is a clone - } - } - if (result != RESULT_ERR_NO_SIGNAL) { - m_seenAddresses[dstAddress] |= SCAN_DONE; - } - } - if (result != RESULT_OK || slave.size() == 0) { // avoid "invalid position" during decode - return result; - } - return scanMessage->storeLastData(pt_slaveData, slave, 0); // update the cache + if (!isValidAddress(dstAddress) || isMaster(dstAddress)) { + return RESULT_ERR_INVALID_ADDR; + } + m_seenAddresses[dstAddress] |= SCAN_INIT; + Message* scanMessage = m_messages->getScanMessage(); + if (scanMessage == NULL) { + return RESULT_ERR_NOTFOUND; + } + istringstream input; + SymbolString master; + result_t result = scanMessage->prepareMaster(m_ownMasterAddress, master, input, UI_FIELD_SEPARATOR, dstAddress); + if (result == RESULT_OK) { + result = sendAndWait(master, slave); + if (result == RESULT_OK) { + Message* message = m_messages->getScanMessage(dstAddress); + if (message != NULL && message != scanMessage) { + scanMessage = message; + scanMessage->storeLastData(pt_masterData, master, 0); // update the cache, expected to work since this is a clone + } + } + if (result != RESULT_ERR_NO_SIGNAL) { + m_seenAddresses[dstAddress] |= SCAN_DONE; + } + } + if (result != RESULT_OK || slave.size() == 0) { // avoid "invalid position" during decode + return result; + } + return scanMessage->storeLastData(pt_slaveData, slave, 0); // update the cache } bool BusHandler::enableGrab(bool enable) { - if (enable == m_grabMessages) { - return false; - } - if (!enable) { - m_grabbedMessages.clear(); - } - m_grabMessages = enable; - return true; + if (enable == m_grabMessages) { + return false; + } + if (!enable) { + m_grabbedMessages.clear(); + } + m_grabMessages = enable; + return true; } void BusHandler::formatGrabResult(const bool unknown, ostringstream& output) { - if (!m_grabMessages) { - output << "grab disabled"; - } else { - bool first = true; - for (map::iterator it = m_grabbedMessages.begin(); it != m_grabbedMessages.end(); it++) { - if (it->second.dump(unknown, m_messages, first, output)) { - first = false; - } - } - } + if (!m_grabMessages) { + output << "grab disabled"; + } else { + bool first = true; + for (map::iterator it = m_grabbedMessages.begin(); it != m_grabbedMessages.end(); it++) { + if (it->second.dump(unknown, m_messages, first, output)) { + first = false; + } + } + } } unsigned char BusHandler::getNextScanAddress(unsigned char lastAddress, bool& scanned) { - if (lastAddress == SYN) { - return SYN; - } - while (++lastAddress != 0) { // 0 is known to be a master - if (!isValidAddress(lastAddress, false) || isMaster(lastAddress)) { - continue; - } - if ((m_seenAddresses[lastAddress]&(SEEN|LOAD_INIT)) == SEEN) { - scanned = (m_seenAddresses[lastAddress]&SCAN_INIT) != 0; - return lastAddress; - } - unsigned char master = getMasterAddress(lastAddress); - if (master != SYN && (m_seenAddresses[master]&SEEN) != 0 && (m_seenAddresses[lastAddress]&LOAD_INIT) == 0) { - scanned = (m_seenAddresses[lastAddress]&SCAN_INIT) != 0; - return lastAddress; - } - } - return SYN; + if (lastAddress == SYN) { + return SYN; + } + while (++lastAddress != 0) { // 0 is known to be a master + if (!isValidAddress(lastAddress, false) || isMaster(lastAddress)) { + continue; + } + if ((m_seenAddresses[lastAddress]&(SEEN|LOAD_INIT)) == SEEN) { + scanned = (m_seenAddresses[lastAddress]&SCAN_INIT) != 0; + return lastAddress; + } + unsigned char master = getMasterAddress(lastAddress); + if (master != SYN && (m_seenAddresses[master]&SEEN) != 0 && (m_seenAddresses[lastAddress]&LOAD_INIT) == 0) { + scanned = (m_seenAddresses[lastAddress]&SCAN_INIT) != 0; + return lastAddress; + } + } + return SYN; } void BusHandler::setScanConfigLoaded(unsigned char address, string file) { - m_seenAddresses[address] |= LOAD_INIT; - if (!file.empty()) { - m_seenAddresses[address] |= LOAD_DONE; - m_messages->addLoadedFile(address, file, ""); - } + m_seenAddresses[address] |= LOAD_INIT; + if (!file.empty()) { + m_seenAddresses[address] |= LOAD_DONE; + m_messages->addLoadedFile(address, file, ""); + } } } // namespace ebusd diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h index 6e333737..10bbe052 100644 --- a/src/ebusd/bushandler.h +++ b/src/ebusd/bushandler.h @@ -62,18 +62,18 @@ using std::string; /** the possible bus states. */ enum BusState { - bs_noSignal, //!< no signal on the bus - bs_skip, //!< skip all symbols until next @a SYN - bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ) - bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set] - bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get] - bs_recvRes, //!< receive response (slave data) [passive set + active get] - bs_recvResAck, //!< receive response ACK/NACK [passive set] - bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get] - bs_sendResAck, //!< send response ACK/NACK [active get] - bs_sendCmdAck, //!< send command ACK/NACK [passive get] - bs_sendRes, //!< send response (slave data) [passive get] - bs_sendSyn, //!< send SYN for completed transfer [active set+get] + bs_noSignal, //!< no signal on the bus + bs_skip, //!< skip all symbols until next @a SYN + bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ) + bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set] + bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get] + bs_recvRes, //!< receive response (slave data) [passive set + active get] + bs_recvResAck, //!< receive response ACK/NACK [passive set] + bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get] + bs_sendResAck, //!< send response ACK/NACK [active get] + bs_sendCmdAck, //!< send command ACK/NACK [passive get] + bs_sendRes, //!< send response (slave data) [passive get] + bs_sendSyn, //!< send SYN for completed transfer [active set+get] }; /** bit for the seen state: seen. */ @@ -97,41 +97,41 @@ class BusHandler; * Generic request for sending to and receiving from the bus. */ class BusRequest { - friend class BusHandler; + friend class BusHandler; - public: - /** - * Constructor. - * @param master the escaped master data @a SymbolString to send. - * @param deleteOnFinish whether to automatically delete this @a BusRequest when finished. - */ - BusRequest(SymbolString& master, const bool deleteOnFinish) - : m_master(master), m_busLostRetries(0), - m_deleteOnFinish(deleteOnFinish) {} + public: + /** + * Constructor. + * @param master the escaped master data @a SymbolString to send. + * @param deleteOnFinish whether to automatically delete this @a BusRequest when finished. + */ + BusRequest(SymbolString& master, const bool deleteOnFinish) + : m_master(master), m_busLostRetries(0), + m_deleteOnFinish(deleteOnFinish) {} - /** - * Destructor. - */ - virtual ~BusRequest() {} + /** + * Destructor. + */ + virtual ~BusRequest() {} - /** - * Notify the request of the specified result. - * @param result the result of the request. - * @param slave the slave data @a SymbolString received. - * @return true if the request needs to be restarted. - */ - virtual bool notify(result_t result, SymbolString& slave) = 0; + /** + * Notify the request of the specified result. + * @param result the result of the request. + * @param slave the slave data @a SymbolString received. + * @return true if the request needs to be restarted. + */ + virtual bool notify(result_t result, SymbolString& slave) = 0; - protected: - /** the escaped master data @a SymbolString to send. */ - SymbolString& m_master; + protected: + /** the escaped master data @a SymbolString to send. */ + SymbolString& m_master; - /** the number of times a send is repeated due to lost arbitration. */ - unsigned int m_busLostRetries; + /** the number of times a send is repeated due to lost arbitration. */ + unsigned int m_busLostRetries; - /** whether to automatically delete this @a BusRequest when finished. */ - const bool m_deleteOnFinish; + /** whether to automatically delete this @a BusRequest when finished. */ + const bool m_deleteOnFinish; }; @@ -139,41 +139,41 @@ class BusRequest { * A poll @a BusRequest handled by @a BusHandler itself. */ class PollRequest : public BusRequest { - friend class BusHandler; + friend class BusHandler; - public: - /** - * Constructor. - * @param message the associated @a Message. - */ - explicit PollRequest(Message* message) - : BusRequest(m_master, true), m_message(message), m_index(0) {} + public: + /** + * Constructor. + * @param message the associated @a Message. + */ + explicit PollRequest(Message* message) + : BusRequest(m_master, true), m_message(message), m_index(0) {} - /** - * Destructor. - */ - virtual ~PollRequest() {} + /** + * Destructor. + */ + virtual ~PollRequest() {} - /** - * Prepare the master data. - * @param masterAddress the master bus address to use. - * @return the result code. - */ - result_t prepare(unsigned char masterAddress); + /** + * Prepare the master data. + * @param masterAddress the master bus address to use. + * @return the result code. + */ + result_t prepare(unsigned char masterAddress); - // @copydoc - virtual bool notify(result_t result, SymbolString& slave); + // @copydoc + virtual bool notify(result_t result, SymbolString& slave); - private: - /** the escaped master data @a SymbolString. */ - SymbolString m_master; + private: + /** the escaped master data @a SymbolString. */ + SymbolString m_master; - /** the associated @a Message. */ - Message* m_message; + /** the associated @a Message. */ + Message* m_message; - /** the current part index in @a m_message. */ - unsigned char m_index; + /** the current part index in @a m_message. */ + unsigned char m_index; }; @@ -181,65 +181,65 @@ class PollRequest : public BusRequest { * A scan @a BusRequest handled by @a BusHandler itself. */ class ScanRequest : public BusRequest { - friend class BusHandler; + friend class BusHandler; - public: - /** - * Constructor. - * @param messageMap the @a MessageMap instance. - * @param messages the @a Message instances to query starting with the primary one. - * @param slaves the slave addresses to scan. - * @param busHandler the @a BusHandler instance to notify of final scan result. - */ - ScanRequest(MessageMap* messageMap, deque messages, deque slaves, BusHandler* busHandler) - : BusRequest(m_master, true), m_messageMap(messageMap), m_index(0), m_allMessages(messages), m_messages(messages), m_slaves(slaves), m_busHandler(busHandler) { - m_message = m_messages.front(); - m_messages.pop_front(); - } + public: + /** + * Constructor. + * @param messageMap the @a MessageMap instance. + * @param messages the @a Message instances to query starting with the primary one. + * @param slaves the slave addresses to scan. + * @param busHandler the @a BusHandler instance to notify of final scan result. + */ + ScanRequest(MessageMap* messageMap, deque messages, deque slaves, BusHandler* busHandler) + : BusRequest(m_master, true), m_messageMap(messageMap), m_index(0), m_allMessages(messages), m_messages(messages), m_slaves(slaves), m_busHandler(busHandler) { + m_message = m_messages.front(); + m_messages.pop_front(); + } - /** - * Destructor. - */ - virtual ~ScanRequest() {} + /** + * Destructor. + */ + virtual ~ScanRequest() {} - /** - * Prepare the next master data. - * @param masterAddress the master bus address to use. - * @return the result code. - */ - result_t prepare(unsigned char masterAddress); + /** + * Prepare the next master data. + * @param masterAddress the master bus address to use. + * @return the result code. + */ + result_t prepare(unsigned char masterAddress); - // @copydoc - virtual bool notify(result_t result, SymbolString& slave); + // @copydoc + virtual bool notify(result_t result, SymbolString& slave); - private: - /** the @a MessageMap instance. */ - MessageMap* m_messageMap; + private: + /** the @a MessageMap instance. */ + MessageMap* m_messageMap; - /** the escaped master data @a SymbolString. */ - SymbolString m_master; + /** the escaped master data @a SymbolString. */ + SymbolString m_master; - /** the currently queried @a Message. */ - Message* m_message; + /** the currently queried @a Message. */ + Message* m_message; - /** the current part index in @a m_message. */ - unsigned char m_index; + /** the current part index in @a m_message. */ + unsigned char m_index; - /** all secondary @a Message instances. */ - const deque m_allMessages; + /** all secondary @a Message instances. */ + const deque m_allMessages; - /** the remaining secondary @a Message instances. */ - deque m_messages; + /** the remaining secondary @a Message instances. */ + deque m_messages; - /** the slave addresses to scan. */ - deque m_slaves; + /** the slave addresses to scan. */ + deque m_slaves; - /** the @a ostringstream for building the scan result of a single slave. */ - ostringstream m_scanResult; + /** the @a ostringstream for building the scan result of a single slave. */ + ostringstream m_scanResult; - /** the @a BusHandler instance to notify of final scan result. */ - BusHandler* m_busHandler; + /** the @a BusHandler instance to notify of final scan result. */ + BusHandler* m_busHandler; }; @@ -247,32 +247,32 @@ class ScanRequest : public BusRequest { * An active @a BusRequest that can be waited for. */ class ActiveBusRequest : public BusRequest { - friend class BusHandler; + friend class BusHandler; - public: - /** - * Constructor. - * @param master the escaped master data @a SymbolString to send. - * @param slave reference to @a SymbolString for filling in the received slave data. - */ - ActiveBusRequest(SymbolString& master, SymbolString& slave) - : BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {} + public: + /** + * Constructor. + * @param master the escaped master data @a SymbolString to send. + * @param slave reference to @a SymbolString for filling in the received slave data. + */ + ActiveBusRequest(SymbolString& master, SymbolString& slave) + : BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {} - /** - * Destructor. - */ - virtual ~ActiveBusRequest() {} + /** + * Destructor. + */ + virtual ~ActiveBusRequest() {} - // @copydoc - virtual bool notify(result_t result, SymbolString& slave); + // @copydoc + virtual bool notify(result_t result, SymbolString& slave); - private: - /** the result of handling the request. */ - result_t m_result; + private: + /** the result of handling the request. */ + result_t m_result; - /** reference to @a SymbolString for filling in the received slave data. */ - SymbolString& m_slave; + /** reference to @a SymbolString for filling in the received slave data. */ + SymbolString& m_slave; }; @@ -280,48 +280,48 @@ class ActiveBusRequest : public BusRequest { * Helper class for keeping track of grabbed messages. */ class GrabbedMessage { - public: - /** - * Construct a new instance. - */ - GrabbedMessage() : m_count(0) {} + public: + /** + * Construct a new instance. + */ + GrabbedMessage() : m_count(0) {} - /** - * Copy constructor. - * @param other the @a GrabbedMessage to copy from. - */ - GrabbedMessage(const GrabbedMessage& other) : m_count(other.m_count) { - m_lastMaster.addAll(other.m_lastMaster); - m_lastSlave.addAll(other.m_lastSlave); - } + /** + * Copy constructor. + * @param other the @a GrabbedMessage to copy from. + */ + GrabbedMessage(const GrabbedMessage& other) : m_count(other.m_count) { + m_lastMaster.addAll(other.m_lastMaster); + m_lastSlave.addAll(other.m_lastSlave); + } - /** - * Set the last received data. - * @param master the last master @a SymbolString. - * @param slave the last slave @a SymbolString. - */ - void setLastData(SymbolString& master, SymbolString& slave); + /** + * Set the last received data. + * @param master the last master @a SymbolString. + * @param slave the last slave @a SymbolString. + */ + void setLastData(SymbolString& master, SymbolString& slave); - /** - * Dump the last received data and message count to the output. - * @param unknown whether to dump only if this message is unknown. - * @param messages the @a MessageMap instance for resolving known @a Message instances. - * @param first whether this is the first message to be added to the output. - * @param output the @a ostringstream to format the messages to. - * @return whether the message was added to the output. - */ - bool dump(const bool unknown, MessageMap* messages, bool first, ostringstream& output); + /** + * Dump the last received data and message count to the output. + * @param unknown whether to dump only if this message is unknown. + * @param messages the @a MessageMap instance for resolving known @a Message instances. + * @param first whether this is the first message to be added to the output. + * @param output the @a ostringstream to format the messages to. + * @return whether the message was added to the output. + */ + bool dump(const bool unknown, MessageMap* messages, bool first, ostringstream& output); - private: - /** the last master @a SymbolString. */ - SymbolString m_lastMaster; + private: + /** the last master @a SymbolString. */ + SymbolString m_lastMaster; - /** the last slave @a SymbolString. */ - SymbolString m_lastSlave; + /** the last slave @a SymbolString. */ + SymbolString m_lastSlave; - /** the number of times this message was seen. */ - unsigned int m_count; + /** the number of times this message was seen. */ + unsigned int m_count; }; @@ -329,328 +329,328 @@ class GrabbedMessage { * Handles input from and output to the bus with respect to the eBUS protocol. */ class BusHandler : public WaitThread { - public: - /** - * Construct a new instance. - * @param device the @a Device instance for accessing the bus. - * @param messages the @a MessageMap instance with all known @a Message instances. - * @param ownAddress the own master address. - * @param answer whether to answer queries for the own master/slave address. - * @param busLostRetries the number of times a send is repeated due to lost arbitration. - * @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration). - * @param transferLatency the bus transfer latency in microseconds. - * @param busAcquireTimeout the maximum time in microseconds for bus acquisition. - * @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge. - * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. - * @param generateSyn whether to enable AUTO-SYN symbol generation. - * @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled. - */ - BusHandler(Device* device, MessageMap* messages, - const unsigned char ownAddress, const bool answer, - const unsigned int busLostRetries, const unsigned int failedSendRetries, - const unsigned int transferLatency, const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout, - const unsigned int lockCount, const bool generateSyn, - const unsigned int pollInterval) - : WaitThread(), m_device(device), m_reconnect(false), m_messages(messages), - m_ownMasterAddress(ownAddress), m_ownSlaveAddress((unsigned char)(ownAddress+5)), - m_answer(answer), m_addressConflict(false), - m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), - m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), - m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0), m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount), - m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0), - m_pollInterval(pollInterval), m_lastReceive(0), m_lastPoll(0), - m_currentRequest(NULL), m_runningScans(0), m_nextSendPos(0), - m_symPerSec(0), m_maxSymPerSec(0), - m_state(bs_noSignal), m_repeat(false), - m_command(false), m_commandCrcValid(false), m_response(false), m_responseCrcValid(false), - m_grabMessages(true) { - memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); - } + public: + /** + * Construct a new instance. + * @param device the @a Device instance for accessing the bus. + * @param messages the @a MessageMap instance with all known @a Message instances. + * @param ownAddress the own master address. + * @param answer whether to answer queries for the own master/slave address. + * @param busLostRetries the number of times a send is repeated due to lost arbitration. + * @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration). + * @param transferLatency the bus transfer latency in microseconds. + * @param busAcquireTimeout the maximum time in microseconds for bus acquisition. + * @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge. + * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. + * @param generateSyn whether to enable AUTO-SYN symbol generation. + * @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled. + */ + BusHandler(Device* device, MessageMap* messages, + const unsigned char ownAddress, const bool answer, + const unsigned int busLostRetries, const unsigned int failedSendRetries, + const unsigned int transferLatency, const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout, + const unsigned int lockCount, const bool generateSyn, + const unsigned int pollInterval) + : WaitThread(), m_device(device), m_reconnect(false), m_messages(messages), + m_ownMasterAddress(ownAddress), m_ownSlaveAddress((unsigned char)(ownAddress+5)), + m_answer(answer), m_addressConflict(false), + m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), + m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), + m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0), m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount), + m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0), + m_pollInterval(pollInterval), m_lastReceive(0), m_lastPoll(0), + m_currentRequest(NULL), m_runningScans(0), m_nextSendPos(0), + m_symPerSec(0), m_maxSymPerSec(0), + m_state(bs_noSignal), m_repeat(false), + m_command(false), m_commandCrcValid(false), m_response(false), m_responseCrcValid(false), + m_grabMessages(true) { + memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); + } - /** - * Destructor. - */ - virtual ~BusHandler() { - stop(); - join(); - BusRequest* req; - while ((req = m_finishedRequests.pop()) != NULL) { - delete req; - } - while ((req = m_nextRequests.pop()) != NULL) { - if (req->m_deleteOnFinish) { - delete req; - } - } - if (m_currentRequest != NULL) { - delete m_currentRequest; - m_currentRequest = NULL; - } - } + /** + * Destructor. + */ + virtual ~BusHandler() { + stop(); + join(); + BusRequest* req; + while ((req = m_finishedRequests.pop()) != NULL) { + delete req; + } + while ((req = m_nextRequests.pop()) != NULL) { + if (req->m_deleteOnFinish) { + delete req; + } + } + if (m_currentRequest != NULL) { + delete m_currentRequest; + m_currentRequest = NULL; + } + } - /** - * Clear stored values (e.g. scan results). - */ - void clear(); + /** + * Clear stored values (e.g. scan results). + */ + void clear(); - /** - * Send a message on the bus and wait for the answer. - * @param master the escaped @a SymbolString with the master data to send. - * @param slave the @a SymbolString that will be filled with retrieved slave data. - * @return the result code. - */ - result_t sendAndWait(SymbolString& master, SymbolString& slave); + /** + * Send a message on the bus and wait for the answer. + * @param master the escaped @a SymbolString with the master data to send. + * @param slave the @a SymbolString that will be filled with retrieved slave data. + * @return the result code. + */ + result_t sendAndWait(SymbolString& master, SymbolString& slave); - /** - * Prepare the master part for the @a Message, send it to the bus and wait for the answer. - * @param message the @a Message instance. - * @param inputStr the input @a string from which to read master values (if any). - * @param dstAddress the destination address to set, or @a SYN to keep the address defined during construction. - * @return the result code. - */ - result_t readFromBus(Message* message, string inputStr, const unsigned char dstAddress = SYN); + /** + * Prepare the master part for the @a Message, send it to the bus and wait for the answer. + * @param message the @a Message instance. + * @param inputStr the input @a string from which to read master values (if any). + * @param dstAddress the destination address to set, or @a SYN to keep the address defined during construction. + * @return the result code. + */ + result_t readFromBus(Message* message, string inputStr, const unsigned char dstAddress = SYN); - /** - * Main thread entry. - */ - virtual void run(); + /** + * Main thread entry. + */ + virtual void run(); - /** - * Initiate a scan of the slave addresses. - * @param full true for a full scan (all slaves), false for scanning only already seen slaves. - * @return the result code. - */ - result_t startScan(bool full = false); + /** + * Initiate a scan of the slave addresses. + * @param full true for a full scan (all slaves), false for scanning only already seen slaves. + * @return the result code. + */ + result_t startScan(bool full = false); - /** - * Set the scan result @a string for a scanned slave address. - * @param dstAddress the scanned slave address. - * @param str the scan result @a string to set, or empty if not a single part of the scan was successful. - */ - void setScanResult(unsigned char dstAddress, string str); + /** + * Set the scan result @a string for a scanned slave address. + * @param dstAddress the scanned slave address. + * @param str the scan result @a string to set, or empty if not a single part of the scan was successful. + */ + void setScanResult(unsigned char dstAddress, string str); - /** - * Called from @a ScanRequest upon completion. - */ - void setScanFinished(); + /** + * Called from @a ScanRequest upon completion. + */ + void setScanFinished(); - /** - * Format the scan result to the @a ostringstream. - * @param output the @a ostringstream to format the scan result to. - */ - void formatScanResult(ostringstream& output); + /** + * Format the scan result to the @a ostringstream. + * @param output the @a ostringstream to format the scan result to. + */ + void formatScanResult(ostringstream& output); - /** - * Format information about seen participants to the @a ostringstream. - * @param output the @a ostringstream to append the info to. - */ - void formatSeenInfo(ostringstream& output); + /** + * Format information about seen participants to the @a ostringstream. + * @param output the @a ostringstream to append the info to. + */ + void formatSeenInfo(ostringstream& output); - /** - * Send a scan message on the bus and wait for the answer. - * @param dstAddress the destination slave address to send to. - * @param slave the @a SymbolString that will be filled with retrieved slave data. - * @return the result code. - */ - result_t scanAndWait(unsigned char dstAddress, SymbolString& slave); + /** + * Send a scan message on the bus and wait for the answer. + * @param dstAddress the destination slave address to send to. + * @param slave the @a SymbolString that will be filled with retrieved slave data. + * @return the result code. + */ + result_t scanAndWait(unsigned char dstAddress, SymbolString& slave); - /** - * Start or stop grabbing unknown messages. - * @param enable true to enable grabbing, false to disable it. - * @return true when the grabbing was changed. - */ - bool enableGrab(bool enable = true); + /** + * Start or stop grabbing unknown messages. + * @param enable true to enable grabbing, false to disable it. + * @return true when the grabbing was changed. + */ + bool enableGrab(bool enable = true); - /** - * Format the grabbed messages to the @a ostringstream. - * @param unknown whether to dump only unknown messages. - * @param output the @a ostringstream to format the messages to. - */ - void formatGrabResult(const bool unknown, ostringstream& output); + /** + * Format the grabbed messages to the @a ostringstream. + * @param unknown whether to dump only unknown messages. + * @param output the @a ostringstream to format the messages to. + */ + void formatGrabResult(const bool unknown, ostringstream& output); - /** - * Return true when a signal on the bus is available. - * @return true when a signal on the bus is available. - */ - bool hasSignal() { return m_state != bs_noSignal; } + /** + * Return true when a signal on the bus is available. + * @return true when a signal on the bus is available. + */ + bool hasSignal() { return m_state != bs_noSignal; } - /** - * Reconnect the device. - */ - void reconnect() { m_reconnect = true; } + /** + * Reconnect the device. + */ + void reconnect() { m_reconnect = true; } - /** - * Return the current symbol rate. - * @return the number of received symbols in the last second. - */ - unsigned int getSymbolRate() { return m_symPerSec; } + /** + * Return the current symbol rate. + * @return the number of received symbols in the last second. + */ + unsigned int getSymbolRate() { return m_symPerSec; } - /** - * Return the maximum seen symbol rate. - * @return the maximum number of received symbols per second ever seen. - */ - unsigned int getMaxSymbolRate() { return m_maxSymPerSec; } + /** + * Return the maximum seen symbol rate. + * @return the maximum number of received symbols per second ever seen. + */ + unsigned int getMaxSymbolRate() { return m_maxSymPerSec; } - /** - * Return the number of masters already seen. - * @return the number of masters already seen (including ebusd itself). - */ - unsigned int getMasterCount() { return m_masterCount; } + /** + * Return the number of masters already seen. + * @return the number of masters already seen (including ebusd itself). + */ + unsigned int getMasterCount() { return m_masterCount; } - /** - * Get the next slave address that still needs to be scanned or loaded. - * @param lastAddress the last returned slave address, or 0 for returning the first one. - * @param scanned set to true when the slave is already scanned but not yet loaded, set to false when it still needs to be scanned and loaded. - * @return the next slave address that still needs to be scanned or loaded, or @a SYN. - */ - unsigned char getNextScanAddress(unsigned char lastAddress, bool& scanned); + /** + * Get the next slave address that still needs to be scanned or loaded. + * @param lastAddress the last returned slave address, or 0 for returning the first one. + * @param scanned set to true when the slave is already scanned but not yet loaded, set to false when it still needs to be scanned and loaded. + * @return the next slave address that still needs to be scanned or loaded, or @a SYN. + */ + unsigned char getNextScanAddress(unsigned char lastAddress, bool& scanned); - /** - * Set the state of the participant to configuration @a LOADED. - * @param address the slave address. - * @param file the file from which the configuration was loaded, or empty if loading was not possible. - */ - void setScanConfigLoaded(unsigned char address, string file); + /** + * Set the state of the participant to configuration @a LOADED. + * @param address the slave address. + * @param file the file from which the configuration was loaded, or empty if loading was not possible. + */ + void setScanConfigLoaded(unsigned char address, string file); - private: - /** - * Handle the next symbol on the bus. - * @return RESULT_OK on success, or an error code. - */ - result_t handleSymbol(); + private: + /** + * Handle the next symbol on the bus. + * @return RESULT_OK on success, or an error code. + */ + result_t handleSymbol(); - /** - * Set a new @a BusState and add a log message if necessary. - * @param state the new @a BusState. - * @param result the result code. - * @param firstRepetition true if the first repetition of a message part is being started. - * @return the result code. - */ - result_t setState(BusState state, result_t result, bool firstRepetition = false); + /** + * Set a new @a BusState and add a log message if necessary. + * @param state the new @a BusState. + * @param result the result code. + * @param firstRepetition true if the first repetition of a message part is being started. + * @return the result code. + */ + result_t setState(BusState state, result_t result, bool firstRepetition = false); - /** - * Add a seen bus address. - * @param address the seen bus address. - */ - void addSeenAddress(unsigned char address); + /** + * Add a seen bus address. + * @param address the seen bus address. + */ + void addSeenAddress(unsigned char address); - /** - * Called when a passive reception was successfully completed. - */ - void receiveCompleted(); + /** + * Called when a passive reception was successfully completed. + */ + void receiveCompleted(); - /** the @a Device instance for accessing the bus. */ - Device* m_device; + /** the @a Device instance for accessing the bus. */ + Device* m_device; - /** set to @p true when the device shall be reconnected. */ - bool m_reconnect; + /** set to @p true when the device shall be reconnected. */ + bool m_reconnect; - /** the @a MessageMap instance with all known @a Message instances. */ - MessageMap* m_messages; + /** the @a MessageMap instance with all known @a Message instances. */ + MessageMap* m_messages; - /** the own master address. */ - const unsigned char m_ownMasterAddress; + /** the own master address. */ + const unsigned char m_ownMasterAddress; - /** the own slave address. */ - const unsigned char m_ownSlaveAddress; + /** the own slave address. */ + const unsigned char m_ownSlaveAddress; - /** whether to answer queries for the own master/slave address. */ - const bool m_answer; + /** whether to answer queries for the own master/slave address. */ + const bool m_answer; - /** set to @p true once an address conflict with the own addresses was detected. */ - bool m_addressConflict; + /** set to @p true once an address conflict with the own addresses was detected. */ + bool m_addressConflict; - /** the number of times a send is repeated due to lost arbitration. */ - const unsigned int m_busLostRetries; + /** the number of times a send is repeated due to lost arbitration. */ + const unsigned int m_busLostRetries; - /** the number of times a failed send is repeated (other than lost arbitration). */ - const unsigned int m_failedSendRetries; + /** the number of times a failed send is repeated (other than lost arbitration). */ + const unsigned int m_failedSendRetries; - /** the bus transfer latency in microseconds. */ - const unsigned int m_transferLatency; + /** the bus transfer latency in microseconds. */ + const unsigned int m_transferLatency; - /** the maximum time in microseconds for bus acquisition. */ - const unsigned int m_busAcquireTimeout; + /** the maximum time in microseconds for bus acquisition. */ + const unsigned int m_busAcquireTimeout; - /** the maximum time in microseconds an addressed slave is expected to acknowledge. */ - const unsigned int m_slaveRecvTimeout; + /** the maximum time in microseconds an addressed slave is expected to acknowledge. */ + const unsigned int m_slaveRecvTimeout; - /** the number of masters already seen. */ - unsigned int m_masterCount; + /** the number of masters already seen. */ + unsigned int m_masterCount; - /** whether m_lockCount shall be detected automatically. */ - const bool m_autoLockCount; + /** whether m_lockCount shall be detected automatically. */ + const bool m_autoLockCount; - /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ - unsigned int m_lockCount; + /** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ + unsigned int m_lockCount; - /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ - unsigned int m_remainLockCount; + /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ + unsigned int m_remainLockCount; - /** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ - unsigned int m_generateSynInterval; + /** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ + unsigned int m_generateSynInterval; - /** the interval in seconds in which poll messages are cycled, or 0 if disabled. */ - const unsigned int m_pollInterval; + /** the interval in seconds in which poll messages are cycled, or 0 if disabled. */ + const unsigned int m_pollInterval; - /** the time of the last received symbol, or 0 for never. */ - time_t m_lastReceive; + /** the time of the last received symbol, or 0 for never. */ + time_t m_lastReceive; - /** the time of the last poll, or 0 for never. */ - time_t m_lastPoll; + /** the time of the last poll, or 0 for never. */ + time_t m_lastPoll; - /** the queue of @a BusRequests that shall be handled. */ - Queue m_nextRequests; + /** the queue of @a BusRequests that shall be handled. */ + Queue m_nextRequests; - /** the currently handled BusRequest, or NULL. */ - BusRequest* m_currentRequest; + /** the currently handled BusRequest, or NULL. */ + BusRequest* m_currentRequest; - /** the queue of @a BusRequests that are already finished. */ - Queue m_finishedRequests; + /** the queue of @a BusRequests that are already finished. */ + Queue m_finishedRequests; - /** the number of scan request currently running. */ - unsigned int m_runningScans; + /** the number of scan request currently running. */ + unsigned int m_runningScans; - /** the offset of the next symbol that needs to be sent from the command or response, - * (only relevant if m_request is set and state is @a bs_command or @a bs_response). */ - unsigned char m_nextSendPos; + /** the offset of the next symbol that needs to be sent from the command or response, + * (only relevant if m_request is set and state is @a bs_command or @a bs_response). */ + unsigned char m_nextSendPos; - /** the number of received symbols in the last second. */ - unsigned int m_symPerSec; + /** the number of received symbols in the last second. */ + unsigned int m_symPerSec; - /** the maximum number of received symbols per second ever seen. */ - unsigned int m_maxSymPerSec; + /** the maximum number of received symbols per second ever seen. */ + unsigned int m_maxSymPerSec; - /** the current @a BusState. */ - BusState m_state; + /** the current @a BusState. */ + BusState m_state; - /** whether the current message part is being repeated. */ - bool m_repeat; + /** whether the current message part is being repeated. */ + bool m_repeat; - /** the unescaped received command. */ - SymbolString m_command; + /** the unescaped received command. */ + SymbolString m_command; - /** whether the command CRC is valid. */ - bool m_commandCrcValid; + /** whether the command CRC is valid. */ + bool m_commandCrcValid; - /** the unescaped received response or escaped response to send. */ - SymbolString m_response; + /** the unescaped received response or escaped response to send. */ + SymbolString m_response; - /** whether the response CRC is valid. */ - bool m_responseCrcValid; + /** whether the response CRC is valid. */ + bool m_responseCrcValid; - /** the participating bus addresses seen so far (0 if not seen yet, or combination of @a SEEN bits). */ - unsigned char m_seenAddresses[256]; + /** the participating bus addresses seen so far (0 if not seen yet, or combination of @a SEEN bits). */ + unsigned char m_seenAddresses[256]; - /** the scan results by slave address. */ - map m_scanResults; + /** the scan results by slave address. */ + map m_scanResults; - /** whether to grab messages. */ - bool m_grabMessages; + /** whether to grab messages. */ + bool m_grabMessages; - /** the grabbed messages by key.*/ - map m_grabbedMessages; + /** the grabbed messages by key.*/ + map m_grabbedMessages; }; } // namespace ebusd diff --git a/src/ebusd/datahandler.cpp b/src/ebusd/datahandler.cpp index f3020182..b186d7f8 100644 --- a/src/ebusd/datahandler.cpp +++ b/src/ebusd/datahandler.cpp @@ -17,14 +17,14 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "datahandler.h" #include #include #ifdef HAVE_MQTT -# include "mqtthandler.h" +# include "mqtthandler.h" #endif namespace ebusd { @@ -37,38 +37,38 @@ static const struct argp_child g_last_argp_child = {NULL, 0, NULL, 0}; /** the list of @a argp_child structures. */ static struct argp_child g_argp_children[ #ifdef HAVE_MQTT - 1 + 1 #endif - +1 + +1 ]; const struct argp_child* datahandler_getargs() { - size_t count = 0; + size_t count = 0; #ifdef HAVE_MQTT - g_argp_children[count++] = *mqtthandler_getargs(); + g_argp_children[count++] = *mqtthandler_getargs(); #endif - if (count > 0) { - g_argp_children[count] = g_last_argp_child; - return g_argp_children; - } - return NULL; + if (count > 0) { + g_argp_children[count] = g_last_argp_child; + return g_argp_children; + } + return NULL; } bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list& handlers) { - bool success = true; + bool success = true; #ifdef HAVE_MQTT - DataHandler* handler = mqtthandler_register(busHandler, messages); - if (handler) { - handlers.push_back(handler); - } else { - success = false; - } + DataHandler* handler = mqtthandler_register(busHandler, messages); + if (handler) { + handlers.push_back(handler); + } else { + success = false; + } #endif - return success; + return success; } void DataSink::notifyUpdate(Message* message) { - m_updatedMessages[message]++; + m_updatedMessages[message]++; } } // namespace ebusd diff --git a/src/ebusd/datahandler.h b/src/ebusd/datahandler.h index 3a51abab..13440cc4 100644 --- a/src/ebusd/datahandler.h +++ b/src/ebusd/datahandler.h @@ -58,33 +58,33 @@ bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list m_updatedMessages; + protected: + /** a map of updated @p Message instances. */ + map m_updatedMessages; }; @@ -123,26 +123,26 @@ class DataSink : virtual public DataHandler { * Base class providing data to be sent on the bus. */ class DataSource : virtual public DataHandler { - public: - /** - * Constructor. - * @param busHandler the @a BusHandler instance. - */ - explicit DataSource(BusHandler* busHandler) - : m_busHandler(busHandler) {} + public: + /** + * Constructor. + * @param busHandler the @a BusHandler instance. + */ + explicit DataSource(BusHandler* busHandler) + : m_busHandler(busHandler) {} - /** - * Destructor. - */ - virtual ~DataSource() {} + /** + * Destructor. + */ + virtual ~DataSource() {} - // @copydoc - virtual bool isDataSource() { return true; } + // @copydoc + virtual bool isDataSource() { return true; } - protected: - /** the @a BusHandler instance. */ - BusHandler* m_busHandler; + protected: + /** the @a BusHandler instance. */ + BusHandler* m_busHandler; }; } // namespace ebusd diff --git a/src/ebusd/main.cpp b/src/ebusd/main.cpp index 9c7e61b1..47b88996 100644 --- a/src/ebusd/main.cpp +++ b/src/ebusd/main.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "main.h" @@ -75,38 +75,38 @@ static bool isDaemon = false; /** the program options. */ static struct options opt = { - "/dev/ttyUSB0", // device - false, // noDeviceCheck - false, // readOnly - false, // initialSend - -1, // latency - CONFIG_PATH, // configPath - false, // scanConfig - BROADCAST, // initialScan - 0, // checkConfig - 5, // pollInterval - 0x31, // address - false, // answer - 9400, // acquireTimeout - 3, // acquireRetries - 2, // sendRetries - SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout - 0, // masterCount - false, // generateSyn - false, // foreground - false, // enableHex - PID_FILE_NAME, // pidFile - 8888, // port - false, // localOnly - 0, // httpPort - "/var/" PACKAGE "/html", // htmlPath - PACKAGE_LOGFILE, // logFile - false, // logRaw - PACKAGE_LOGFILE, // logRawFile - 100, // logRawSize - false, // dump - "/tmp/" PACKAGE "_dump.bin", // dumpFile - 100, // dumpSize + "/dev/ttyUSB0", // device + false, // noDeviceCheck + false, // readOnly + false, // initialSend + -1, // latency + CONFIG_PATH, // configPath + false, // scanConfig + BROADCAST, // initialScan + 0, // checkConfig + 5, // pollInterval + 0x31, // address + false, // answer + 9400, // acquireTimeout + 3, // acquireRetries + 2, // sendRetries + SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout + 0, // masterCount + false, // generateSyn + false, // foreground + false, // enableHex + PID_FILE_NAME, // pidFile + 8888, // port + false, // localOnly + 0, // httpPort + "/var/" PACKAGE "/html", // htmlPath + PACKAGE_LOGFILE, // logFile + false, // logRaw + PACKAGE_LOGFILE, // logRawFile + 100, // logRawSize + false, // dump + "/tmp/" PACKAGE "_dump.bin", // dumpFile + 100, // dumpSize }; /** the @a MessageMap instance, or NULL. */ @@ -123,7 +123,7 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT""; /** the documentation of the program. */ static const char argpdoc[] = - "A daemon for communication with eBUS heating systems."; + "A daemon for communication with eBUS heating systems."; #define O_INISND 1 #define O_DEVLAT (O_INISND+1) @@ -152,55 +152,55 @@ static const char argpdoc[] = /** the definition of the known program arguments. */ static const struct argp_option argpoptions[] = { - {NULL, 0, NULL, 0, "Device options:", 1 }, - {"device", 'd', "DEV", 0, "Use DEV as eBUS device (serial or [udp:]ip:port) [/dev/ttyUSB0]", 0 }, - {"nodevicecheck", 'n', NULL, 0, "Skip serial eBUS device test", 0 }, - {"readonly", 'r', NULL, 0, "Only read from device, never write to it", 0 }, - {"initsend", O_INISND, NULL, 0, "Send an initial escape symbol after connecting device", 0 }, - {"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 }, + {NULL, 0, NULL, 0, "Device options:", 1 }, + {"device", 'd', "DEV", 0, "Use DEV as eBUS device (serial or [udp:]ip:port) [/dev/ttyUSB0]", 0 }, + {"nodevicecheck", 'n', NULL, 0, "Skip serial eBUS device test", 0 }, + {"readonly", 'r', NULL, 0, "Only read from device, never write to it", 0 }, + {"initsend", O_INISND, NULL, 0, "Send an initial escape symbol after connecting device", 0 }, + {"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 }, - {NULL, 0, NULL, 0, "Message configuration options:", 2 }, - {"configpath", 'c', "PATH", 0, "Read CSV config files from PATH [" CONFIG_PATH "]", 0 }, - {"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan (ADDR=\"none\" or empty for no initial scan message, \"full\" for full scan, or a single hex address to scan, default is broadcast ident message). If combined with --checkconfig, you can add scan message data as arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".", 0 }, - {"checkconfig", O_CHKCFG, NULL, 0, "Check CSV config files, then stop", 0 }, - {"dumpconfig", O_DMPCFG, NULL, 0, "Check and dump CSV config files, then stop", 0 }, - {"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]", 0 }, + {NULL, 0, NULL, 0, "Message configuration options:", 2 }, + {"configpath", 'c', "PATH", 0, "Read CSV config files from PATH [" CONFIG_PATH "]", 0 }, + {"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan (ADDR=\"none\" or empty for no initial scan message, \"full\" for full scan, or a single hex address to scan, default is broadcast ident message). If combined with --checkconfig, you can add scan message data as arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\".", 0 }, + {"checkconfig", O_CHKCFG, NULL, 0, "Check CSV config files, then stop", 0 }, + {"dumpconfig", O_DMPCFG, NULL, 0, "Check and dump CSV config files, then stop", 0 }, + {"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]", 0 }, - {NULL, 0, NULL, 0, "eBUS options:", 3 }, - {"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 }, - {"answer", O_ANSWER, NULL, 0, "Actively answer to requests from other masters", 0 }, - {"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 }, - {"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 }, - {"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 }, - {"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 }, - {"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 }, - {"generatesyn", O_GENSYN, NULL, 0, "Enable AUTO-SYN symbol generation", 0 }, + {NULL, 0, NULL, 0, "eBUS options:", 3 }, + {"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 }, + {"answer", O_ANSWER, NULL, 0, "Actively answer to requests from other masters", 0 }, + {"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 }, + {"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 }, + {"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 }, + {"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 }, + {"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 }, + {"generatesyn", O_GENSYN, NULL, 0, "Enable AUTO-SYN symbol generation", 0 }, - {NULL, 0, NULL, 0, "Daemon options:", 4 }, - {"foreground", 'f', NULL, 0, "Run in foreground", 0 }, - {"enablehex", O_HEXCMD, NULL, 0, "Enable hex command", 0 }, - {"pidfile", O_PIDFIL, "FILE", 0, "PID file name (only for daemon) [" PID_FILE_NAME "]", 0 }, - {"port", 'p', "PORT", 0, "Listen for command line connections on PORT [8888]", 0 }, - {"localhost", O_LOCAL, NULL, 0, "Listen for command line connections on 127.0.0.1 interface only", 0 }, - {"httpport", O_HTTPPT, "PORT", 0, "Listen for HTTP connections on PORT, 0 to disable [0]", 0 }, - {"htmlpath", O_HTMLPA, "PATH", 0, "Path for HTML files served by HTTP port [/var/ebusd/html]", 0 }, + {NULL, 0, NULL, 0, "Daemon options:", 4 }, + {"foreground", 'f', NULL, 0, "Run in foreground", 0 }, + {"enablehex", O_HEXCMD, NULL, 0, "Enable hex command", 0 }, + {"pidfile", O_PIDFIL, "FILE", 0, "PID file name (only for daemon) [" PID_FILE_NAME "]", 0 }, + {"port", 'p', "PORT", 0, "Listen for command line connections on PORT [8888]", 0 }, + {"localhost", O_LOCAL, NULL, 0, "Listen for command line connections on 127.0.0.1 interface only", 0 }, + {"httpport", O_HTTPPT, "PORT", 0, "Listen for HTTP connections on PORT, 0 to disable [0]", 0 }, + {"htmlpath", O_HTMLPA, "PATH", 0, "Path for HTML files served by HTTP port [/var/ebusd/html]", 0 }, - {NULL, 0, NULL, 0, "Log options:", 5 }, - {"logfile", 'l', "FILE", 0, "Write log to FILE (only for daemon) [" PACKAGE_LOGFILE "]", 0 }, - {"logareas", O_LOGARE, "AREAS", 0, "Only write log for matching AREA(S): main,network,bus,update,all [all]", 0 }, - {"loglevel", O_LOGLEV, "LEVEL", 0, "Only write log below or equal to LEVEL: error/notice/info/debug [notice]", 0 }, + {NULL, 0, NULL, 0, "Log options:", 5 }, + {"logfile", 'l', "FILE", 0, "Write log to FILE (only for daemon) [" PACKAGE_LOGFILE "]", 0 }, + {"logareas", O_LOGARE, "AREAS", 0, "Only write log for matching AREA(S): main,network,bus,update,all [all]", 0 }, + {"loglevel", O_LOGLEV, "LEVEL", 0, "Only write log below or equal to LEVEL: error/notice/info/debug [notice]", 0 }, - {NULL, 0, NULL, 0, "Raw logging options:", 6 }, - {"lograwdata", O_RAW, NULL, 0, "Log each received/sent byte on the bus", 0 }, - {"lograwdatafile", O_RAWFIL, "FILE", 0, "Write raw log to FILE [" PACKAGE_LOGFILE "]", 0 }, - {"lograwdatasize", O_RAWSIZ, "SIZE", 0, "Make raw log file no larger than SIZE kB [100]", 0 }, + {NULL, 0, NULL, 0, "Raw logging options:", 6 }, + {"lograwdata", O_RAW, NULL, 0, "Log each received/sent byte on the bus", 0 }, + {"lograwdatafile", O_RAWFIL, "FILE", 0, "Write raw log to FILE [" PACKAGE_LOGFILE "]", 0 }, + {"lograwdatasize", O_RAWSIZ, "SIZE", 0, "Make raw log file no larger than SIZE kB [100]", 0 }, - {NULL, 0, NULL, 0, "Binary dump options:", 7 }, - {"dump", 'D', NULL, 0, "Enable binary dump of received bytes", 0 }, - {"dumpfile", O_DMPFIL, "FILE", 0, "Dump received bytes to FILE [/tmp/" PACKAGE "_dump.bin]", 0 }, - {"dumpsize", O_DMPSIZ, "SIZE", 0, "Make dump file no larger than SIZE kB [100]", 0 }, + {NULL, 0, NULL, 0, "Binary dump options:", 7 }, + {"dump", 'D', NULL, 0, "Enable binary dump of received bytes", 0 }, + {"dumpfile", O_DMPFIL, "FILE", 0, "Dump received bytes to FILE [/tmp/" PACKAGE "_dump.bin]", 0 }, + {"dumpsize", O_DMPSIZ, "SIZE", 0, "Make dump file no larger than SIZE kB [100]", 0 }, - {NULL, 0, NULL, 0, NULL, 0 }, + {NULL, 0, NULL, 0, NULL, 0 }, }; /** the global @a DataFieldTemplates. */ @@ -219,362 +219,362 @@ static map s_templatesByPath; * @param state the parsing state. */ error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct options *opt = (struct options*)state->input; - result_t result = RESULT_OK; + struct options *opt = (struct options*)state->input; + result_t result = RESULT_OK; - switch (key) { - // Device options: - case 'd': // --device=/dev/ttyUSB0 - if (arg == NULL || arg[0] == 0) { - argp_error(state, "invalid device"); - return EINVAL; - } - opt->device = arg; - break; - case 'n': // --nodevicecheck - opt->noDeviceCheck = true; - break; - case 'r': // --readonly - opt->readOnly = true; - if (opt->scanConfig || opt->answer || opt->generateSyn) { - argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); - return EINVAL; - } - break; - case O_INISND: // --initsend - opt->initialSend = true; - break; - case O_DEVLAT: // --latency=10000 - opt->latency = parseInt(arg, 10, 0, 200000, result); - if (result != RESULT_OK) { - argp_error(state, "invalid latency"); - return EINVAL; - } - break; + switch (key) { + // Device options: + case 'd': // --device=/dev/ttyUSB0 + if (arg == NULL || arg[0] == 0) { + argp_error(state, "invalid device"); + return EINVAL; + } + opt->device = arg; + break; + case 'n': // --nodevicecheck + opt->noDeviceCheck = true; + break; + case 'r': // --readonly + opt->readOnly = true; + if (opt->scanConfig || opt->answer || opt->generateSyn) { + argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); + return EINVAL; + } + break; + case O_INISND: // --initsend + opt->initialSend = true; + break; + case O_DEVLAT: // --latency=10000 + opt->latency = parseInt(arg, 10, 0, 200000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid latency"); + return EINVAL; + } + break; - // Message configuration options: - case 'c': // --configpath=/etc/ebusd - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid configpath"); - return EINVAL; - } - opt->configPath = arg; - break; - case 's': // --scanconfig[=ADDR] (ADDR=|full|) - opt->scanConfig = true; - if (opt->readOnly) { - argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); - return EINVAL; - } - if (opt->pollInterval == 0) { - argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!"); - return EINVAL; - } - if (arg != NULL) { - if (arg[0] == 0 || strcmp("none", arg) == 0) { - opt->initialScan = ESC; - } else if (strcmp("full", arg) == 0) { - opt->initialScan = SYN; - } else { - opt->initialScan = (unsigned char)parseInt(arg, 16, 0x00, 0xff, result); - if (!isValidAddress(opt->initialScan)) { - argp_error(state, "invalid initial scan address"); - return EINVAL; - } - if (isMaster(opt->initialScan)) { - opt->initialScan = (unsigned char)(opt->initialScan+5); - } - } - } - break; - case O_CHKCFG: // --checkconfig - if (opt->checkConfig == 0) { - opt->checkConfig = 1; - } - break; - case O_DMPCFG: // --dumpconfig - opt->checkConfig = 2; - break; - case O_POLINT: // --pollinterval=5 - opt->pollInterval = parseInt(arg, 10, 0, 3600, result); - if (result != RESULT_OK) { - argp_error(state, "invalid pollinterval"); - return EINVAL; - } - if (opt->pollInterval == 0 && opt->scanConfig) { - argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!"); - return EINVAL; - } - break; + // Message configuration options: + case 'c': // --configpath=/etc/ebusd + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid configpath"); + return EINVAL; + } + opt->configPath = arg; + break; + case 's': // --scanconfig[=ADDR] (ADDR=|full|) + opt->scanConfig = true; + if (opt->readOnly) { + argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); + return EINVAL; + } + if (opt->pollInterval == 0) { + argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!"); + return EINVAL; + } + if (arg != NULL) { + if (arg[0] == 0 || strcmp("none", arg) == 0) { + opt->initialScan = ESC; + } else if (strcmp("full", arg) == 0) { + opt->initialScan = SYN; + } else { + opt->initialScan = (unsigned char)parseInt(arg, 16, 0x00, 0xff, result); + if (!isValidAddress(opt->initialScan)) { + argp_error(state, "invalid initial scan address"); + return EINVAL; + } + if (isMaster(opt->initialScan)) { + opt->initialScan = (unsigned char)(opt->initialScan+5); + } + } + } + break; + case O_CHKCFG: // --checkconfig + if (opt->checkConfig == 0) { + opt->checkConfig = 1; + } + break; + case O_DMPCFG: // --dumpconfig + opt->checkConfig = 2; + break; + case O_POLINT: // --pollinterval=5 + opt->pollInterval = parseInt(arg, 10, 0, 3600, result); + if (result != RESULT_OK) { + argp_error(state, "invalid pollinterval"); + return EINVAL; + } + if (opt->pollInterval == 0 && opt->scanConfig) { + argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!"); + return EINVAL; + } + break; - // eBUS options: - case 'a': // --address=31 - opt->address = (unsigned char)parseInt(arg, 16, 0, 0xff, result); - if (result != RESULT_OK || !isMaster(opt->address)) { - argp_error(state, "invalid address"); - return EINVAL; - } - break; - case O_ANSWER: // --answer - opt->answer = true; - if (opt->readOnly) { - argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); - return EINVAL; - } - break; - case O_ACQTIM: // --acquiretimeout=9400 - opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, result); - if (result != RESULT_OK) { - argp_error(state, "invalid acquiretimeout"); - return EINVAL; - } - break; - case O_ACQRET: // --acquireretries=3 - opt->acquireRetries = parseInt(arg, 10, 0, 10, result); - if (result != RESULT_OK) { - argp_error(state, "invalid acquireretries"); - return EINVAL; - } - break; - case O_SNDRET: // --sendretries=2 - opt->sendRetries = parseInt(arg, 10, 0, 10, result); - if (result != RESULT_OK) { - argp_error(state, "invalid sendretries"); - return EINVAL; - } - break; - case O_RCVTIM: // --receivetimeout=25000 - opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, result); - if (result != RESULT_OK) { - argp_error(state, "invalid receivetimeout"); - return EINVAL; - } - break; - case O_MASCNT: // --numbermasters=0 - opt->masterCount = parseInt(arg, 10, 0, 25, result); - if (result != RESULT_OK) { - argp_error(state, "invalid numbermasters"); - return EINVAL; - } - break; - case O_GENSYN: // --generatesyn - opt->generateSyn = true; - if (opt->readOnly) { - argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); - return EINVAL; - } - break; + // eBUS options: + case 'a': // --address=31 + opt->address = (unsigned char)parseInt(arg, 16, 0, 0xff, result); + if (result != RESULT_OK || !isMaster(opt->address)) { + argp_error(state, "invalid address"); + return EINVAL; + } + break; + case O_ANSWER: // --answer + opt->answer = true; + if (opt->readOnly) { + argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); + return EINVAL; + } + break; + case O_ACQTIM: // --acquiretimeout=9400 + opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid acquiretimeout"); + return EINVAL; + } + break; + case O_ACQRET: // --acquireretries=3 + opt->acquireRetries = parseInt(arg, 10, 0, 10, result); + if (result != RESULT_OK) { + argp_error(state, "invalid acquireretries"); + return EINVAL; + } + break; + case O_SNDRET: // --sendretries=2 + opt->sendRetries = parseInt(arg, 10, 0, 10, result); + if (result != RESULT_OK) { + argp_error(state, "invalid sendretries"); + return EINVAL; + } + break; + case O_RCVTIM: // --receivetimeout=25000 + opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid receivetimeout"); + return EINVAL; + } + break; + case O_MASCNT: // --numbermasters=0 + opt->masterCount = parseInt(arg, 10, 0, 25, result); + if (result != RESULT_OK) { + argp_error(state, "invalid numbermasters"); + return EINVAL; + } + break; + case O_GENSYN: // --generatesyn + opt->generateSyn = true; + if (opt->readOnly) { + argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); + return EINVAL; + } + break; - // Daemon options: - case 'f': // --foreground - opt->foreground = true; - break; - case O_HEXCMD: // --enablehex - opt->enableHex = true; - break; - case O_PIDFIL: // --pidfile=/var/run/ebusd.pid - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid pidfile"); - return EINVAL; - } - opt->pidFile = arg; - break; - case 'p': // --port=8888 - opt->port = (uint16_t)parseInt(arg, 10, 1, 65535, result); - if (result != RESULT_OK) { - argp_error(state, "invalid port"); - return EINVAL; - } - break; - case O_LOCAL: // --localhost - opt->localOnly = true; - break; - case O_HTTPPT: // --httpport=0 - opt->httpPort = (uint16_t)parseInt(arg, 10, 1, 65535, result); - if (result != RESULT_OK) { - argp_error(state, "invalid httpport"); - return EINVAL; - } - break; - case O_HTMLPA: // --htmlpath=/var/ebusd/html - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid htmlpath"); - return EINVAL; - } - opt->htmlPath = arg; - break; + // Daemon options: + case 'f': // --foreground + opt->foreground = true; + break; + case O_HEXCMD: // --enablehex + opt->enableHex = true; + break; + case O_PIDFIL: // --pidfile=/var/run/ebusd.pid + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid pidfile"); + return EINVAL; + } + opt->pidFile = arg; + break; + case 'p': // --port=8888 + opt->port = (uint16_t)parseInt(arg, 10, 1, 65535, result); + if (result != RESULT_OK) { + argp_error(state, "invalid port"); + return EINVAL; + } + break; + case O_LOCAL: // --localhost + opt->localOnly = true; + break; + case O_HTTPPT: // --httpport=0 + opt->httpPort = (uint16_t)parseInt(arg, 10, 1, 65535, result); + if (result != RESULT_OK) { + argp_error(state, "invalid httpport"); + return EINVAL; + } + break; + case O_HTMLPA: // --htmlpath=/var/ebusd/html + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid htmlpath"); + return EINVAL; + } + opt->htmlPath = arg; + break; - // Log options: - case 'l': // --logfile=/var/log/ebusd.log - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid logfile"); - return EINVAL; - } - opt->logFile = arg; - break; - case O_LOGARE: // --logareas=all - if (!setLogFacilities(arg)) { - argp_error(state, "invalid logareas"); - return EINVAL; - } - break; - case O_LOGLEV: // --loglevel=notice - if (!setLogLevel(arg)) { - argp_error(state, "invalid loglevel"); - return EINVAL; - } - break; + // Log options: + case 'l': // --logfile=/var/log/ebusd.log + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid logfile"); + return EINVAL; + } + opt->logFile = arg; + break; + case O_LOGARE: // --logareas=all + if (!setLogFacilities(arg)) { + argp_error(state, "invalid logareas"); + return EINVAL; + } + break; + case O_LOGLEV: // --loglevel=notice + if (!setLogLevel(arg)) { + argp_error(state, "invalid loglevel"); + return EINVAL; + } + break; - // Raw logging options: - case O_RAW: // --lograwdata - opt->logRaw = true; - break; - case O_RAWFIL: // --lograwdatafile=/var/log/ebusd.log - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid lograwdatafile"); - return EINVAL; - } - opt->logRawFile = arg; - break; - case O_RAWSIZ: // --lograwdatasize=100 - opt->logRawSize = (unsigned int)parseInt(arg, 10, 1, 1000000, result); - if (result != RESULT_OK) { - argp_error(state, "invalid lograwdatasize"); - return EINVAL; - } - break; + // Raw logging options: + case O_RAW: // --lograwdata + opt->logRaw = true; + break; + case O_RAWFIL: // --lograwdatafile=/var/log/ebusd.log + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid lograwdatafile"); + return EINVAL; + } + opt->logRawFile = arg; + break; + case O_RAWSIZ: // --lograwdatasize=100 + opt->logRawSize = (unsigned int)parseInt(arg, 10, 1, 1000000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid lograwdatasize"); + return EINVAL; + } + break; - // Binary dump options: - case 'D': // --dump - opt->dump = true; - break; - case O_DMPFIL: // --dumpfile=/tmp/ebusd_dump.bin - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid dumpfile"); - return EINVAL; - } - opt->dumpFile = arg; - break; - case O_DMPSIZ: // --dumpsize=100 - opt->dumpSize = (unsigned int)parseInt(arg, 10, 1, 1000000, result); - if (result != RESULT_OK) { - argp_error(state, "invalid dumpsize"); - return EINVAL; - } - break; + // Binary dump options: + case 'D': // --dump + opt->dump = true; + break; + case O_DMPFIL: // --dumpfile=/tmp/ebusd_dump.bin + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid dumpfile"); + return EINVAL; + } + opt->dumpFile = arg; + break; + case O_DMPSIZ: // --dumpsize=100 + opt->dumpSize = (unsigned int)parseInt(arg, 10, 1, 1000000, result); + if (result != RESULT_OK) { + argp_error(state, "invalid dumpsize"); + return EINVAL; + } + break; - case ARGP_KEY_ARG: - if (!opt->checkConfig) { - argp_error(state, "invalid arguments starting with \"%s\"", arg); - return EINVAL; - } - return ARGP_ERR_UNKNOWN; - default: - return ARGP_ERR_UNKNOWN; - } + case ARGP_KEY_ARG: + if (!opt->checkConfig) { + argp_error(state, "invalid arguments starting with \"%s\"", arg); + return EINVAL; + } + return ARGP_ERR_UNKNOWN; + default: + return ARGP_ERR_UNKNOWN; + } - return 0; + return 0; } void daemonize() { - // fork off the parent process - pid_t pid = fork(); + // fork off the parent process + pid_t pid = fork(); - if (pid < 0) { - logError(lf_main, "fork() failed"); - exit(EXIT_FAILURE); - } + if (pid < 0) { + logError(lf_main, "fork() failed"); + exit(EXIT_FAILURE); + } - // If we got a good PID, then we can exit the parent process - if (pid > 0) { - exit(EXIT_SUCCESS); - } - // At this point we are executing as the child process + // If we got a good PID, then we can exit the parent process + if (pid > 0) { + exit(EXIT_SUCCESS); + } + // At this point we are executing as the child process - // Create a new SID for the child process and - // detach the process from the parent (normally a shell) - if (setsid() < 0) { - logError(lf_main, "setsid() failed"); - exit(EXIT_FAILURE); - } + // Create a new SID for the child process and + // detach the process from the parent (normally a shell) + if (setsid() < 0) { + logError(lf_main, "setsid() failed"); + exit(EXIT_FAILURE); + } - // Change the current working directory. This prevents the current - // directory from being locked; hence not being able to remove it. - if (chdir("/tmp") < 0) { - logError(lf_main, "daemon chdir() failed"); - exit(EXIT_FAILURE); - } + // Change the current working directory. This prevents the current + // directory from being locked; hence not being able to remove it. + if (chdir("/tmp") < 0) { + logError(lf_main, "daemon chdir() failed"); + exit(EXIT_FAILURE); + } - // Close stdin, stdout and stderr - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); + // Close stdin, stdout and stderr + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); - // create pid file and try to lock it - pidFile = fopen(opt.pidFile, "w+"); + // create pid file and try to lock it + pidFile = fopen(opt.pidFile, "w+"); - umask(S_IWGRP | S_IRWXO); // set permissions of newly created files to 750 + umask(S_IWGRP | S_IRWXO); // set permissions of newly created files to 750 - if (pidFile != NULL) { - setbuf(pidFile, NULL); // disable buffering - if (lockf(fileno(pidFile), F_TLOCK, 0) < 0 - || fprintf(pidFile, "%d\n", getpid()) <= 0) { - fclose(pidFile); - pidFile = NULL; - } - } - if (pidFile == NULL) { - logError(lf_main, "can't open pidfile: %s", opt.pidFile); - exit(EXIT_FAILURE); - } + if (pidFile != NULL) { + setbuf(pidFile, NULL); // disable buffering + if (lockf(fileno(pidFile), F_TLOCK, 0) < 0 + || fprintf(pidFile, "%d\n", getpid()) <= 0) { + fclose(pidFile); + pidFile = NULL; + } + } + if (pidFile == NULL) { + logError(lf_main, "can't open pidfile: %s", opt.pidFile); + exit(EXIT_FAILURE); + } - isDaemon = true; + isDaemon = true; } void closePidFile() { - if (pidFile != NULL) { - if (fclose(pidFile) != 0) { - return; - } - remove(opt.pidFile); - } + if (pidFile != NULL) { + if (fclose(pidFile) != 0) { + return; + } + remove(opt.pidFile); + } } /** * Helper method performing shutdown. */ void shutdown() { - // stop main loop and all dependent components - if (s_mainLoop != NULL) { - delete s_mainLoop; - s_mainLoop = NULL; - } - if (s_messageMap != NULL) { - delete s_messageMap; - s_messageMap = NULL; - } - // free templates - for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { - if (it->second != &s_globalTemplates) { - delete it->second; - } - it->second = NULL; - } - s_templatesByPath.clear(); + // stop main loop and all dependent components + if (s_mainLoop != NULL) { + delete s_mainLoop; + s_mainLoop = NULL; + } + if (s_messageMap != NULL) { + delete s_messageMap; + s_messageMap = NULL; + } + // free templates + for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { + if (it->second != &s_globalTemplates) { + delete it->second; + } + it->second = NULL; + } + s_templatesByPath.clear(); - // reset all signal handlers to default - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); + // reset all signal handlers to default + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); - // delete daemon pid file if necessary - closePidFile(); + // delete daemon pid file if necessary + closePidFile(); - logNotice(lf_main, "ebusd stopped"); - closeLogFile(); + logNotice(lf_main, "ebusd stopped"); + closeLogFile(); - exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); } /** @@ -582,22 +582,22 @@ void shutdown() { * @param sig the received signal. */ void signalHandler(int sig) { - switch (sig) { - case SIGHUP: - logNotice(lf_main, "SIGHUP received"); - break; - case SIGINT: - logNotice(lf_main, "SIGINT received"); - shutdown(); - break; - case SIGTERM: - logNotice(lf_main, "SIGTERM received"); - shutdown(); - break; - default: - logNotice(lf_main, "undefined signal %s", strsignal(sig)); - break; - } + switch (sig) { + case SIGHUP: + logNotice(lf_main, "SIGHUP received"); + break; + case SIGINT: + logNotice(lf_main, "SIGINT received"); + shutdown(); + break; + case SIGTERM: + logNotice(lf_main, "SIGTERM received"); + shutdown(); + break; + default: + logNotice(lf_main, "undefined signal %s", strsignal(sig)); + break; + } } /** @@ -611,56 +611,56 @@ void signalHandler(int sig) { * @return the result code. */ static result_t collectConfigFiles(const string path, const string prefix, const string extension, - vector& files, vector* dirs = NULL, bool* hasTemplates = NULL) { - DIR* dir = opendir(path.c_str()); + vector& files, vector* dirs = NULL, bool* hasTemplates = NULL) { + DIR* dir = opendir(path.c_str()); - if (dir == NULL) { - return RESULT_ERR_NOTFOUND; - } - dirent* d; - while ((d = readdir(dir)) != NULL) { - string name = d->d_name; + if (dir == NULL) { + return RESULT_ERR_NOTFOUND; + } + dirent* d; + while ((d = readdir(dir)) != NULL) { + string name = d->d_name; - if (name == "." || name == "..") { - continue; - } - const string p = path + "/" + name; - struct stat stat_buf; + if (name == "." || name == "..") { + continue; + } + const string p = path + "/" + name; + struct stat stat_buf; - if (stat(p.c_str(), &stat_buf) != 0) { - continue; - } - if (S_ISDIR(stat_buf.st_mode)) { - if (dirs != NULL) { - dirs->push_back(p); - } - } else if (S_ISREG(stat_buf.st_mode) && name.length() >= extension.length() - && name.substr(name.length()-extension.length()) == extension) { - if (name == "_templates"+extension) { - if (hasTemplates) { - *hasTemplates = true; - } - } else if (prefix.length() == 0 || (name.length() >= prefix.length() && name.substr(0, prefix.length()) == prefix)) { - files.push_back(p); - } - } - } - closedir(dir); + if (stat(p.c_str(), &stat_buf) != 0) { + continue; + } + if (S_ISDIR(stat_buf.st_mode)) { + if (dirs != NULL) { + dirs->push_back(p); + } + } else if (S_ISREG(stat_buf.st_mode) && name.length() >= extension.length() + && name.substr(name.length()-extension.length()) == extension) { + if (name == "_templates"+extension) { + if (hasTemplates) { + *hasTemplates = true; + } + } else if (prefix.length() == 0 || (name.length() >= prefix.length() && name.substr(0, prefix.length()) == prefix)) { + files.push_back(p); + } + } + } + closedir(dir); - return RESULT_OK; + return RESULT_OK; } DataFieldTemplates* getTemplates(const string filename) { - string path; - size_t pos = filename.find_last_of('/'); - if (pos != string::npos) { - path = filename.substr(0, pos); - } - map::iterator it = s_templatesByPath.find(path); - if (it != s_templatesByPath.end()) { - return it->second; - } - return &s_globalTemplates; + string path; + size_t pos = filename.find_last_of('/'); + if (pos != string::npos) { + path = filename.substr(0, pos); + } + map::iterator it = s_templatesByPath.find(path); + if (it != s_templatesByPath.end()) { + return it->second; + } + return &s_globalTemplates; } /** @@ -673,28 +673,28 @@ DataFieldTemplates* getTemplates(const string filename) { * @return the @a DataFieldTemplates. */ static bool readTemplates(const string path, const string extension, bool available, bool verbose = false) { - map::iterator it = s_templatesByPath.find(path); - if (it != s_templatesByPath.end()) { - return false; - } - DataFieldTemplates* templates; - if (path == opt.configPath || !available) { - templates = &s_globalTemplates; - } else { - templates = new DataFieldTemplates(s_globalTemplates); - } - s_templatesByPath[path] = templates; - if (!available) { - // global templates are stored as replacement in order to determine whether the directory was already loaded - return true; - } - result_t result = templates->readFromFile(path+"/_templates"+extension, verbose); - if (result == RESULT_OK) { - logInfo(lf_main, "read templates in %s", path.c_str()); - } else { - logError(lf_main, "error reading templates in %s: %s, last error: %s", path.c_str(), getResultCode(result), templates->getLastError().c_str()); - } - return templates; + map::iterator it = s_templatesByPath.find(path); + if (it != s_templatesByPath.end()) { + return false; + } + DataFieldTemplates* templates; + if (path == opt.configPath || !available) { + templates = &s_globalTemplates; + } else { + templates = new DataFieldTemplates(s_globalTemplates); + } + s_templatesByPath[path] = templates; + if (!available) { + // global templates are stored as replacement in order to determine whether the directory was already loaded + return true; + } + result_t result = templates->readFromFile(path+"/_templates"+extension, verbose); + if (result == RESULT_OK) { + logInfo(lf_main, "read templates in %s", path.c_str()); + } else { + logError(lf_main, "error reading templates in %s: %s, last error: %s", path.c_str(), getResultCode(result), templates->getLastError().c_str()); + } + return templates; } /** @@ -707,32 +707,32 @@ static bool readTemplates(const string path, const string extension, bool availa * @return the result code. */ static result_t readConfigFiles(const string path, const string extension, MessageMap* messages, bool recursive, bool verbose) { - vector files, dirs; - bool hasTemplates = false; - result_t result = collectConfigFiles(path, "", extension, files, &dirs, &hasTemplates); - if (result != RESULT_OK) { - return result; - } - readTemplates(path, extension, hasTemplates, verbose); - for (vector::iterator it = files.begin(); it != files.end(); it++) { - string name = *it; - logInfo(lf_main, "reading file %s", name.c_str()); - result_t result = messages->readFromFile(name, verbose); - if (result != RESULT_OK) { - return result; - } - } - if (recursive) { - for (vector::iterator it = dirs.begin(); it != dirs.end(); it++) { - string name = *it; - logInfo(lf_main, "reading dir %s", name.c_str()); - result_t result = readConfigFiles(name, extension, messages, true, verbose); - if (result != RESULT_OK) { - return result; - } - } - } - return RESULT_OK; + vector files, dirs; + bool hasTemplates = false; + result_t result = collectConfigFiles(path, "", extension, files, &dirs, &hasTemplates); + if (result != RESULT_OK) { + return result; + } + readTemplates(path, extension, hasTemplates, verbose); + for (vector::iterator it = files.begin(); it != files.end(); it++) { + string name = *it; + logInfo(lf_main, "reading file %s", name.c_str()); + result_t result = messages->readFromFile(name, verbose); + if (result != RESULT_OK) { + return result; + } + } + if (recursive) { + for (vector::iterator it = dirs.begin(); it != dirs.end(); it++) { + string name = *it; + logInfo(lf_main, "reading dir %s", name.c_str()); + result_t result = readConfigFiles(name, extension, messages, true, verbose); + if (result != RESULT_OK) { + return result; + } + } + } + return RESULT_OK; } /** @@ -740,14 +740,14 @@ static result_t readConfigFiles(const string path, const string extension, Messa * @param message the @a Message to read. */ void readMessage(Message* message) { - if (!s_mainLoop || !message) { - return; - } - BusHandler* busHandler = s_mainLoop->getBusHandler(); - result_t result = busHandler->readFromBus(message, ""); - if (result != RESULT_OK) { - logError(lf_main, "error reading message %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result)); - } + if (!s_mainLoop || !message) { + return; + } + BusHandler* busHandler = s_mainLoop->getBusHandler(); + result_t result = busHandler->readFromBus(message, ""); + if (result != RESULT_OK) { + logError(lf_main, "error reading message %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result)); + } } /** @@ -756,191 +756,191 @@ void readMessage(Message* message) { * @param verbose whether to verbosely log all problems. */ void executeInstructions(MessageMap* messages, bool verbose) { - result_t result = messages->resolveConditions(verbose); - if (result != RESULT_OK) { - logError(lf_main, "error resolving conditions: %s, last error: %s", getResultCode(result), messages->getLastError().c_str()); - } - ostringstream log; - result = messages->executeInstructions(log, readMessage); - if (result != RESULT_OK) { - logError(lf_main, "error executing instructions: %s, last error: %s, %s", getResultCode(result), messages->getLastError().c_str(), log.str().c_str()); - } else if (verbose && log.tellp() > 0) { - logInfo(lf_main, log.str().c_str()); - } - logNotice(lf_main, "found messages: %d (%d conditional on %d conditions, %d poll, %d update)", messages->size(), messages->sizeConditional(), messages->sizeConditions(), messages->sizePoll(), messages->sizePassive()); + result_t result = messages->resolveConditions(verbose); + if (result != RESULT_OK) { + logError(lf_main, "error resolving conditions: %s, last error: %s", getResultCode(result), messages->getLastError().c_str()); + } + ostringstream log; + result = messages->executeInstructions(log, readMessage); + if (result != RESULT_OK) { + logError(lf_main, "error executing instructions: %s, last error: %s, %s", getResultCode(result), messages->getLastError().c_str(), log.str().c_str()); + } else if (verbose && log.tellp() > 0) { + logInfo(lf_main, log.str().c_str()); + } + logNotice(lf_main, "found messages: %d (%d conditional on %d conditions, %d poll, %d update)", messages->size(), messages->sizeConditional(), messages->sizeConditions(), messages->sizePoll(), messages->sizePassive()); } result_t loadConfigFiles(MessageMap* messages, bool verbose, bool denyRecursive) { - logInfo(lf_main, "loading configuration files from %s", opt.configPath); - messages->clear(); - s_globalTemplates.clear(); - for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { - if (it->second != &s_globalTemplates) { - delete it->second; - } - it->second = NULL; - } - s_templatesByPath.clear(); + logInfo(lf_main, "loading configuration files from %s", opt.configPath); + messages->clear(); + s_globalTemplates.clear(); + for (map::iterator it = s_templatesByPath.begin(); it != s_templatesByPath.end(); it++) { + if (it->second != &s_globalTemplates) { + delete it->second; + } + it->second = NULL; + } + s_templatesByPath.clear(); - result_t result = readConfigFiles(string(opt.configPath), ".csv", messages, (!opt.scanConfig || opt.checkConfig) && !denyRecursive, verbose); - if (result == RESULT_OK) { - logInfo(lf_main, "read config files"); - } else { - logError(lf_main, "error reading config files: %s, last error: %s", getResultCode(result), messages->getLastError().c_str()); - } - executeInstructions(messages, verbose); - return RESULT_OK; + result_t result = readConfigFiles(string(opt.configPath), ".csv", messages, (!opt.scanConfig || opt.checkConfig) && !denyRecursive, verbose); + if (result == RESULT_OK) { + logInfo(lf_main, "read config files"); + } else { + logError(lf_main, "error reading config files: %s, last error: %s", getResultCode(result), messages->getLastError().c_str()); + } + executeInstructions(messages, verbose); + return RESULT_OK; } result_t loadScanConfigFile(MessageMap* messages, unsigned char address, SymbolString& data, string& relativeFile, bool verbose) { - PartType partType; - if (isMaster(address)) { - address = (unsigned char)(data[0]+5); // slave address of sending master - partType = pt_masterData; - if (data.size() < 5+1+5+2+2) { // skip QQ ZZ PB SB NN - logError(lf_main, "unable to load scan config %2.2x: master part too short", address); - return RESULT_EMPTY; - } - } else { - partType = pt_slaveData; - if (data.size() < 1+1+5+2+2) { // skip NN - logError(lf_main, "unable to load scan config %2.2x: slave part too short", address); - return RESULT_EMPTY; - } - } - DataFieldSet* identFields = DataFieldSet::getIdentFields(); - string path, prefix, ident; // path: cfgpath/MANUFACTURER, prefix: ZZ., ident: C[C[C[C[C]]]], SW: xxxx, HW: xxxx - unsigned int sw, hw; - ostringstream out; - unsigned char offset = 0; - size_t field = 0; - result_t result = (*identFields)[field]->read(partType, data, offset, out, 0); // manufacturer name - if (result == RESULT_ERR_NOTFOUND) { - result = (*identFields)[field]->read(partType, data, offset, out, OF_NUMERIC); // manufacturer name - } - if (result == RESULT_OK) { - path = out.str(); - transform(path.begin(), path.end(), path.begin(), ::tolower); - path = string(opt.configPath) + "/" + path; - out.str(""); - out << setw(2) << hex << setfill('0') << nouppercase << static_cast(address) << "."; - prefix = out.str(); - out.str(""); - out.clear(); - offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); - result = (*identFields)[field]->read(partType, data, offset, out, 0); // identification string - } - if (result == RESULT_OK) { - ident = out.str(); - out.str(""); - offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); - result = (*identFields)[field]->read(partType, data, offset, sw, 0); // software version number - } - if (result == RESULT_OK) { - offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); - result = (*identFields)[field]->read(partType, data, offset, hw, 0); // hardware version number - } - if (result != RESULT_OK) { - logError(lf_main, "unable to load scan config %2.2x: decode %s", address, getResultCode(result)); - return result; - } - vector files; - bool hasTemplates = false; - // find files matching MANUFACTURER/ZZ.*csv in cfgpath - result = collectConfigFiles(path, prefix, ".csv", files, NULL, &hasTemplates); - if (result != RESULT_OK) { - logError(lf_main, "unable to load scan config %2.2x: list files in %s %s", address, path.c_str(), getResultCode(result)); - return result; - } - if (files.empty()) { - logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s found", address, path.c_str(), prefix.c_str()); - return RESULT_ERR_NOTFOUND; - } - logDebug(lf_main, "found %d matching scan config files from %s with prefix %s: %s", files.size(), path.c_str(), prefix.c_str(), getResultCode(result)); - for (string::iterator it = ident.begin(); it != ident.end(); it++) { - if (::isspace(*it)) { - ident.erase(it--); - } else { - *it = static_cast(::tolower(*it)); - } - } - // complete name: cfgpath/MANUFACTURER/ZZ[.C[C[C[C[C]]]]][.circuit][.suffix][.*][.SWxxxx][.HWxxxx][.*].csv - size_t bestMatch = 0; - string best; - for (vector::iterator it = files.begin(); it != files.end(); it++) { - string name = *it; - unsigned char checkDest; - string checkIdent, useCircuit, useSuffix; - unsigned int checkSw, checkHw; - if (!FileReader::extractDefaultsFromFilename(name.substr(path.length()+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { - continue; - } - if (address != checkDest || (checkSw != UINT_MAX && sw != checkSw) || (checkHw != UINT_MAX && hw != checkHw)) { - continue; - } - size_t match = 1; - if (!checkIdent.empty()) { - string remain = ident; - bool matches = false; - while (remain.length() > 0 && remain.length() >= checkIdent.length()) { - if (checkIdent == remain) { - matches = true; - break; - } - if (remain[remain.length()-1] < '0' || remain[remain.length()-1] > '9') { - break; - } - remain.erase(remain.length()-1); // remove trailing digit - } - if (!matches) { - continue; // IDENT mismatch - } - match += remain.length(); - } - if (match >= bestMatch) { - bestMatch = match; - best = name; - } - break; - } + PartType partType; + if (isMaster(address)) { + address = (unsigned char)(data[0]+5); // slave address of sending master + partType = pt_masterData; + if (data.size() < 5+1+5+2+2) { // skip QQ ZZ PB SB NN + logError(lf_main, "unable to load scan config %2.2x: master part too short", address); + return RESULT_EMPTY; + } + } else { + partType = pt_slaveData; + if (data.size() < 1+1+5+2+2) { // skip NN + logError(lf_main, "unable to load scan config %2.2x: slave part too short", address); + return RESULT_EMPTY; + } + } + DataFieldSet* identFields = DataFieldSet::getIdentFields(); + string path, prefix, ident; // path: cfgpath/MANUFACTURER, prefix: ZZ., ident: C[C[C[C[C]]]], SW: xxxx, HW: xxxx + unsigned int sw, hw; + ostringstream out; + unsigned char offset = 0; + size_t field = 0; + result_t result = (*identFields)[field]->read(partType, data, offset, out, 0); // manufacturer name + if (result == RESULT_ERR_NOTFOUND) { + result = (*identFields)[field]->read(partType, data, offset, out, OF_NUMERIC); // manufacturer name + } + if (result == RESULT_OK) { + path = out.str(); + transform(path.begin(), path.end(), path.begin(), ::tolower); + path = string(opt.configPath) + "/" + path; + out.str(""); + out << setw(2) << hex << setfill('0') << nouppercase << static_cast(address) << "."; + prefix = out.str(); + out.str(""); + out.clear(); + offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); + result = (*identFields)[field]->read(partType, data, offset, out, 0); // identification string + } + if (result == RESULT_OK) { + ident = out.str(); + out.str(""); + offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); + result = (*identFields)[field]->read(partType, data, offset, sw, 0); // software version number + } + if (result == RESULT_OK) { + offset = (unsigned char)(offset+(*identFields)[field++]->getLength(partType)); + result = (*identFields)[field]->read(partType, data, offset, hw, 0); // hardware version number + } + if (result != RESULT_OK) { + logError(lf_main, "unable to load scan config %2.2x: decode %s", address, getResultCode(result)); + return result; + } + vector files; + bool hasTemplates = false; + // find files matching MANUFACTURER/ZZ.*csv in cfgpath + result = collectConfigFiles(path, prefix, ".csv", files, NULL, &hasTemplates); + if (result != RESULT_OK) { + logError(lf_main, "unable to load scan config %2.2x: list files in %s %s", address, path.c_str(), getResultCode(result)); + return result; + } + if (files.empty()) { + logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s found", address, path.c_str(), prefix.c_str()); + return RESULT_ERR_NOTFOUND; + } + logDebug(lf_main, "found %d matching scan config files from %s with prefix %s: %s", files.size(), path.c_str(), prefix.c_str(), getResultCode(result)); + for (string::iterator it = ident.begin(); it != ident.end(); it++) { + if (::isspace(*it)) { + ident.erase(it--); + } else { + *it = static_cast(::tolower(*it)); + } + } + // complete name: cfgpath/MANUFACTURER/ZZ[.C[C[C[C[C]]]]][.circuit][.suffix][.*][.SWxxxx][.HWxxxx][.*].csv + size_t bestMatch = 0; + string best; + for (vector::iterator it = files.begin(); it != files.end(); it++) { + string name = *it; + unsigned char checkDest; + string checkIdent, useCircuit, useSuffix; + unsigned int checkSw, checkHw; + if (!FileReader::extractDefaultsFromFilename(name.substr(path.length()+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { + continue; + } + if (address != checkDest || (checkSw != UINT_MAX && sw != checkSw) || (checkHw != UINT_MAX && hw != checkHw)) { + continue; + } + size_t match = 1; + if (!checkIdent.empty()) { + string remain = ident; + bool matches = false; + while (remain.length() > 0 && remain.length() >= checkIdent.length()) { + if (checkIdent == remain) { + matches = true; + break; + } + if (remain[remain.length()-1] < '0' || remain[remain.length()-1] > '9') { + break; + } + remain.erase(remain.length()-1); // remove trailing digit + } + if (!matches) { + continue; // IDENT mismatch + } + match += remain.length(); + } + if (match >= bestMatch) { + bestMatch = match; + best = name; + } + break; + } - if (best.empty()) { - logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s matches ID \"%s\", SW%4.4d, HW%4.4d", address, path.c_str(), prefix.c_str(), ident.c_str(), sw, hw); - return RESULT_ERR_NOTFOUND; - } + if (best.empty()) { + logError(lf_main, "unable to load scan config %2.2x: no file from %s with prefix %s matches ID \"%s\", SW%4.4d, HW%4.4d", address, path.c_str(), prefix.c_str(), ident.c_str(), sw, hw); + return RESULT_ERR_NOTFOUND; + } - // found the right file. load the templates if necessary, then load the file itself - bool readCommon = readTemplates(path, ".csv", hasTemplates, opt.checkConfig); - if (readCommon) { - result = collectConfigFiles(path, "", ".csv", files); - if (result == RESULT_OK && !files.empty()) { - for (vector::iterator it = files.begin(); it != files.end(); it++) { - string name = *it; - name = name.substr(path.length()+1, name.length()-path.length()-strlen(".csv")); // *. - if (name == "_templates.") { // skip templates - continue; - } - if (name.length() < 3 || name.find_first_of('.') != 2) { // different from the scheme "ZZ." - name = *it; - result = messages->readFromFile(name, opt.checkConfig); - if (result == RESULT_OK) { - logNotice(lf_main, "read common config file %s", name.c_str()); - } else { - logError(lf_main, "error reading common config file %s: %s", name.c_str(), getResultCode(result)); - } - } - } - } - } - result = messages->readFromFile(best, opt.checkConfig, "", ident); - if (result != RESULT_OK) { - logError(lf_main, "error reading scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d: %s", best.c_str(), ident.c_str(), sw, hw, getResultCode(result)); - return result; - } - logNotice(lf_main, "read scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d", best.c_str(), ident.c_str(), sw, hw); - relativeFile = best.substr(strlen(opt.configPath)+1); - executeInstructions(messages, verbose); - return RESULT_OK; + // found the right file. load the templates if necessary, then load the file itself + bool readCommon = readTemplates(path, ".csv", hasTemplates, opt.checkConfig); + if (readCommon) { + result = collectConfigFiles(path, "", ".csv", files); + if (result == RESULT_OK && !files.empty()) { + for (vector::iterator it = files.begin(); it != files.end(); it++) { + string name = *it; + name = name.substr(path.length()+1, name.length()-path.length()-strlen(".csv")); // *. + if (name == "_templates.") { // skip templates + continue; + } + if (name.length() < 3 || name.find_first_of('.') != 2) { // different from the scheme "ZZ." + name = *it; + result = messages->readFromFile(name, opt.checkConfig); + if (result == RESULT_OK) { + logNotice(lf_main, "read common config file %s", name.c_str()); + } else { + logError(lf_main, "error reading common config file %s: %s", name.c_str(), getResultCode(result)); + } + } + } + } + } + result = messages->readFromFile(best, opt.checkConfig, "", ident); + if (result != RESULT_OK) { + logError(lf_main, "error reading scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d: %s", best.c_str(), ident.c_str(), sw, hw, getResultCode(result)); + return result; + } + logNotice(lf_main, "read scan config file %s for ID \"%s\", SW%4.4d, HW%4.4d", best.c_str(), ident.c_str(), sw, hw); + relativeFile = best.substr(strlen(opt.configPath)+1); + executeInstructions(messages, verbose); + return RESULT_OK; } } // namespace ebusd @@ -954,95 +954,95 @@ using namespace ebusd; * @param argv the command line arguments. */ int main(int argc, char* argv[]) { - struct argp aargp = { argpoptions, parse_opt, NULL, argpdoc, datahandler_getargs(), NULL, NULL }; - int arg_index = -1; - setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); - if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, &opt) != 0) { - logError(lf_main, "invalid arguments"); - return EINVAL; - } + struct argp aargp = { argpoptions, parse_opt, NULL, argpdoc, datahandler_getargs(), NULL, NULL }; + int arg_index = -1; + setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); + if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, &opt) != 0) { + logError(lf_main, "invalid arguments"); + return EINVAL; + } - s_messageMap = new MessageMap(opt.checkConfig && opt.scanConfig && arg_index >= argc); - if (opt.checkConfig) { - logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check..."); + s_messageMap = new MessageMap(opt.checkConfig && opt.scanConfig && arg_index >= argc); + if (opt.checkConfig) { + logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check..."); - result_t result = loadConfigFiles(s_messageMap, true, opt.scanConfig && arg_index < argc); + result_t result = loadConfigFiles(s_messageMap, true, opt.scanConfig && arg_index < argc); - while (result == RESULT_OK && opt.scanConfig && arg_index < argc) { - // check scan config for each passed ident message - string arg = argv[arg_index++]; - size_t pos = arg.find_first_of('/'); - if (pos == string::npos) { - logError(lf_main, "invalid scan message %s: missing \"/\"", arg.c_str()); - continue; - } - SymbolString master(false), slave(false); - result_t res = master.parseHex(arg.substr(0, pos)); - if (res == RESULT_OK) { - res = slave.parseHex(arg.substr(pos+1)); - } - if (res != RESULT_OK) { - logError(lf_main, "invalid scan message %s: %s", arg.c_str(), getResultCode(res)); - continue; - } - if (master.size() < 5) { // skip QQ ZZ PB SB NN - logError(lf_main, "invalid scan message %s: master part too short", arg.c_str()); - continue; - } - unsigned char address = master[1]; - Message* message = s_messageMap->getScanMessage(address); - if (!message) { - logError(lf_main, "invalid scan address %2.2x", address); - } else { - message->storeLastData(master, slave); - string file; - res = loadScanConfigFile(s_messageMap, address, slave, file, true); - if (res == RESULT_OK) { - logInfo(lf_main, "scan config %2.2x: file %s loaded", address, file.c_str()); - } - } - } - if (result == RESULT_OK && opt.checkConfig > 1) { - logNotice(lf_main, "configuration dump:"); - s_messageMap->dump(cout, true); - } - shutdown(); - return 0; - } + while (result == RESULT_OK && opt.scanConfig && arg_index < argc) { + // check scan config for each passed ident message + string arg = argv[arg_index++]; + size_t pos = arg.find_first_of('/'); + if (pos == string::npos) { + logError(lf_main, "invalid scan message %s: missing \"/\"", arg.c_str()); + continue; + } + SymbolString master(false), slave(false); + result_t res = master.parseHex(arg.substr(0, pos)); + if (res == RESULT_OK) { + res = slave.parseHex(arg.substr(pos+1)); + } + if (res != RESULT_OK) { + logError(lf_main, "invalid scan message %s: %s", arg.c_str(), getResultCode(res)); + continue; + } + if (master.size() < 5) { // skip QQ ZZ PB SB NN + logError(lf_main, "invalid scan message %s: master part too short", arg.c_str()); + continue; + } + unsigned char address = master[1]; + Message* message = s_messageMap->getScanMessage(address); + if (!message) { + logError(lf_main, "invalid scan address %2.2x", address); + } else { + message->storeLastData(master, slave); + string file; + res = loadScanConfigFile(s_messageMap, address, slave, file, true); + if (res == RESULT_OK) { + logInfo(lf_main, "scan config %2.2x: file %s loaded", address, file.c_str()); + } + } + } + if (result == RESULT_OK && opt.checkConfig > 1) { + logNotice(lf_main, "configuration dump:"); + s_messageMap->dump(cout, true); + } + shutdown(); + return 0; + } - // open the device - Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); - if (device == NULL) { - logError(lf_main, "unable to create device %s", opt.device); - return EINVAL; - } + // open the device + Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); + if (device == NULL) { + logError(lf_main, "unable to create device %s", opt.device); + return EINVAL; + } - if (!opt.foreground) { - setLogFile(opt.logFile); - daemonize(); // make me daemon - } + if (!opt.foreground) { + setLogFile(opt.logFile); + daemonize(); // make me daemon + } - // trap signals that we expect to receive - signal(SIGHUP, signalHandler); - signal(SIGINT, signalHandler); - signal(SIGTERM, signalHandler); + // trap signals that we expect to receive + signal(SIGHUP, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); - logNotice(lf_main, PACKAGE_STRING "." REVISION " started"); + logNotice(lf_main, PACKAGE_STRING "." REVISION " started"); - // create the MainLoop and start it - s_mainLoop = new MainLoop(opt, device, s_messageMap); - s_mainLoop->start("mainloop"); + // create the MainLoop and start it + s_mainLoop = new MainLoop(opt, device, s_messageMap); + s_mainLoop->start("mainloop"); - // load configuration files - loadConfigFiles(s_messageMap); - if (s_messageMap->sizeConditions() > 0 && opt.pollInterval == 0) { - logError(lf_main, "conditions require a poll interval > 0"); - } - // wait for end of MainLoop - s_mainLoop->join(); + // load configuration files + loadConfigFiles(s_messageMap); + if (s_messageMap->sizeConditions() > 0 && opt.pollInterval == 0) { + logError(lf_main, "conditions require a poll interval > 0"); + } + // wait for end of MainLoop + s_mainLoop->join(); - // shutdown - shutdown(); - return 0; + // shutdown + shutdown(); + return 0; } diff --git a/src/ebusd/main.h b/src/ebusd/main.h index 3ca3096f..a7e32a09 100644 --- a/src/ebusd/main.h +++ b/src/ebusd/main.h @@ -31,44 +31,44 @@ namespace ebusd { /** A structure holding all program options. */ struct options { - const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0] - bool noDeviceCheck; //!< skip serial eBUS device test - bool readOnly; //!< read-only access to the device - bool initialSend; //!< send an initial escape symbol after connecting device - int latency; //!< transfer latency in us [0 for USB, 10000 for IP] + const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0] + bool noDeviceCheck; //!< skip serial eBUS device test + bool readOnly; //!< read-only access to the device + bool initialSend; //!< send an initial escape symbol after connecting device + int latency; //!< transfer latency in us [0 for USB, 10000 for IP] - const char* configPath; //!< path to CSV configuration files [/etc/ebusd] - bool scanConfig; //!< pick configuration files matching initial scan - unsigned char initialScan; //!< the initial address to scan for scanconfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address) - int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop - int pollInterval; //!< poll interval in seconds, 0 to disable [5] + const char* configPath; //!< path to CSV configuration files [/etc/ebusd] + bool scanConfig; //!< pick configuration files matching initial scan + unsigned char initialScan; //!< the initial address to scan for scanconfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address) + int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop + int pollInterval; //!< poll interval in seconds, 0 to disable [5] - unsigned char address; //!< own bus address [31] - bool answer; //!< answer to requests from other masters - int acquireTimeout; //!< bus acquisition timeout in us [9400] - int acquireRetries; //!< number of retries for bus acquisition [3] - int sendRetries; //!< number of retries for failed sends [2] - int receiveTimeout; //!< timeout for receiving answer from slave in us [25000] - int masterCount; //!< expected number of masters for arbitration [0] - bool generateSyn; //!< enable AUTO-SYN symbol generation + unsigned char address; //!< own bus address [31] + bool answer; //!< answer to requests from other masters + int acquireTimeout; //!< bus acquisition timeout in us [9400] + int acquireRetries; //!< number of retries for bus acquisition [3] + int sendRetries; //!< number of retries for failed sends [2] + int receiveTimeout; //!< timeout for receiving answer from slave in us [25000] + int masterCount; //!< expected number of masters for arbitration [0] + bool generateSyn; //!< enable AUTO-SYN symbol generation - bool foreground; //!< run in foreground - bool enableHex; //!< enable hex command - const char* pidFile; //!< PID file name [/var/run/ebusd.pid] - uint16_t port; //!< port to listen for command line connections [8888] - bool localOnly; //!< listen on 127.0.0.1 interface only - uint16_t httpPort; //!< optional port to listen for HTTP connections, 0 to disable [0] - const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html] + bool foreground; //!< run in foreground + bool enableHex; //!< enable hex command + const char* pidFile; //!< PID file name [/var/run/ebusd.pid] + uint16_t port; //!< port to listen for command line connections [8888] + bool localOnly; //!< listen on 127.0.0.1 interface only + uint16_t httpPort; //!< optional port to listen for HTTP connections, 0 to disable [0] + const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html] - const char* logFile; //!< log file name [/var/log/ebusd.log] + const char* logFile; //!< log file name [/var/log/ebusd.log] - bool logRaw; //!< raw log each received/sent byte on the bus - const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log] - unsigned int logRawSize; //!< maximum size of raw log file in kB [100] + bool logRaw; //!< raw log each received/sent byte on the bus + const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log] + unsigned int logRawSize; //!< maximum size of raw log file in kB [100] - bool dump; //!< binary dump received bytes - const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin] - unsigned int dumpSize; //!< maximum size of dump file in kB [100] + bool dump; //!< binary dump received bytes + const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin] + unsigned int dumpSize; //!< maximum size of dump file in kB [100] }; /** diff --git a/src/ebusd/mainloop.cpp b/src/ebusd/mainloop.cpp index 240a1343..ee64bd7c 100644 --- a/src/ebusd/mainloop.cpp +++ b/src/ebusd/mainloop.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "mainloop.h" @@ -39,28 +39,28 @@ using std::setw; /** the known column names (pairs of full length name and short length name). */ static const char* columnNames[] = { - "type", "t", - "circuit", "c", - "name", "n", - "comment", "co", - "qq", "q", - "zz", "z", - "pbsb", "p", - "id", "i", - "fields", "f", + "type", "t", + "circuit", "c", + "name", "n", + "comment", "co", + "qq", "q", + "zz", "z", + "pbsb", "p", + "id", "i", + "fields", "f", }; /** the known column IDs according to @a columnNames. */ static const size_t columnIds[] = { - COLUMN_TYPE, COLUMN_TYPE, - COLUMN_CIRCUIT, COLUMN_CIRCUIT, - COLUMN_NAME, COLUMN_NAME, - COLUMN_COMMENT, COLUMN_COMMENT, - COLUMN_QQ, COLUMN_QQ, - COLUMN_ZZ, COLUMN_ZZ, - COLUMN_PBSB, COLUMN_PBSB, - COLUMN_ID, COLUMN_ID, - COLUMN_FIELDS, COLUMN_FIELDS, + COLUMN_TYPE, COLUMN_TYPE, + COLUMN_CIRCUIT, COLUMN_CIRCUIT, + COLUMN_NAME, COLUMN_NAME, + COLUMN_COMMENT, COLUMN_COMMENT, + COLUMN_QQ, COLUMN_QQ, + COLUMN_ZZ, COLUMN_ZZ, + COLUMN_PBSB, COLUMN_PBSB, + COLUMN_ID, COLUMN_ID, + COLUMN_FIELDS, COLUMN_FIELDS, }; /** the number of known column names. */ @@ -68,1478 +68,1478 @@ static const size_t columnCount = sizeof(columnNames) / sizeof(char*); MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* messages) - : Thread(), m_device(device), m_reconnectCount(0), m_messages(messages), - m_address(opt.address), m_scanConfig(opt.scanConfig), - m_initialScan(opt.initialScan), m_enableHex(opt.enableHex) { - // open Device - result_t result = m_device->open(); - if (result != RESULT_OK) { - logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); - } else if (!m_device->isValid()) { - logError(lf_bus, "device %s not available", m_device->getName()); - } - m_device->setListener(this); - if (opt.dumpFile[0]) { - m_dumpFile = new RotateFile(opt.dumpFile, opt.dumpSize); - } else { - m_dumpFile = NULL; - } - if (opt.logRawFile[0] && strcmp(opt.logRawFile, opt.logFile) != 0) { - m_logRawFile = new RotateFile(opt.logRawFile, opt.logRawSize, true); - } else { - m_logRawFile = NULL; - } - m_logRawEnabled = opt.logRaw; - // create BusHandler - unsigned int latency; - if (opt.latency < 0) { - latency = device->getLatency(); - } else { - latency = (unsigned int)opt.latency; - } - m_busHandler = new BusHandler(m_device, m_messages, - m_address, opt.answer, - opt.acquireRetries, opt.sendRetries, - latency, opt.acquireTimeout, opt.receiveTimeout, - opt.masterCount, opt.generateSyn, - opt.pollInterval); - m_busHandler->start("bushandler"); + : Thread(), m_device(device), m_reconnectCount(0), m_messages(messages), + m_address(opt.address), m_scanConfig(opt.scanConfig), + m_initialScan(opt.initialScan), m_enableHex(opt.enableHex) { + // open Device + result_t result = m_device->open(); + if (result != RESULT_OK) { + logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result)); + } else if (!m_device->isValid()) { + logError(lf_bus, "device %s not available", m_device->getName()); + } + m_device->setListener(this); + if (opt.dumpFile[0]) { + m_dumpFile = new RotateFile(opt.dumpFile, opt.dumpSize); + } else { + m_dumpFile = NULL; + } + if (opt.logRawFile[0] && strcmp(opt.logRawFile, opt.logFile) != 0) { + m_logRawFile = new RotateFile(opt.logRawFile, opt.logRawSize, true); + } else { + m_logRawFile = NULL; + } + m_logRawEnabled = opt.logRaw; + // create BusHandler + unsigned int latency; + if (opt.latency < 0) { + latency = device->getLatency(); + } else { + latency = (unsigned int)opt.latency; + } + m_busHandler = new BusHandler(m_device, m_messages, + m_address, opt.answer, + opt.acquireRetries, opt.sendRetries, + latency, opt.acquireTimeout, opt.receiveTimeout, + opt.masterCount, opt.generateSyn, + opt.pollInterval); + m_busHandler->start("bushandler"); - // create network - m_htmlPath = opt.htmlPath; - m_network = new Network(opt.localOnly, opt.port, opt.httpPort, &m_netQueue); - m_network->start("network"); - if (!datahandler_register(m_busHandler, messages, m_dataHandlers)) { - logError(lf_main, "error registering data handlers"); - } + // create network + m_htmlPath = opt.htmlPath; + m_network = new Network(opt.localOnly, opt.port, opt.httpPort, &m_netQueue); + m_network->start("network"); + if (!datahandler_register(m_busHandler, messages, m_dataHandlers)) { + logError(lf_main, "error registering data handlers"); + } } MainLoop::~MainLoop() { - join(); + join(); - for (list::iterator it = m_dataHandlers.begin(); it != m_dataHandlers.end(); it++) { - delete *it; - } - if (m_dumpFile) { - delete m_dumpFile; - m_dumpFile = NULL; - } - if (m_logRawFile) { - delete m_logRawFile; - m_logRawFile = NULL; - } - if (m_network != NULL) { - delete m_network; - m_network = NULL; - } - if (m_busHandler != NULL) { - delete m_busHandler; - m_busHandler = NULL; - } - if (m_device != NULL) { - delete m_device; - m_device = NULL; - } - NetMessage* msg; - while ((msg = m_netQueue.pop()) != NULL) { - delete msg; - } + for (list::iterator it = m_dataHandlers.begin(); it != m_dataHandlers.end(); it++) { + delete *it; + } + if (m_dumpFile) { + delete m_dumpFile; + m_dumpFile = NULL; + } + if (m_logRawFile) { + delete m_logRawFile; + m_logRawFile = NULL; + } + if (m_network != NULL) { + delete m_network; + m_network = NULL; + } + if (m_busHandler != NULL) { + delete m_busHandler; + m_busHandler = NULL; + } + if (m_device != NULL) { + delete m_device; + m_device = NULL; + } + NetMessage* msg; + while ((msg = m_netQueue.pop()) != NULL) { + delete msg; + } } void MainLoop::run() { - bool reload = true; - time_t lastTaskRun, now, lastSignal = 0, since, sinkSince = 1; - int taskDelay = 5; - unsigned char lastScanAddress = 0; // 0 is known to be a master - time(&now); - lastTaskRun = now; - ostringstream updates; - list dataSinks; - deque messages; + bool reload = true; + time_t lastTaskRun, now, lastSignal = 0, since, sinkSince = 1; + int taskDelay = 5; + unsigned char lastScanAddress = 0; // 0 is known to be a master + time(&now); + lastTaskRun = now; + ostringstream updates; + list dataSinks; + deque messages; - for (list::iterator it = m_dataHandlers.begin(); it != m_dataHandlers.end(); it++) { - if ((*it)->isDataSink()) { - dataSinks.push_back(dynamic_cast(*it)); - } - (*it)->start(); - } - while (true) { - string result; - // pick the next message to handle - NetMessage* netMessage = m_netQueue.pop(taskDelay); - time(&now); - if (now < lastTaskRun) { - // clock skew - if (now < lastSignal) { - lastSignal -= lastTaskRun-now; - } - lastTaskRun = now; - } else if (now > lastTaskRun+taskDelay) { - logDebug(lf_main, "performing regular tasks"); - if (m_busHandler->hasSignal()) { - lastSignal = now; - } else if (lastSignal && now > lastSignal+RECONNECT_MISSING_SIGNAL) { - lastSignal = 0; - m_busHandler->reconnect(); - m_reconnectCount++; - } - if (m_scanConfig) { - if (m_initialScan != ESC && reload && m_busHandler->hasSignal()) { - result_t result = RESULT_ERR_NO_SIGNAL; - if (m_initialScan == SYN) { - logNotice(lf_main, "initiating full scan"); - result = m_busHandler->startScan(true); - } else { - logNotice(lf_main, "starting initial scan for %2.2x", m_initialScan); - SymbolString slave(false); - result = m_busHandler->scanAndWait(m_initialScan, slave); - Message* message = m_messages->getScanMessage(m_initialScan); - if (result == RESULT_OK && message != NULL) { - ostringstream ret; - result = message->decodeLastData(ret, 0, true); // decode data - if (result == RESULT_OK) { - logNotice(lf_main, "initial scan result: %2.2x%s", m_initialScan, ret.str().c_str()); - } - } - } - if (result != RESULT_OK) { - logError(lf_main, "initial scan failed: %s", getResultCode(result)); - } - if (result != RESULT_ERR_NO_SIGNAL) { - reload = false; - } - } - bool scanned = false; - lastScanAddress = m_busHandler->getNextScanAddress(lastScanAddress, scanned); - if (lastScanAddress == SYN) { - taskDelay = 5; - lastScanAddress = 0; - } else { - SymbolString slave(false); - if (scanned) { - Message* message = m_messages->getScanMessage(lastScanAddress); - slave = message->getLastSlaveData(); - scanned = message->getLastUpdateTime() > 0; - } else { - result_t result = m_busHandler->scanAndWait(lastScanAddress, slave); - taskDelay = (result == RESULT_ERR_NO_SIGNAL) ? 10 : 1; - if (result != RESULT_OK) { - logError(lf_main, "scan config %2.2x message: %s", lastScanAddress, getResultCode(result)); - } else { - scanned = true; - logInfo(lf_main, "scan config %2.2x message received", lastScanAddress); - } - } - if (scanned) { - string file; - result_t result = loadScanConfigFile(m_messages, lastScanAddress, slave, file); - if (result == RESULT_OK) { - logNotice(lf_main, "scan config %2.2x: file %s loaded", lastScanAddress, file.c_str()); - m_busHandler->setScanConfigLoaded(lastScanAddress, file); - } else { - m_busHandler->setScanConfigLoaded(lastScanAddress, ""); - } - } - } - } - time(&lastTaskRun); - } - time(&now); - if (!dataSinks.empty()) { - messages = m_messages->findAll("", "", false, true, true, true, false, true, sinkSince, now); - for (deque::iterator it = messages.begin(); it != messages.end(); it++) { - Message* message = *it; - for (list::iterator it = dataSinks.begin(); it != dataSinks.end(); it++) { - (*it)->notifyUpdate(message); - } - } - sinkSince = now; - } - if (netMessage == NULL) { - continue; - } - string request = netMessage->getRequest(); - bool listening = netMessage->isListening(&since); - if (!listening) { - since = now; - } - bool connected = true; - if (request.length() > 0) { - logDebug(lf_main, ">>> %s", request.c_str()); - result = decodeMessage(request, netMessage->isHttp(), connected, listening, reload); + for (list::iterator it = m_dataHandlers.begin(); it != m_dataHandlers.end(); it++) { + if ((*it)->isDataSink()) { + dataSinks.push_back(dynamic_cast(*it)); + } + (*it)->start(); + } + while (true) { + string result; + // pick the next message to handle + NetMessage* netMessage = m_netQueue.pop(taskDelay); + time(&now); + if (now < lastTaskRun) { + // clock skew + if (now < lastSignal) { + lastSignal -= lastTaskRun-now; + } + lastTaskRun = now; + } else if (now > lastTaskRun+taskDelay) { + logDebug(lf_main, "performing regular tasks"); + if (m_busHandler->hasSignal()) { + lastSignal = now; + } else if (lastSignal && now > lastSignal+RECONNECT_MISSING_SIGNAL) { + lastSignal = 0; + m_busHandler->reconnect(); + m_reconnectCount++; + } + if (m_scanConfig) { + if (m_initialScan != ESC && reload && m_busHandler->hasSignal()) { + result_t result = RESULT_ERR_NO_SIGNAL; + if (m_initialScan == SYN) { + logNotice(lf_main, "initiating full scan"); + result = m_busHandler->startScan(true); + } else { + logNotice(lf_main, "starting initial scan for %2.2x", m_initialScan); + SymbolString slave(false); + result = m_busHandler->scanAndWait(m_initialScan, slave); + Message* message = m_messages->getScanMessage(m_initialScan); + if (result == RESULT_OK && message != NULL) { + ostringstream ret; + result = message->decodeLastData(ret, 0, true); // decode data + if (result == RESULT_OK) { + logNotice(lf_main, "initial scan result: %2.2x%s", m_initialScan, ret.str().c_str()); + } + } + } + if (result != RESULT_OK) { + logError(lf_main, "initial scan failed: %s", getResultCode(result)); + } + if (result != RESULT_ERR_NO_SIGNAL) { + reload = false; + } + } + bool scanned = false; + lastScanAddress = m_busHandler->getNextScanAddress(lastScanAddress, scanned); + if (lastScanAddress == SYN) { + taskDelay = 5; + lastScanAddress = 0; + } else { + SymbolString slave(false); + if (scanned) { + Message* message = m_messages->getScanMessage(lastScanAddress); + slave = message->getLastSlaveData(); + scanned = message->getLastUpdateTime() > 0; + } else { + result_t result = m_busHandler->scanAndWait(lastScanAddress, slave); + taskDelay = (result == RESULT_ERR_NO_SIGNAL) ? 10 : 1; + if (result != RESULT_OK) { + logError(lf_main, "scan config %2.2x message: %s", lastScanAddress, getResultCode(result)); + } else { + scanned = true; + logInfo(lf_main, "scan config %2.2x message received", lastScanAddress); + } + } + if (scanned) { + string file; + result_t result = loadScanConfigFile(m_messages, lastScanAddress, slave, file); + if (result == RESULT_OK) { + logNotice(lf_main, "scan config %2.2x: file %s loaded", lastScanAddress, file.c_str()); + m_busHandler->setScanConfigLoaded(lastScanAddress, file); + } else { + m_busHandler->setScanConfigLoaded(lastScanAddress, ""); + } + } + } + } + time(&lastTaskRun); + } + time(&now); + if (!dataSinks.empty()) { + messages = m_messages->findAll("", "", false, true, true, true, false, true, sinkSince, now); + for (deque::iterator it = messages.begin(); it != messages.end(); it++) { + Message* message = *it; + for (list::iterator it = dataSinks.begin(); it != dataSinks.end(); it++) { + (*it)->notifyUpdate(message); + } + } + sinkSince = now; + } + if (netMessage == NULL) { + continue; + } + string request = netMessage->getRequest(); + bool listening = netMessage->isListening(&since); + if (!listening) { + since = now; + } + bool connected = true; + if (request.length() > 0) { + logDebug(lf_main, ">>> %s", request.c_str()); + result = decodeMessage(request, netMessage->isHttp(), connected, listening, reload); - if (result.length() == 0 && !netMessage->isHttp()) { - result = getResultCode(RESULT_EMPTY); - } - if (result.length() > 100) { - logDebug(lf_main, "<<< %s ...", result.substr(0, 100).c_str()); - } else { - logDebug(lf_main, "<<< %s", result.c_str()); - } - if (result.length() == 0) { - result = "\n"; // only for HTTP - } else if (!netMessage->isHttp()) { - result += "\n\n"; - } - } - if (listening) { - messages = m_messages->findAll("", "", false, true, true, true, false, true, since, now); - updates.str(""); - updates.clear(); - for (deque::iterator it = messages.begin(); it != messages.end(); it++) { - Message* message = *it; - updates << message->getCircuit() << " " << message->getName() << " = " << dec; - message->decodeLastData(updates); - updates << endl; - } - result += updates.str(); - } - // send result to client - netMessage->setResult(result, listening, now, !connected); - } + if (result.length() == 0 && !netMessage->isHttp()) { + result = getResultCode(RESULT_EMPTY); + } + if (result.length() > 100) { + logDebug(lf_main, "<<< %s ...", result.substr(0, 100).c_str()); + } else { + logDebug(lf_main, "<<< %s", result.c_str()); + } + if (result.length() == 0) { + result = "\n"; // only for HTTP + } else if (!netMessage->isHttp()) { + result += "\n\n"; + } + } + if (listening) { + messages = m_messages->findAll("", "", false, true, true, true, false, true, since, now); + updates.str(""); + updates.clear(); + for (deque::iterator it = messages.begin(); it != messages.end(); it++) { + Message* message = *it; + updates << message->getCircuit() << " " << message->getName() << " = " << dec; + message->decodeLastData(updates); + updates << endl; + } + result += updates.str(); + } + // send result to client + netMessage->setResult(result, listening, now, !connected); + } } void MainLoop::notifyDeviceData(const unsigned char byte, bool received) { - if (received && m_dumpFile) { - m_dumpFile->write((unsigned char*)&byte, 1); - } - if (m_logRawFile) { - m_logRawFile->write((unsigned char*)&byte, 1, received); - } else if (m_logRawEnabled) { - if (received) { - logNotice(lf_bus, "<%02x", byte); - } else { - logNotice(lf_bus, ">%02x", byte); - } - } + if (received && m_dumpFile) { + m_dumpFile->write((unsigned char*)&byte, 1); + } + if (m_logRawFile) { + m_logRawFile->write((unsigned char*)&byte, 1, received); + } else if (m_logRawEnabled) { + if (received) { + logNotice(lf_bus, "<%02x", byte); + } else { + logNotice(lf_bus, ">%02x", byte); + } + } } string MainLoop::decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload) { - // prepare data - string token, previous; - istringstream stream(data); - vector args; - bool escaped = false; + // prepare data + string token, previous; + istringstream stream(data); + vector args; + bool escaped = false; - char delim = ' '; - while (getline(stream, token, delim)) { - if (!isHttp) { - if (escaped) { - args.pop_back(); - if (token.length() > 0 && token[token.length()-1] == '"') { - token.erase(token.length() - 1, 1); - escaped = false; - } - token = previous + " " + token; - } else if (token.length() == 0) { // allow multiple space chars for a single delimiter - continue; - } else if (token[0] == '"') { - token.erase(0, 1); - if (token.length() > 0 && token[token.length()-1] == '"') { - token.erase(token.length() - 1, 1); - } else { - escaped = true; - } - } - } - args.push_back(token); - previous = token; - if (isHttp) { - delim = (args.size() == 1) ? '?' : '\n'; - } - } + char delim = ' '; + while (getline(stream, token, delim)) { + if (!isHttp) { + if (escaped) { + args.pop_back(); + if (token.length() > 0 && token[token.length()-1] == '"') { + token.erase(token.length() - 1, 1); + escaped = false; + } + token = previous + " " + token; + } else if (token.length() == 0) { // allow multiple space chars for a single delimiter + continue; + } else if (token[0] == '"') { + token.erase(0, 1); + if (token.length() > 0 && token[token.length()-1] == '"') { + token.erase(token.length() - 1, 1); + } else { + escaped = true; + } + } + } + args.push_back(token); + previous = token; + if (isHttp) { + delim = (args.size() == 1) ? '?' : '\n'; + } + } - if (isHttp) { - const char* str = args.size() > 0 ? args[0].c_str() : ""; - if (strcmp(str, "GET") == 0) { - return executeGet(args, connected); - } - connected = false; - return "HTTP/1.0 405 Method Not Allowed\r\n\r\n"; - } + if (isHttp) { + const char* str = args.size() > 0 ? args[0].c_str() : ""; + if (strcmp(str, "GET") == 0) { + return executeGet(args, connected); + } + connected = false; + return "HTTP/1.0 405 Method Not Allowed\r\n\r\n"; + } - if (args.size() == 0) { - return executeHelp(); - } - const char* str = args[0].c_str(); - if (args.size() == 2) { - // check for "CMD -h" - if (strcasecmp(args[1].c_str(), "-h") == 0 || strcasecmp(args[1].c_str(), "-?") == 0 || strcasecmp(args[1].c_str(), "--help") == 0) { - args.clear(); // empty args is used as command help indicator - } else if (strcasecmp(args[0].c_str(), "H") == 0 || strcasecmp(args[0].c_str(), "HELP") == 0) { // check for "HELP CMD" - str = args[1].c_str(); - args.clear(); // empty args is used as command help indicator - } - } - if (strcasecmp(str, "R") == 0 || strcasecmp(str, "READ") == 0) { - return executeRead(args); - } - if (strcasecmp(str, "W") == 0 || strcasecmp(str, "WRITE") == 0) { - return executeWrite(args); - } - if (strcasecmp(str, "HEX") == 0) { - if (m_enableHex) { - return executeHex(args); - } - return "ERR: command not enabled"; - } - if (strcasecmp(str, "F") == 0 || strcasecmp(str, "FIND") == 0) { - return executeFind(args); - } - if (strcasecmp(str, "L") == 0 || strcasecmp(str, "LISTEN") == 0) { - return executeListen(args, listening); - } - if (strcasecmp(str, "S") == 0 || strcasecmp(str, "STATE") == 0) { - return executeState(args); - } - if (strcasecmp(str, "G") == 0 || strcasecmp(str, "GRAB") == 0) { - return executeGrab(args); - } - if (strcasecmp(str, "SCAN") == 0) { - return executeScan(args); - } - if (strcasecmp(str, "LOG") == 0) { - return executeLog(args); - } - if (strcasecmp(str, "RAW") == 0) { - return executeRaw(args); - } - if (strcasecmp(str, "DUMP") == 0) { - return executeDump(args); - } - if (strcasecmp(str, "RELOAD") == 0) { - reload = true; - return executeReload(args); - } - if (strcasecmp(str, "Q") == 0 || strcasecmp(str, "QUIT") == 0) { - return executeQuit(args, connected); - } - if (strcasecmp(str, "I") == 0 || strcasecmp(str, "INFO") == 0) { - return executeInfo(args); - } - if (strcasecmp(str, "H") == 0 || strcasecmp(str, "HELP") == 0) { - return executeHelp(); - } - return "ERR: command not found"; + if (args.size() == 0) { + return executeHelp(); + } + const char* str = args[0].c_str(); + if (args.size() == 2) { + // check for "CMD -h" + if (strcasecmp(args[1].c_str(), "-h") == 0 || strcasecmp(args[1].c_str(), "-?") == 0 || strcasecmp(args[1].c_str(), "--help") == 0) { + args.clear(); // empty args is used as command help indicator + } else if (strcasecmp(args[0].c_str(), "H") == 0 || strcasecmp(args[0].c_str(), "HELP") == 0) { // check for "HELP CMD" + str = args[1].c_str(); + args.clear(); // empty args is used as command help indicator + } + } + if (strcasecmp(str, "R") == 0 || strcasecmp(str, "READ") == 0) { + return executeRead(args); + } + if (strcasecmp(str, "W") == 0 || strcasecmp(str, "WRITE") == 0) { + return executeWrite(args); + } + if (strcasecmp(str, "HEX") == 0) { + if (m_enableHex) { + return executeHex(args); + } + return "ERR: command not enabled"; + } + if (strcasecmp(str, "F") == 0 || strcasecmp(str, "FIND") == 0) { + return executeFind(args); + } + if (strcasecmp(str, "L") == 0 || strcasecmp(str, "LISTEN") == 0) { + return executeListen(args, listening); + } + if (strcasecmp(str, "S") == 0 || strcasecmp(str, "STATE") == 0) { + return executeState(args); + } + if (strcasecmp(str, "G") == 0 || strcasecmp(str, "GRAB") == 0) { + return executeGrab(args); + } + if (strcasecmp(str, "SCAN") == 0) { + return executeScan(args); + } + if (strcasecmp(str, "LOG") == 0) { + return executeLog(args); + } + if (strcasecmp(str, "RAW") == 0) { + return executeRaw(args); + } + if (strcasecmp(str, "DUMP") == 0) { + return executeDump(args); + } + if (strcasecmp(str, "RELOAD") == 0) { + reload = true; + return executeReload(args); + } + if (strcasecmp(str, "Q") == 0 || strcasecmp(str, "QUIT") == 0) { + return executeQuit(args, connected); + } + if (strcasecmp(str, "I") == 0 || strcasecmp(str, "INFO") == 0) { + return executeInfo(args); + } + if (strcasecmp(str, "H") == 0 || strcasecmp(str, "HELP") == 0) { + return executeHelp(); + } + return "ERR: command not found"; } result_t MainLoop::parseHexMaster(vector &args, size_t argPos, SymbolString& master) { - ostringstream msg; - while (argPos < args.size()) { - if ((args[argPos].length() % 2) != 0) { - return RESULT_ERR_INVALID_NUM; - } - msg << args[argPos++]; - } - if (msg.str().size() < 4*2) { // at least ZZ, PB, SB, NN - return RESULT_ERR_INVALID_ARG; - } - result_t ret; - unsigned int length = parseInt(msg.str().substr(3*2, 2).c_str(), 16, 0, MAX_POS, ret); - if (ret == RESULT_OK && (4+length)*2 != msg.str().size()) { - return RESULT_ERR_INVALID_ARG; - } - ret = master.push_back(m_address, false); - if (ret == RESULT_OK) { - ret = master.parseHex(msg.str()); - } - if (ret == RESULT_OK && !isValidAddress(master[1])) { - ret = RESULT_ERR_INVALID_ADDR; - } - return ret; + ostringstream msg; + while (argPos < args.size()) { + if ((args[argPos].length() % 2) != 0) { + return RESULT_ERR_INVALID_NUM; + } + msg << args[argPos++]; + } + if (msg.str().size() < 4*2) { // at least ZZ, PB, SB, NN + return RESULT_ERR_INVALID_ARG; + } + result_t ret; + unsigned int length = parseInt(msg.str().substr(3*2, 2).c_str(), 16, 0, MAX_POS, ret); + if (ret == RESULT_OK && (4+length)*2 != msg.str().size()) { + return RESULT_ERR_INVALID_ARG; + } + ret = master.push_back(m_address, false); + if (ret == RESULT_OK) { + ret = master.parseHex(msg.str()); + } + if (ret == RESULT_OK && !isValidAddress(master[1])) { + ret = RESULT_ERR_INVALID_ADDR; + } + return ret; } string MainLoop::executeRead(vector &args) { - size_t argPos = 1; - bool hex = false, numeric = false; - OutputFormat verbosity = 0; - time_t maxAge = 5*60; - string circuit, params; - unsigned char dstAddress = SYN, pollPriority = 0; - while (args.size() > argPos && args[argPos][0] == '-') { - if (args[argPos] == "-h") { - hex = true; - } else if (args[argPos] == "-f") { - maxAge = 0; - } else if (args[argPos] == "-v") { - switch (verbosity) { - case 0: - verbosity = OF_NAMES; - break; - case OF_NAMES: - verbosity |= OF_UNITS; - break; - case OF_NAMES|OF_UNITS: - verbosity |= OF_COMMENTS; - break; - } - } else if (args[argPos] == "-vv") { - verbosity |= OF_NAMES|OF_UNITS; - } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { - verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; - } else if (args[argPos] == "-n") { - numeric = true; - } else if (args[argPos] == "-m") { - argPos++; - if (args.size() > argPos) { - result_t result; - maxAge = parseInt(args[argPos].c_str(), 10, 0, 24*60*60, result); - if (result != RESULT_OK) { - argPos = 0; // print usage - break; - } - } else { - argPos = 0; // print usage - break; - } - } else if (args[argPos] == "-c") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - circuit = args[argPos]; - } else if (args[argPos] == "-d") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - result_t ret; - dstAddress = (unsigned char)parseInt(args[argPos].c_str(), 16, 0, 0xff, ret); - if (ret != RESULT_OK || !isValidAddress(dstAddress) || isMaster(dstAddress)) { - return getResultCode(RESULT_ERR_INVALID_ADDR); - } - } else if (args[argPos] == "-p") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - result_t ret; - pollPriority = (unsigned char)parseInt(args[argPos].c_str(), 10, 1, 9, ret); - if (ret != RESULT_OK) { - return getResultCode(RESULT_ERR_INVALID_NUM); - } - } else if (args[argPos] == "-i") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - params = args[argPos]; - } else { - argPos = 0; // print usage - break; - } - argPos++; - } - if (hex && (dstAddress != SYN || !circuit.empty() || verbosity != 0 || numeric || pollPriority > 0 || args.size() < argPos + 1)) { - argPos = 0; // print usage - } + size_t argPos = 1; + bool hex = false, numeric = false; + OutputFormat verbosity = 0; + time_t maxAge = 5*60; + string circuit, params; + unsigned char dstAddress = SYN, pollPriority = 0; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-h") { + hex = true; + } else if (args[argPos] == "-f") { + maxAge = 0; + } else if (args[argPos] == "-v") { + switch (verbosity) { + case 0: + verbosity = OF_NAMES; + break; + case OF_NAMES: + verbosity |= OF_UNITS; + break; + case OF_NAMES|OF_UNITS: + verbosity |= OF_COMMENTS; + break; + } + } else if (args[argPos] == "-vv") { + verbosity |= OF_NAMES|OF_UNITS; + } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { + verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; + } else if (args[argPos] == "-n") { + numeric = true; + } else if (args[argPos] == "-m") { + argPos++; + if (args.size() > argPos) { + result_t result; + maxAge = parseInt(args[argPos].c_str(), 10, 0, 24*60*60, result); + if (result != RESULT_OK) { + argPos = 0; // print usage + break; + } + } else { + argPos = 0; // print usage + break; + } + } else if (args[argPos] == "-c") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + circuit = args[argPos]; + } else if (args[argPos] == "-d") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + result_t ret; + dstAddress = (unsigned char)parseInt(args[argPos].c_str(), 16, 0, 0xff, ret); + if (ret != RESULT_OK || !isValidAddress(dstAddress) || isMaster(dstAddress)) { + return getResultCode(RESULT_ERR_INVALID_ADDR); + } + } else if (args[argPos] == "-p") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + result_t ret; + pollPriority = (unsigned char)parseInt(args[argPos].c_str(), 10, 1, 9, ret); + if (ret != RESULT_OK) { + return getResultCode(RESULT_ERR_INVALID_NUM); + } + } else if (args[argPos] == "-i") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + params = args[argPos]; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } + if (hex && (dstAddress != SYN || !circuit.empty() || verbosity != 0 || numeric || pollPriority > 0 || args.size() < argPos + 1)) { + argPos = 0; // print usage + } - time_t now; - time(&now); + time_t now; + time(&now); - if (hex && argPos > 0) { - SymbolString cacheMaster(false); - result_t ret = parseHexMaster(args, argPos, cacheMaster); - if (ret != RESULT_OK) { - return getResultCode(ret); - } - if (cacheMaster[1] == BROADCAST || isMaster(cacheMaster[1])) { - return getResultCode(RESULT_ERR_INVALID_ARG); - } - logNotice(lf_main, "read hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); + if (hex && argPos > 0) { + SymbolString cacheMaster(false); + result_t ret = parseHexMaster(args, argPos, cacheMaster); + if (ret != RESULT_OK) { + return getResultCode(ret); + } + if (cacheMaster[1] == BROADCAST || isMaster(cacheMaster[1])) { + return getResultCode(RESULT_ERR_INVALID_ARG); + } + logNotice(lf_main, "read hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); - // find message - Message* message = m_messages->find(cacheMaster, false, true, false, false); + // find message + Message* message = m_messages->find(cacheMaster, false, true, false, false); - if (message == NULL) { - return getResultCode(RESULT_ERR_NOTFOUND); - } - if (message->isWrite()) { - return getResultCode(RESULT_ERR_INVALID_ARG); - } - if (circuit.length() > 0 && circuit != message->getCircuit()) { - return getResultCode(RESULT_ERR_INVALID_ARG); // non-matching circuit - } - if (message->getLastUpdateTime() + maxAge > now || (message->isPassive() && message->getLastUpdateTime() != 0)) { - SymbolString& slave = message->getLastSlaveData(); - logNotice(lf_main, "hex read %s %s from cache", message->getCircuit().c_str(), message->getName().c_str()); - return slave.getDataStr(true, false); - } + if (message == NULL) { + return getResultCode(RESULT_ERR_NOTFOUND); + } + if (message->isWrite()) { + return getResultCode(RESULT_ERR_INVALID_ARG); + } + if (circuit.length() > 0 && circuit != message->getCircuit()) { + return getResultCode(RESULT_ERR_INVALID_ARG); // non-matching circuit + } + if (message->getLastUpdateTime() + maxAge > now || (message->isPassive() && message->getLastUpdateTime() != 0)) { + SymbolString& slave = message->getLastSlaveData(); + logNotice(lf_main, "hex read %s %s from cache", message->getCircuit().c_str(), message->getName().c_str()); + return slave.getDataStr(true, false); + } - // send message - SymbolString master(true); - master.addAll(cacheMaster); - SymbolString slave(false); - ret = m_busHandler->sendAndWait(master, slave); + // send message + SymbolString master(true); + master.addAll(cacheMaster); + SymbolString slave(false); + ret = m_busHandler->sendAndWait(master, slave); - if (ret == RESULT_OK) { - ret = message->storeLastData(cacheMaster, slave); - ostringstream result; - if (ret == RESULT_OK) { - ret = message->decodeLastData(result); - } - if (ret >= RESULT_OK) { - logInfo(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); - } else { - logError(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - } - return slave.getDataStr(true, false); - } - logError(lf_main, "read hex %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - return getResultCode(ret); - } - if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) { - return "usage: read [-f] [-m SECONDS] [-c CIRCUIT] [-d ZZ] [-p PRIO] [-v|-V] [-n] [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n" - " or: read [-f] [-m SECONDS] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " Read value(s) or hex message.\n" - " -f force reading from the bus (same as '-m 0')\n" - " -m SECONDS only return cached value if age is less than SECONDS [300]\n" - " -c CIRCUIT limit to messages of CIRCUIT\n" - " -d ZZ override destination address ZZ\n" - " -p PRIO set the message poll priority (1-9)\n" - " -v increase verbosity (include names/units/comments)\n" - " -V be very verbose (include names, units, and comments)\n" - " -n use numeric value of value=name pairs\n" - " -i VALUE read additional message parameters from VALUE\n" - " NAME NAME of the message to send\n" - " FIELD only retrieve the field named FIELD\n" - " N only retrieve the N'th field named FIELD (0-based)\n" - " -h send hex read message (or answer from cache):\n" - " ZZ destination address\n" - " PB SB primary/secondary command byte\n" - " NN number of following data bytes\n" - " Dx data byte(s) to send"; - } - string fieldName; - char fieldIndex = -2; - if (args.size() == argPos + 2) { - fieldName = args[argPos + 1]; - fieldIndex = -1; - size_t pos = fieldName.find_last_of('.'); - if (pos != string::npos) { - result_t result = RESULT_OK; - fieldIndex = static_cast(parseInt(fieldName.substr(pos+1).c_str(), 10, 0, MAX_POS, result)); - if (result == RESULT_OK) { - fieldName = fieldName.substr(0, pos); - } - } - } + if (ret == RESULT_OK) { + ret = message->storeLastData(cacheMaster, slave); + ostringstream result; + if (ret == RESULT_OK) { + ret = message->decodeLastData(result); + } + if (ret >= RESULT_OK) { + logInfo(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); + } else { + logError(lf_main, "read hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + } + return slave.getDataStr(true, false); + } + logError(lf_main, "read hex %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + return getResultCode(ret); + } + if (argPos == 0 || args.size() < argPos + 1 || args.size() > argPos + 2) { + return "usage: read [-f] [-m SECONDS] [-c CIRCUIT] [-d ZZ] [-p PRIO] [-v|-V] [-n] [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n" + " or: read [-f] [-m SECONDS] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " Read value(s) or hex message.\n" + " -f force reading from the bus (same as '-m 0')\n" + " -m SECONDS only return cached value if age is less than SECONDS [300]\n" + " -c CIRCUIT limit to messages of CIRCUIT\n" + " -d ZZ override destination address ZZ\n" + " -p PRIO set the message poll priority (1-9)\n" + " -v increase verbosity (include names/units/comments)\n" + " -V be very verbose (include names, units, and comments)\n" + " -n use numeric value of value=name pairs\n" + " -i VALUE read additional message parameters from VALUE\n" + " NAME NAME of the message to send\n" + " FIELD only retrieve the field named FIELD\n" + " N only retrieve the N'th field named FIELD (0-based)\n" + " -h send hex read message (or answer from cache):\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of following data bytes\n" + " Dx data byte(s) to send"; + } + string fieldName; + char fieldIndex = -2; + if (args.size() == argPos + 2) { + fieldName = args[argPos + 1]; + fieldIndex = -1; + size_t pos = fieldName.find_last_of('.'); + if (pos != string::npos) { + result_t result = RESULT_OK; + fieldIndex = static_cast(parseInt(fieldName.substr(pos+1).c_str(), 10, 0, MAX_POS, result)); + if (result == RESULT_OK) { + fieldName = fieldName.substr(0, pos); + } + } + } - ostringstream result; - Message* message = m_messages->find(circuit, args[argPos], false); + ostringstream result; + Message* message = m_messages->find(circuit, args[argPos], false); - // adjust poll priority - if (message != NULL && pollPriority > 0 && message->setPollPriority(pollPriority)) { - m_messages->addPollMessage(message); - } + // adjust poll priority + if (message != NULL && pollPriority > 0 && message->setPollPriority(pollPriority)) { + m_messages->addPollMessage(message); + } - if (dstAddress == SYN && maxAge > 0 && params.length() == 0) { - Message* cacheMessage = m_messages->find(circuit, args[argPos], false, true); - bool hasCache = cacheMessage != NULL; - if (!hasCache || (message != NULL && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) { - cacheMessage = message; // message is newer/better - } - if (cacheMessage != NULL && (cacheMessage->getLastUpdateTime() + maxAge > now || (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) { - if (verbosity & OF_NAMES) { - result << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; - } - result_t ret = cacheMessage->decodeLastData(result, verbosity|(numeric?OF_NUMERIC:0), false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); - if (ret != RESULT_OK) { - if (ret < RESULT_OK) { - logError(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), getResultCode(ret)); - } - return getResultCode(ret); - } - logInfo(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), result.str().c_str()); - return result.str(); - } + if (dstAddress == SYN && maxAge > 0 && params.length() == 0) { + Message* cacheMessage = m_messages->find(circuit, args[argPos], false, true); + bool hasCache = cacheMessage != NULL; + if (!hasCache || (message != NULL && message->getLastUpdateTime() > cacheMessage->getLastUpdateTime())) { + cacheMessage = message; // message is newer/better + } + if (cacheMessage != NULL && (cacheMessage->getLastUpdateTime() + maxAge > now || (cacheMessage->isPassive() && cacheMessage->getLastUpdateTime() != 0))) { + if (verbosity & OF_NAMES) { + result << cacheMessage->getCircuit() << " " << cacheMessage->getName() << " "; + } + result_t ret = cacheMessage->decodeLastData(result, verbosity|(numeric?OF_NUMERIC:0), false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); + if (ret != RESULT_OK) { + if (ret < RESULT_OK) { + logError(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), getResultCode(ret)); + } + return getResultCode(ret); + } + logInfo(lf_main, "read %s %s cached: %s", cacheMessage->getCircuit().c_str(), cacheMessage->getName().c_str(), result.str().c_str()); + return result.str(); + } - if (message == NULL && hasCache) { - return "ERR: no data stored"; - } // else: read directly from bus - } + if (message == NULL && hasCache) { + return "ERR: no data stored"; + } // else: read directly from bus + } - if (message == NULL) { - return getResultCode(RESULT_ERR_NOTFOUND); - } - if (message->getDstAddress() == SYN && dstAddress == SYN) { - return getResultCode(RESULT_ERR_INVALID_ADDR); - } - // read directly from bus - result_t ret = m_busHandler->readFromBus(message, params, dstAddress); - if (ret != RESULT_OK) { - return getResultCode(ret); - } - if (verbosity & OF_NAMES) { - result << message->getCircuit() << " " << message->getName() << " "; - } - ret = message->decodeLastData(pt_slaveData, result, verbosity|(numeric?OF_NUMERIC:0), false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); - if (ret < RESULT_OK) { - logError(lf_main, "read %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - result.str(""); - result << getResultCode(ret) << " in decode"; - return result.str(); - } - if (ret > RESULT_OK) { - return getResultCode(ret); - } - logInfo(lf_main, "read %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); - return result.str(); + if (message == NULL) { + return getResultCode(RESULT_ERR_NOTFOUND); + } + if (message->getDstAddress() == SYN && dstAddress == SYN) { + return getResultCode(RESULT_ERR_INVALID_ADDR); + } + // read directly from bus + result_t ret = m_busHandler->readFromBus(message, params, dstAddress); + if (ret != RESULT_OK) { + return getResultCode(ret); + } + if (verbosity & OF_NAMES) { + result << message->getCircuit() << " " << message->getName() << " "; + } + ret = message->decodeLastData(pt_slaveData, result, verbosity|(numeric?OF_NUMERIC:0), false, fieldIndex == -2 ? NULL : fieldName.c_str(), fieldIndex); + if (ret < RESULT_OK) { + logError(lf_main, "read %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + result.str(""); + result << getResultCode(ret) << " in decode"; + return result.str(); + } + if (ret > RESULT_OK) { + return getResultCode(ret); + } + logInfo(lf_main, "read %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); + return result.str(); } string MainLoop::executeWrite(vector &args) { - size_t argPos = 1; - bool hex = false; - string circuit; - unsigned char dstAddress = SYN; - while (args.size() > argPos && args[argPos][0] == '-') { - if (args[argPos] == "-h") { - hex = true; - } else if (args[argPos] == "-d") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - result_t ret; - dstAddress = (unsigned char)parseInt(args[argPos].c_str(), 16, 0, 0xff, ret); - if (ret != RESULT_OK || !isValidAddress(dstAddress) || isMaster(dstAddress)) { - return getResultCode(RESULT_ERR_INVALID_ADDR); - } - } else if (args[argPos] == "-c") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - circuit = args[argPos]; - } else { - argPos = 0; // print usage - break; - } - argPos++; - } + size_t argPos = 1; + bool hex = false; + string circuit; + unsigned char dstAddress = SYN; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-h") { + hex = true; + } else if (args[argPos] == "-d") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + result_t ret; + dstAddress = (unsigned char)parseInt(args[argPos].c_str(), 16, 0, 0xff, ret); + if (ret != RESULT_OK || !isValidAddress(dstAddress) || isMaster(dstAddress)) { + return getResultCode(RESULT_ERR_INVALID_ADDR); + } + } else if (args[argPos] == "-c") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + circuit = args[argPos]; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } - if (hex && (dstAddress != SYN || !circuit.empty() || args.size() < argPos + 1)) { - argPos = 0; // print usage - } + if (hex && (dstAddress != SYN || !circuit.empty() || args.size() < argPos + 1)) { + argPos = 0; // print usage + } - if (hex && argPos > 0) { - SymbolString cacheMaster(false); - result_t ret = parseHexMaster(args, argPos, cacheMaster); - if (ret != RESULT_OK) { - return getResultCode(ret); - } - logNotice(lf_main, "write hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); + if (hex && argPos > 0) { + SymbolString cacheMaster(false); + result_t ret = parseHexMaster(args, argPos, cacheMaster); + if (ret != RESULT_OK) { + return getResultCode(ret); + } + logNotice(lf_main, "write hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); - // find message - Message* message = m_messages->find(cacheMaster, false, false, true, false); + // find message + Message* message = m_messages->find(cacheMaster, false, false, true, false); - if (message == NULL) { - return getResultCode(RESULT_ERR_NOTFOUND); - } - if (!message->isWrite()) { - return getResultCode(RESULT_ERR_INVALID_ARG); - } - if (circuit.length() > 0 && circuit != message->getCircuit()) { - return getResultCode(RESULT_ERR_INVALID_ARG); // non-matching circuit - } - // send message - SymbolString master(true); - master.addAll(cacheMaster); - SymbolString slave(false); - ret = m_busHandler->sendAndWait(master, slave); + if (message == NULL) { + return getResultCode(RESULT_ERR_NOTFOUND); + } + if (!message->isWrite()) { + return getResultCode(RESULT_ERR_INVALID_ARG); + } + if (circuit.length() > 0 && circuit != message->getCircuit()) { + return getResultCode(RESULT_ERR_INVALID_ARG); // non-matching circuit + } + // send message + SymbolString master(true); + master.addAll(cacheMaster); + SymbolString slave(false); + ret = m_busHandler->sendAndWait(master, slave); - if (ret == RESULT_OK) { - // also update read messages - ret = message->storeLastData(cacheMaster, slave); - ostringstream result; - if (ret == RESULT_OK) { - ret = message->decodeLastData(result); - } - if (ret >= RESULT_OK) { - logInfo(lf_main, "write hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); - } else { - logError(lf_main, "write hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - } - if (master[1] == BROADCAST) { - return "done broadcast"; - } - if (isMaster(master[1])) { - return getResultCode(RESULT_OK); - } - return slave.getDataStr(true, false); - } - logError(lf_main, "write hex %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - return getResultCode(ret); - } + if (ret == RESULT_OK) { + // also update read messages + ret = message->storeLastData(cacheMaster, slave); + ostringstream result; + if (ret == RESULT_OK) { + ret = message->decodeLastData(result); + } + if (ret >= RESULT_OK) { + logInfo(lf_main, "write hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); + } else { + logError(lf_main, "write hex %s %s cache update: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + } + if (master[1] == BROADCAST) { + return "done broadcast"; + } + if (isMaster(master[1])) { + return getResultCode(RESULT_OK); + } + return slave.getDataStr(true, false); + } + logError(lf_main, "write hex %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + return getResultCode(ret); + } - if (argPos == 0 || circuit.empty() || (args.size() != argPos + 2 && args.size() != argPos + 1)) { - return "usage: write [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" - " or: write [-c CIRCUIT] -h ZZPBSBNNDx\n" - " Write value(s) or hex message.\n" - " -d ZZ override destination address ZZ\n" - " -c CIRCUIT CIRCUIT of the message to send\n" - " NAME NAME of the message to send\n" - " VALUE a single field VALUE\n" - " -h send hex write message:\n" - " ZZ destination address\n" - " PB SB primary/secondary command byte\n" - " NN number of following data bytes\n" - " Dx data byte(s) to send"; - } - Message* message = m_messages->find(circuit, args[argPos], true); + if (argPos == 0 || circuit.empty() || (args.size() != argPos + 2 && args.size() != argPos + 1)) { + return "usage: write [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" + " or: write [-c CIRCUIT] -h ZZPBSBNNDx\n" + " Write value(s) or hex message.\n" + " -d ZZ override destination address ZZ\n" + " -c CIRCUIT CIRCUIT of the message to send\n" + " NAME NAME of the message to send\n" + " VALUE a single field VALUE\n" + " -h send hex write message:\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of following data bytes\n" + " Dx data byte(s) to send"; + } + Message* message = m_messages->find(circuit, args[argPos], true); - if (message == NULL) { - return getResultCode(RESULT_ERR_NOTFOUND); - } - if (message->getDstAddress() == SYN && dstAddress == SYN) { - return getResultCode(RESULT_ERR_INVALID_ADDR); - } - result_t ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress); // allow missing values - if (ret != RESULT_OK) { - logError(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - return getResultCode(ret); - } - dstAddress = message->getLastMasterData()[1]; - ostringstream result; - if (dstAddress == BROADCAST || isMaster(dstAddress)) { - logNotice(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - if (dstAddress == BROADCAST) { - return "done broadcast"; - } - return getResultCode(RESULT_OK); - } + if (message == NULL) { + return getResultCode(RESULT_ERR_NOTFOUND); + } + if (message->getDstAddress() == SYN && dstAddress == SYN) { + return getResultCode(RESULT_ERR_INVALID_ADDR); + } + result_t ret = m_busHandler->readFromBus(message, args.size() == argPos + 1 ? "" : args[argPos + 1], dstAddress); // allow missing values + if (ret != RESULT_OK) { + logError(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + return getResultCode(ret); + } + dstAddress = message->getLastMasterData()[1]; + ostringstream result; + if (dstAddress == BROADCAST || isMaster(dstAddress)) { + logNotice(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + if (dstAddress == BROADCAST) { + return "done broadcast"; + } + return getResultCode(RESULT_OK); + } - ret = message->decodeLastData(pt_slaveData, result); // decode data - if (ret >= RESULT_OK && result.str().empty()) { - logNotice(lf_main, "write %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - return getResultCode(RESULT_OK); - } - if (ret != RESULT_OK) { - logError(lf_main, "write %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); - result.str(""); - result << getResultCode(ret) << " in decode"; - return result.str(); - } - logNotice(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); - return result.str(); + ret = message->decodeLastData(pt_slaveData, result); // decode data + if (ret >= RESULT_OK && result.str().empty()) { + logNotice(lf_main, "write %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + return getResultCode(RESULT_OK); + } + if (ret != RESULT_OK) { + logError(lf_main, "write %s %s: decode %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(ret)); + result.str(""); + result << getResultCode(ret) << " in decode"; + return result.str(); + } + logNotice(lf_main, "write %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), result.str().c_str()); + return result.str(); } string MainLoop::executeHex(vector &args) { - size_t argPos = 1; - if (args.size() < argPos + 1 || (args.size() > argPos && args[argPos][0] == '-')) { - argPos = 0; // print usage - } + size_t argPos = 1; + if (args.size() < argPos + 1 || (args.size() > argPos && args[argPos][0] == '-')) { + argPos = 0; // print usage + } - if (argPos > 0) { - SymbolString cacheMaster(false); - result_t ret = parseHexMaster(args, argPos, cacheMaster); - if (ret != RESULT_OK) { - return getResultCode(ret); - } - logNotice(lf_main, "hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); + if (argPos > 0) { + SymbolString cacheMaster(false); + result_t ret = parseHexMaster(args, argPos, cacheMaster); + if (ret != RESULT_OK) { + return getResultCode(ret); + } + logNotice(lf_main, "hex cmd: %s", cacheMaster.getDataStr(true, false).c_str()); - // send message - SymbolString master(true); - master.addAll(cacheMaster); - SymbolString slave(false); - ret = m_busHandler->sendAndWait(master, slave); + // send message + SymbolString master(true); + master.addAll(cacheMaster); + SymbolString slave(false); + ret = m_busHandler->sendAndWait(master, slave); - if (ret == RESULT_OK) { - if (master[1] == BROADCAST) { - return "done broadcast"; - } - if (isMaster(master[1])) { - return getResultCode(RESULT_OK); - } - return slave.getDataStr(true, false); - } - logError(lf_main, "hex: %s", getResultCode(ret)); - return getResultCode(ret); - } + if (ret == RESULT_OK) { + if (master[1] == BROADCAST) { + return "done broadcast"; + } + if (isMaster(master[1])) { + return getResultCode(RESULT_OK); + } + return slave.getDataStr(true, false); + } + logError(lf_main, "hex: %s", getResultCode(ret)); + return getResultCode(ret); + } - return "usage: hex ZZPBSBNNDx\n" - " Send arbitrary data in hex (only if enabled).\n" - " ZZ destination address\n" - " PB SB primary/secondary command byte\n" - " NN number of following data bytes\n" - " Dx data byte(s) to send"; + return "usage: hex ZZPBSBNNDx\n" + " Send arbitrary data in hex (only if enabled).\n" + " ZZ destination address\n" + " PB SB primary/secondary command byte\n" + " NN number of following data bytes\n" + " Dx data byte(s) to send"; } string MainLoop::executeFind(vector &args) { - size_t argPos = 1; - bool configFormat = false, exact = false, withRead = true, withWrite = false, withPassive = true, first = true, onlyWithData = false, hexFormat = false; - OutputFormat verbosity = 0; - vector columns; - string circuit; - vector id; - while (args.size() > argPos && args[argPos][0] == '-') { - if (args[argPos] == "-v") { - switch (verbosity) { - case 0: - verbosity |= OF_NAMES; - break; - case OF_NAMES: - verbosity |= OF_UNITS; - break; - case OF_NAMES|OF_UNITS: - verbosity |= OF_COMMENTS; - break; - } - } else if (args[argPos] == "-vv") { - verbosity |= OF_NAMES|OF_UNITS; - } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { - verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; - } else if (args[argPos] == "-f") { - configFormat = true; - if (hexFormat) { - argPos = 0; // print usage - break; - } - } else if (args[argPos] == "-F") { - argPos++; - if (hexFormat || (argPos >= args.size())) { - argPos = 0; // print usage - break; - } - istringstream input(args[argPos]); - string column; - while (getline(input, column, ',')) { - size_t idx = columnCount; - for (size_t i = 0; i < columnCount; i++) { - if (strcasecmp(columnNames[i], column.c_str()) == 0) { - idx = i; - break; - } - } - if (idx == columnCount) { - argPos = 0; // print usage - break; - } - columns.push_back(columnIds[idx]); - } - if (columns.empty()) { - argPos = 0; // print usage - break; - } - } else if (args[argPos] == "-e") { - exact = true; - } else if (args[argPos] == "-r") { - if (first) { - first = false; - withWrite = withPassive = false; - } - withRead = true; - } else if (args[argPos] == "-w") { - if (first) { - first = false; - withRead = withPassive = false; - } - withWrite = true; - } else if (args[argPos] == "-p") { - if (first) { - first = false; - withRead = withWrite = false; - } - withPassive = true; - } else if (args[argPos] == "-d") { - onlyWithData = true; - } else if (args[argPos] == "-h") { - hexFormat = true; - if (configFormat) { - argPos = 0; // print usage - break; - } - } else if (args[argPos] == "-i") { - argPos++; - if (argPos >= args.size() || !id.empty()) { - argPos = 0; // print usage - break; - } - result_t result = Message::parseId(args[argPos], id); - if (result != RESULT_OK) { - return getResultCode(result); - } - if (id.empty()) { - argPos = 0; // print usage - break; - } - } else if (args[argPos] == "-c") { - argPos++; - if (argPos >= args.size()) { - argPos = 0; // print usage - break; - } - circuit = args[argPos]; - } else { - argPos = 0; // print usage - break; - } - argPos++; - } - if (argPos == 0 || args.size() < argPos || args.size() > argPos + 1) { - return "usage: find [-v|-V] [-r] [-w] [-p] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e] [-c CIRCUIT] [NAME]\n" - " Find message(s).\n" - " -v increase verbosity (include names/units/comments+destination address+update time)\n" - " -V be very verbose (include everything)\n" - " -r limit to active read messages (default: read + passive)\n" - " -w limit to active write messages (default: read + passive)\n" - " -p limit to passive messages (default: read + passive)\n" - " -d only include messages with actual data\n" - " -h show hex data instead of decoded values\n" - " -i ID limit to messages with ID (in hex, PB, SB and further ID bytes)\n" - " -f list messages in CSV configuration file format\n" - " -F COL[,COL]* list messages in the specified format\n" - " (COL: type,circuit,name,comment,qq,zz,pbsb,id,fields)\n" - " -e match NAME and optional CIRCUIT exactly (ignoring case)\n" - " -c CIRCUIT limit to messages of CIRCUIT (or a part thereof without '-e')\n" - " NAME NAME of the messages to find (or a part thereof without '-e')"; - } - deque messages = m_messages->findAll( - circuit, args.size() == argPos ? "" : args[argPos], exact, withRead, withWrite, withPassive - ); + size_t argPos = 1; + bool configFormat = false, exact = false, withRead = true, withWrite = false, withPassive = true, first = true, onlyWithData = false, hexFormat = false; + OutputFormat verbosity = 0; + vector columns; + string circuit; + vector id; + while (args.size() > argPos && args[argPos][0] == '-') { + if (args[argPos] == "-v") { + switch (verbosity) { + case 0: + verbosity |= OF_NAMES; + break; + case OF_NAMES: + verbosity |= OF_UNITS; + break; + case OF_NAMES|OF_UNITS: + verbosity |= OF_COMMENTS; + break; + } + } else if (args[argPos] == "-vv") { + verbosity |= OF_NAMES|OF_UNITS; + } else if (args[argPos] == "-vvv" || args[argPos] == "-V") { + verbosity |= OF_NAMES|OF_UNITS|OF_COMMENTS; + } else if (args[argPos] == "-f") { + configFormat = true; + if (hexFormat) { + argPos = 0; // print usage + break; + } + } else if (args[argPos] == "-F") { + argPos++; + if (hexFormat || (argPos >= args.size())) { + argPos = 0; // print usage + break; + } + istringstream input(args[argPos]); + string column; + while (getline(input, column, ',')) { + size_t idx = columnCount; + for (size_t i = 0; i < columnCount; i++) { + if (strcasecmp(columnNames[i], column.c_str()) == 0) { + idx = i; + break; + } + } + if (idx == columnCount) { + argPos = 0; // print usage + break; + } + columns.push_back(columnIds[idx]); + } + if (columns.empty()) { + argPos = 0; // print usage + break; + } + } else if (args[argPos] == "-e") { + exact = true; + } else if (args[argPos] == "-r") { + if (first) { + first = false; + withWrite = withPassive = false; + } + withRead = true; + } else if (args[argPos] == "-w") { + if (first) { + first = false; + withRead = withPassive = false; + } + withWrite = true; + } else if (args[argPos] == "-p") { + if (first) { + first = false; + withRead = withWrite = false; + } + withPassive = true; + } else if (args[argPos] == "-d") { + onlyWithData = true; + } else if (args[argPos] == "-h") { + hexFormat = true; + if (configFormat) { + argPos = 0; // print usage + break; + } + } else if (args[argPos] == "-i") { + argPos++; + if (argPos >= args.size() || !id.empty()) { + argPos = 0; // print usage + break; + } + result_t result = Message::parseId(args[argPos], id); + if (result != RESULT_OK) { + return getResultCode(result); + } + if (id.empty()) { + argPos = 0; // print usage + break; + } + } else if (args[argPos] == "-c") { + argPos++; + if (argPos >= args.size()) { + argPos = 0; // print usage + break; + } + circuit = args[argPos]; + } else { + argPos = 0; // print usage + break; + } + argPos++; + } + if (argPos == 0 || args.size() < argPos || args.size() > argPos + 1) { + return "usage: find [-v|-V] [-r] [-w] [-p] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e] [-c CIRCUIT] [NAME]\n" + " Find message(s).\n" + " -v increase verbosity (include names/units/comments+destination address+update time)\n" + " -V be very verbose (include everything)\n" + " -r limit to active read messages (default: read + passive)\n" + " -w limit to active write messages (default: read + passive)\n" + " -p limit to passive messages (default: read + passive)\n" + " -d only include messages with actual data\n" + " -h show hex data instead of decoded values\n" + " -i ID limit to messages with ID (in hex, PB, SB and further ID bytes)\n" + " -f list messages in CSV configuration file format\n" + " -F COL[,COL]* list messages in the specified format\n" + " (COL: type,circuit,name,comment,qq,zz,pbsb,id,fields)\n" + " -e match NAME and optional CIRCUIT exactly (ignoring case)\n" + " -c CIRCUIT limit to messages of CIRCUIT (or a part thereof without '-e')\n" + " NAME NAME of the messages to find (or a part thereof without '-e')"; + } + deque messages = m_messages->findAll( + circuit, args.size() == argPos ? "" : args[argPos], exact, withRead, withWrite, withPassive + ); - bool found = false; - ostringstream result; - char str[32]; - for (deque::iterator it = messages.begin(); it != messages.end();) { - Message* message = *it++; - if (!id.empty() && !message->checkIdPrefix(id)) { - continue; - } - time_t lastup = message->getLastUpdateTime(); - if (onlyWithData && lastup == 0) { - continue; - } - if (configFormat) { - if (found) { - result << endl; - } - message->dump(result); - } else if (!columns.empty()) { - if (found) { - result << endl; - } - message->dump(result, &columns); - } else { - if (found) { - result << endl; - } - result << message->getCircuit() << " " << message->getName() << " = "; - if (lastup == 0) { - result << "no data stored"; - } else if (hexFormat) { - result << message->getLastMasterData().getDataStr() - << " / " << message->getLastSlaveData().getDataStr(true, false); - } else { - result_t ret = message->decodeLastData(result, verbosity); - if (ret != RESULT_OK) { - result << " (" << getResultCode(ret) - << " for " << message->getLastMasterData().getDataStr() - << " / " << message->getLastSlaveData().getDataStr(true, false) << ")"; - } - } - if (verbosity == (OF_NAMES|OF_UNITS|OF_COMMENTS)) { - unsigned char dstAddress = message->getDstAddress(); - if (dstAddress != SYN) { - snprintf(str, sizeof(str), "%02x", dstAddress); - } else if (lastup != 0 && message->getLastMasterData().size() > 1) { - snprintf(str, sizeof(str), "%02x", message->getLastMasterData()[1]); - } else { - snprintf(str, sizeof(str), "any"); - } - if (lastup != 0) { - struct tm td; - localtime_r(&lastup, &td); - size_t len = strlen(str); - snprintf(str+len, sizeof(str)-len, ", lastup=%04d-%02d-%02d %02d:%02d:%02d", - td.tm_year+1900, td.tm_mon+1, td.tm_mday, - td.tm_hour, td.tm_min, td.tm_sec); - } - result << " [ZZ=" << str; - if (message->isPassive()) { - result << ", passive"; - } else { - result << ", active"; - } - if (message->isWrite()) { - result << " write]"; - } else { - result << " read]"; - } - } - } - found = true; - } - if (!found) { - return getResultCode(RESULT_ERR_NOTFOUND); - } - return result.str(); + bool found = false; + ostringstream result; + char str[32]; + for (deque::iterator it = messages.begin(); it != messages.end();) { + Message* message = *it++; + if (!id.empty() && !message->checkIdPrefix(id)) { + continue; + } + time_t lastup = message->getLastUpdateTime(); + if (onlyWithData && lastup == 0) { + continue; + } + if (configFormat) { + if (found) { + result << endl; + } + message->dump(result); + } else if (!columns.empty()) { + if (found) { + result << endl; + } + message->dump(result, &columns); + } else { + if (found) { + result << endl; + } + result << message->getCircuit() << " " << message->getName() << " = "; + if (lastup == 0) { + result << "no data stored"; + } else if (hexFormat) { + result << message->getLastMasterData().getDataStr() + << " / " << message->getLastSlaveData().getDataStr(true, false); + } else { + result_t ret = message->decodeLastData(result, verbosity); + if (ret != RESULT_OK) { + result << " (" << getResultCode(ret) + << " for " << message->getLastMasterData().getDataStr() + << " / " << message->getLastSlaveData().getDataStr(true, false) << ")"; + } + } + if (verbosity == (OF_NAMES|OF_UNITS|OF_COMMENTS)) { + unsigned char dstAddress = message->getDstAddress(); + if (dstAddress != SYN) { + snprintf(str, sizeof(str), "%02x", dstAddress); + } else if (lastup != 0 && message->getLastMasterData().size() > 1) { + snprintf(str, sizeof(str), "%02x", message->getLastMasterData()[1]); + } else { + snprintf(str, sizeof(str), "any"); + } + if (lastup != 0) { + struct tm td; + localtime_r(&lastup, &td); + size_t len = strlen(str); + snprintf(str+len, sizeof(str)-len, ", lastup=%04d-%02d-%02d %02d:%02d:%02d", + td.tm_year+1900, td.tm_mon+1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec); + } + result << " [ZZ=" << str; + if (message->isPassive()) { + result << ", passive"; + } else { + result << ", active"; + } + if (message->isWrite()) { + result << " write]"; + } else { + result << " read]"; + } + } + } + found = true; + } + if (!found) { + return getResultCode(RESULT_ERR_NOTFOUND); + } + return result.str(); } string MainLoop::executeListen(vector &args, bool& listening) { - if (args.size() == 1) { - if (listening) { - return "listen continued"; - } - listening = true; - return "listen started"; - } + if (args.size() == 1) { + if (listening) { + return "listen continued"; + } + listening = true; + return "listen started"; + } - if (args.size() != 2 || args[1] != "stop") { - return "usage: listen [stop]\n" - " Listen for updates or stop it."; - } - listening = false; - return "listen stopped"; + if (args.size() != 2 || args[1] != "stop") { + return "usage: listen [stop]\n" + " Listen for updates or stop it."; + } + listening = false; + return "listen stopped"; } string MainLoop::executeState(vector &args) { - if (args.size() == 0) { - return "usage: state\n" - " Report bus state."; - } - if (m_busHandler->hasSignal()) { - ostringstream result; - result << "signal acquired, " - << static_cast(m_busHandler->getSymbolRate()) << " symbols/sec (" - << static_cast(m_busHandler->getMaxSymbolRate()) << " max), " - << static_cast(m_busHandler->getMasterCount()) << " masters"; - return result.str(); - } - return "no signal"; + if (args.size() == 0) { + return "usage: state\n" + " Report bus state."; + } + if (m_busHandler->hasSignal()) { + ostringstream result; + result << "signal acquired, " + << static_cast(m_busHandler->getSymbolRate()) << " symbols/sec (" + << static_cast(m_busHandler->getMaxSymbolRate()) << " max), " + << static_cast(m_busHandler->getMasterCount()) << " masters"; + return result.str(); + } + return "no signal"; } string MainLoop::executeGrab(vector &args) { - if (args.size() == 1) { - return m_busHandler->enableGrab(true) ? "grab started" : "grab continued"; - } - if (args.size() == 2 && strcasecmp(args[1].c_str(), "STOP") == 0) { - return m_busHandler->enableGrab(false) ? "grab stopped" : "grab not running"; - } - if (args.size() >= 2 && strcasecmp(args[1].c_str(), "RESULT") == 0) { - if (args.size() == 2 || strcasecmp(args[2].c_str(), "ALL") == 0) { - ostringstream result; - m_busHandler->formatGrabResult(args.size() == 2, result); - return result.str(); - } - } - return "usage: grab [stop]\n" - " or: grab result [all]\n" - " Start or stop grabbing, or report unknown or all grabbed messages."; + if (args.size() == 1) { + return m_busHandler->enableGrab(true) ? "grab started" : "grab continued"; + } + if (args.size() == 2 && strcasecmp(args[1].c_str(), "STOP") == 0) { + return m_busHandler->enableGrab(false) ? "grab stopped" : "grab not running"; + } + if (args.size() >= 2 && strcasecmp(args[1].c_str(), "RESULT") == 0) { + if (args.size() == 2 || strcasecmp(args[2].c_str(), "ALL") == 0) { + ostringstream result; + m_busHandler->formatGrabResult(args.size() == 2, result); + return result.str(); + } + } + return "usage: grab [stop]\n" + " or: grab result [all]\n" + " Start or stop grabbing, or report unknown or all grabbed messages."; } string MainLoop::executeScan(vector &args) { - if (args.size() == 1) { - result_t result = m_busHandler->startScan(); - if (result != RESULT_OK) { - logError(lf_main, "scan: %s", getResultCode(result)); - } - return getResultCode(result); - } + if (args.size() == 1) { + result_t result = m_busHandler->startScan(); + if (result != RESULT_OK) { + logError(lf_main, "scan: %s", getResultCode(result)); + } + return getResultCode(result); + } - if (args.size() == 2) { - if (strcasecmp(args[1].c_str(), "FULL") == 0) { - result_t result = m_busHandler->startScan(true); - if (result != RESULT_OK) { - logError(lf_main, "full scan: %s", getResultCode(result)); - } - return getResultCode(result); - } + if (args.size() == 2) { + if (strcasecmp(args[1].c_str(), "FULL") == 0) { + result_t result = m_busHandler->startScan(true); + if (result != RESULT_OK) { + logError(lf_main, "full scan: %s", getResultCode(result)); + } + return getResultCode(result); + } - if (strcasecmp(args[1].c_str(), "RESULT") == 0) { - ostringstream ret; - m_busHandler->formatScanResult(ret); - return ret.str(); - } + if (strcasecmp(args[1].c_str(), "RESULT") == 0) { + ostringstream ret; + m_busHandler->formatScanResult(ret); + return ret.str(); + } - result_t result; - unsigned char dstAddress = (unsigned char)parseInt(args[1].c_str(), 16, 0, 0xff, result); - if (result == RESULT_OK && !isValidAddress(dstAddress, false)) { - result = RESULT_ERR_INVALID_ADDR; - } - if (result != RESULT_OK) { - return getResultCode(result); - } - SymbolString slave(false); - result = m_busHandler->scanAndWait(dstAddress, slave); - if (result != RESULT_OK) { - return getResultCode(result); - } - Message* message = m_messages->getScanMessage(dstAddress); // never NULL due to scanAndWait() == RESULT_OK && dstAddress != BROADCAST - ostringstream ret; - ret << hex << setw(2) << setfill('0') << static_cast(dstAddress); - result = message->decodeLastData(ret, 0, true); // decode data - if (result != RESULT_OK) { - return getResultCode(result); - } - return ret.str(); - } + result_t result; + unsigned char dstAddress = (unsigned char)parseInt(args[1].c_str(), 16, 0, 0xff, result); + if (result == RESULT_OK && !isValidAddress(dstAddress, false)) { + result = RESULT_ERR_INVALID_ADDR; + } + if (result != RESULT_OK) { + return getResultCode(result); + } + SymbolString slave(false); + result = m_busHandler->scanAndWait(dstAddress, slave); + if (result != RESULT_OK) { + return getResultCode(result); + } + Message* message = m_messages->getScanMessage(dstAddress); // never NULL due to scanAndWait() == RESULT_OK && dstAddress != BROADCAST + ostringstream ret; + ret << hex << setw(2) << setfill('0') << static_cast(dstAddress); + result = message->decodeLastData(ret, 0, true); // decode data + if (result != RESULT_OK) { + return getResultCode(result); + } + return ret.str(); + } - return "usage: scan [full|ZZ]\n" - " or: scan result\n" - " Scan seen slaves, all slaves (full), a single slave (address ZZ), or report scan result."; + return "usage: scan [full|ZZ]\n" + " or: scan result\n" + " Scan seen slaves, all slaves (full), a single slave (address ZZ), or report scan result."; } string MainLoop::executeLog(vector &args) { - if (args.size() == 1) { - ostringstream ret; - char str[48]; - if (getLogFacilities(str)) { - ret << str << ' '; - } - ret << getLogLevel(); - return ret.str(); - } - bool result; - // old format: log areas AREA[,AREA]*, log level LEVEL - if ((args.size() == 3 || args.size() == 2) && strcasecmp(args[1].c_str(), "AREAS") == 0) { - result = setLogFacilities(args.size() == 3 ? args[2].c_str() : ""); - } else if (args.size() == 3 && strcasecmp(args[1].c_str(), "LEVEL") == 0) { - result = setLogLevel(args[2].c_str()); - } else if (args.size() == 2) { - result = setLogLevel(args[1].c_str()) || setLogFacilities(args[1].c_str()); - } else if (args.size() == 3) { - result = setLogFacilities(args[1].c_str()) && setLogLevel(args[2].c_str()); - } else { - return "usage: log [AREA[,AREA]*] [LEVEL]\n" - " Set log area(s) and/or log level or get current settings.\n" - " AREA log area to include (main|network|bus|update|all)\n" - " LEVEL log level to set (error|notice|info|debug)"; - } - if (result) { - return getResultCode(RESULT_OK); - } - return getResultCode(RESULT_ERR_INVALID_ARG); + if (args.size() == 1) { + ostringstream ret; + char str[48]; + if (getLogFacilities(str)) { + ret << str << ' '; + } + ret << getLogLevel(); + return ret.str(); + } + bool result; + // old format: log areas AREA[,AREA]*, log level LEVEL + if ((args.size() == 3 || args.size() == 2) && strcasecmp(args[1].c_str(), "AREAS") == 0) { + result = setLogFacilities(args.size() == 3 ? args[2].c_str() : ""); + } else if (args.size() == 3 && strcasecmp(args[1].c_str(), "LEVEL") == 0) { + result = setLogLevel(args[2].c_str()); + } else if (args.size() == 2) { + result = setLogLevel(args[1].c_str()) || setLogFacilities(args[1].c_str()); + } else if (args.size() == 3) { + result = setLogFacilities(args[1].c_str()) && setLogLevel(args[2].c_str()); + } else { + return "usage: log [AREA[,AREA]*] [LEVEL]\n" + " Set log area(s) and/or log level or get current settings.\n" + " AREA log area to include (main|network|bus|update|all)\n" + " LEVEL log level to set (error|notice|info|debug)"; + } + if (result) { + return getResultCode(RESULT_OK); + } + return getResultCode(RESULT_ERR_INVALID_ARG); } string MainLoop::executeRaw(vector &args) { - if (args.size() != 1) { - return "usage: raw\n" - " Toggle logging of each byte."; - } - bool enabled; - if (m_logRawFile) { - enabled = !m_logRawFile->isEnabled(); - m_logRawFile->setEnabled(enabled); - } else { - enabled = !m_logRawEnabled; - m_logRawEnabled = enabled; - } - return enabled ? "raw logging enabled" : "raw logging disabled"; + if (args.size() != 1) { + return "usage: raw\n" + " Toggle logging of each byte."; + } + bool enabled; + if (m_logRawFile) { + enabled = !m_logRawFile->isEnabled(); + m_logRawFile->setEnabled(enabled); + } else { + enabled = !m_logRawEnabled; + m_logRawEnabled = enabled; + } + return enabled ? "raw logging enabled" : "raw logging disabled"; } string MainLoop::executeDump(vector &args) { - if (args.size() != 1) { - return "usage: dump\n" - " Toggle binary dump of received bytes."; - } - if (!m_dumpFile) { - return "dump not configured"; - } - bool enabled = !m_dumpFile->isEnabled(); - m_dumpFile->setEnabled(enabled); - return enabled ? "dump enabled" : "dump disabled"; + if (args.size() != 1) { + return "usage: dump\n" + " Toggle binary dump of received bytes."; + } + if (!m_dumpFile) { + return "dump not configured"; + } + bool enabled = !m_dumpFile->isEnabled(); + m_dumpFile->setEnabled(enabled); + return enabled ? "dump enabled" : "dump disabled"; } string MainLoop::executeReload(vector &args) { - if (args.size() != 1) { - return "usage: reload\n" - " Reload CSV config files."; - } - m_busHandler->clear(); - result_t result = loadConfigFiles(m_messages); - return getResultCode(result); + if (args.size() != 1) { + return "usage: reload\n" + " Reload CSV config files."; + } + m_busHandler->clear(); + result_t result = loadConfigFiles(m_messages); + return getResultCode(result); } string MainLoop::executeInfo(vector &args) { - if (args.size() == 0) { - return "usage: info\n" - " Report information about the daemon, the configuration, and seen devices."; - } - ostringstream result; - result << "version: " << PACKAGE_STRING "." REVISION "\n"; - if (m_busHandler->hasSignal()) { - result << "signal: acquired\n"; - result << "symbol rate: " << static_cast(m_busHandler->getSymbolRate()) << "\n"; - } else { - result << "signal: no signal\n"; - } - result << "reconnects: " << static_cast(m_reconnectCount) << "\n"; - result << "masters: " << static_cast(m_busHandler->getMasterCount()) << "\n"; - result << "messages: " << static_cast(m_messages->size()) << "\n"; - result << "conditional: " << static_cast(m_messages->sizeConditional()) << "\n"; - result << "poll: " << static_cast(m_messages->sizePoll()) << "\n"; - result << "update: " << static_cast(m_messages->sizePassive()); - m_busHandler->formatSeenInfo(result); - return result.str(); + if (args.size() == 0) { + return "usage: info\n" + " Report information about the daemon, the configuration, and seen devices."; + } + ostringstream result; + result << "version: " << PACKAGE_STRING "." REVISION "\n"; + if (m_busHandler->hasSignal()) { + result << "signal: acquired\n"; + result << "symbol rate: " << static_cast(m_busHandler->getSymbolRate()) << "\n"; + } else { + result << "signal: no signal\n"; + } + result << "reconnects: " << static_cast(m_reconnectCount) << "\n"; + result << "masters: " << static_cast(m_busHandler->getMasterCount()) << "\n"; + result << "messages: " << static_cast(m_messages->size()) << "\n"; + result << "conditional: " << static_cast(m_messages->sizeConditional()) << "\n"; + result << "poll: " << static_cast(m_messages->sizePoll()) << "\n"; + result << "update: " << static_cast(m_messages->sizePassive()); + m_busHandler->formatSeenInfo(result); + return result.str(); } string MainLoop::executeQuit(vector &args, bool& connected) { - if (args.size() == 1) { - connected = false; - return "connection closed"; - } - return "usage: quit\n" - " Close client connection."; + if (args.size() == 1) { + connected = false; + return "connection closed"; + } + return "usage: quit\n" + " Close client connection."; } string MainLoop::executeHelp() { - return "usage:\n" - " read|r Read value(s): read [-f] [-m SECONDS] [-c CIRCUIT] [-d ZZ] [-p PRIO] [-v|-V] [-n] [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n" - " Read hex message: read [-f] [-m SECONDS] [-c CIRCUIT] -h ZZPBSBNNDx\n" - " write|w Write value(s): write [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" - " Write hex message: write [-c CIRCUIT] -h ZZPBSBNNDx\n" - " hex Send hex data: hex ZZPBSBNNDx\n" - " find|f Find message(s): find [-v|-V] [-r] [-w] [-p] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e] [-c CIRCUIT] [NAME]\n" - " listen|l Listen for updates: listen [stop]\n" - " state|s Report bus state\n" - " info|i Report information about the daemon, the configuration, and seen devices.\n" - " grab|g Grab messages: grab [stop]\n" - " Report the messages: grab result [all]\n" - " scan Scan slaves: scan [full|ZZ]\n" - " Report scan result: scan result\n" - " log Set log area/level: log [AREA[,AREA]*] [LEVEL]\n" - " AREA: main|network|bus|update|all\n" - " LEVEL: error|notice|info|debug\n" - " raw Toggle logging of each byte\n" - " dump Toggle binary dump of received bytes\n" - " reload Reload CSV config files\n" - " quit|q Close connection\n" - " help|h Print help help [COMMAND]"; + return "usage:\n" + " read|r Read value(s): read [-f] [-m SECONDS] [-c CIRCUIT] [-d ZZ] [-p PRIO] [-v|-V] [-n] [-i VALUE[;VALUE]*] NAME [FIELD[.N]]\n" + " Read hex message: read [-f] [-m SECONDS] [-c CIRCUIT] -h ZZPBSBNNDx\n" + " write|w Write value(s): write [-d ZZ] -c CIRCUIT NAME [VALUE[;VALUE]*]\n" + " Write hex message: write [-c CIRCUIT] -h ZZPBSBNNDx\n" + " hex Send hex data: hex ZZPBSBNNDx\n" + " find|f Find message(s): find [-v|-V] [-r] [-w] [-p] [-d] [-h] [-i ID] [-f] [-F COL[,COL]*] [-e] [-c CIRCUIT] [NAME]\n" + " listen|l Listen for updates: listen [stop]\n" + " state|s Report bus state\n" + " info|i Report information about the daemon, the configuration, and seen devices.\n" + " grab|g Grab messages: grab [stop]\n" + " Report the messages: grab result [all]\n" + " scan Scan slaves: scan [full|ZZ]\n" + " Report scan result: scan result\n" + " log Set log area/level: log [AREA[,AREA]*] [LEVEL]\n" + " AREA: main|network|bus|update|all\n" + " LEVEL: error|notice|info|debug\n" + " raw Toggle logging of each byte\n" + " dump Toggle binary dump of received bytes\n" + " reload Reload CSV config files\n" + " quit|q Close connection\n" + " help|h Print help help [COMMAND]"; } string MainLoop::executeGet(vector &args, bool& connected) { - result_t ret = RESULT_OK; - bool numeric = false, required = false; - OutputFormat verbosity = OF_NAMES; - size_t argPos = 1; - string uri = args[argPos++]; - ostringstream result; - int type = -1; + result_t ret = RESULT_OK; + bool numeric = false, required = false; + OutputFormat verbosity = OF_NAMES; + size_t argPos = 1; + string uri = args[argPos++]; + ostringstream result; + int type = -1; - if (strncmp(uri.c_str(), "/data/", 6) == 0) { - string circuit = "", name = ""; - size_t pos = uri.find('/', 6); - if (pos == string::npos) { - circuit = uri.substr(6); - } else { - circuit = uri.substr(6, pos-6); - name = uri.substr(pos+1); - } - time_t since = 0; - unsigned char pollPriority = 0; - bool exact = false; - if (args.size() > argPos) { - string query = args[argPos++]; - istringstream stream(query); - string token; - while (getline(stream, token, '&')) { - pos = token.find('='); - string qname, value; - if (pos != string::npos) { - qname = token.substr(0, pos); - value = token.substr(pos+1); - } else { - qname = token; - } - if (strcmp(qname.c_str(), "since") == 0) { - since = parseInt(value.c_str(), 10, 0, 0xffffffff, ret); - } else if (strcmp(qname.c_str(), "poll") == 0) { - pollPriority = (unsigned char)parseInt(value.c_str(), 10, 1, 9, ret); - } else if (strcmp(qname.c_str(), "exact") == 0) { - exact = value.length() == 0 || strcmp(value.c_str(), "1") == 0; - } else if (strcmp(qname.c_str(), "verbose") == 0) { - if (value.length() == 0 || strcmp(value.c_str(), "1") == 0) { - verbosity |= OF_UNITS|OF_COMMENTS; - } - } else if (strcmp(qname.c_str(), "indexed") == 0) { - if (value.length() == 0 || strcmp(value.c_str(), "1") == 0) { - verbosity &= ~OF_NAMES; - } - } else if (strcmp(qname.c_str(), "numeric") == 0) { - numeric = value.length() == 0 || strcmp(value.c_str(), "1") == 0; - } else if (strcmp(qname.c_str(), "required") == 0) { - required = value.length() == 0 || strcmp(value.c_str(), "1") == 0; - } - if (ret != RESULT_OK) { - break; - } - } - } - deque messages = m_messages->findAll(circuit, name, exact, true, false, true); + if (strncmp(uri.c_str(), "/data/", 6) == 0) { + string circuit = "", name = ""; + size_t pos = uri.find('/', 6); + if (pos == string::npos) { + circuit = uri.substr(6); + } else { + circuit = uri.substr(6, pos-6); + name = uri.substr(pos+1); + } + time_t since = 0; + unsigned char pollPriority = 0; + bool exact = false; + if (args.size() > argPos) { + string query = args[argPos++]; + istringstream stream(query); + string token; + while (getline(stream, token, '&')) { + pos = token.find('='); + string qname, value; + if (pos != string::npos) { + qname = token.substr(0, pos); + value = token.substr(pos+1); + } else { + qname = token; + } + if (strcmp(qname.c_str(), "since") == 0) { + since = parseInt(value.c_str(), 10, 0, 0xffffffff, ret); + } else if (strcmp(qname.c_str(), "poll") == 0) { + pollPriority = (unsigned char)parseInt(value.c_str(), 10, 1, 9, ret); + } else if (strcmp(qname.c_str(), "exact") == 0) { + exact = value.length() == 0 || strcmp(value.c_str(), "1") == 0; + } else if (strcmp(qname.c_str(), "verbose") == 0) { + if (value.length() == 0 || strcmp(value.c_str(), "1") == 0) { + verbosity |= OF_UNITS|OF_COMMENTS; + } + } else if (strcmp(qname.c_str(), "indexed") == 0) { + if (value.length() == 0 || strcmp(value.c_str(), "1") == 0) { + verbosity &= ~OF_NAMES; + } + } else if (strcmp(qname.c_str(), "numeric") == 0) { + numeric = value.length() == 0 || strcmp(value.c_str(), "1") == 0; + } else if (strcmp(qname.c_str(), "required") == 0) { + required = value.length() == 0 || strcmp(value.c_str(), "1") == 0; + } + if (ret != RESULT_OK) { + break; + } + } + } + deque messages = m_messages->findAll(circuit, name, exact, true, false, true); - bool first = true; - result << "{"; - string lastCircuit = ""; - time_t maxLastUp = 0; - for (deque::iterator it = messages.begin(); ret == RESULT_OK && it != messages.end();) { - Message* message = *it++; - unsigned char dstAddress = message->getDstAddress(); - if (dstAddress == SYN) { - continue; - } - if (pollPriority > 0 && message->setPollPriority(pollPriority)) { - m_messages->addPollMessage(message); - } - time_t lastup = message->getLastUpdateTime(); - if (lastup == 0 && required) { - // read directly from bus - if (message->isPassive()) { - continue; // not possible to actively read this message - } - if (m_busHandler->readFromBus(message, "") != RESULT_OK) { - continue; - } - lastup = message->getLastUpdateTime(); - } else { - if (since > 0 && lastup <= since) { - continue; - } - if (lastup > maxLastUp) { - maxLastUp = lastup; - } - } - if (message->getCircuit() != lastCircuit) { - if (lastCircuit.length() > 0) { - result << "\n },"; - } - lastCircuit = message->getCircuit(); - result << "\n \"" << lastCircuit << "\": {"; - first = true; - } - if (first) { - first = false; - } else { - result << ","; - } - result << "\n \"" << message->getName() << "\": {"; - result << "\n \"lastup\": " << setw(0) << dec << static_cast(lastup); - if (lastup != 0) { - result << ",\n \"zz\": \"" << setfill('0') << setw(2) << hex << static_cast(dstAddress) << "\""; - size_t pos = (size_t)result.tellp(); - result << ",\n \"fields\": {"; - result_t dret = message->decodeLastData(result, verbosity|(numeric?OF_NUMERIC:0)|OF_JSON); - if (dret == RESULT_OK) { - result << "\n }"; - } else { - string prefix = result.str().substr(0, pos); - result.str(""); - result.clear(); // remove written fields - result << prefix << ",\n \"decodeerror\": \"" << getResultCode(dret) << "\""; - } - } - result << ",\n \"passive\": " << (message->isPassive() ? "true" : "false"); - result << ",\n \"write\": " << (message->isWrite() ? "true" : "false"); - result << "\n }"; - } + bool first = true; + result << "{"; + string lastCircuit = ""; + time_t maxLastUp = 0; + for (deque::iterator it = messages.begin(); ret == RESULT_OK && it != messages.end();) { + Message* message = *it++; + unsigned char dstAddress = message->getDstAddress(); + if (dstAddress == SYN) { + continue; + } + if (pollPriority > 0 && message->setPollPriority(pollPriority)) { + m_messages->addPollMessage(message); + } + time_t lastup = message->getLastUpdateTime(); + if (lastup == 0 && required) { + // read directly from bus + if (message->isPassive()) { + continue; // not possible to actively read this message + } + if (m_busHandler->readFromBus(message, "") != RESULT_OK) { + continue; + } + lastup = message->getLastUpdateTime(); + } else { + if (since > 0 && lastup <= since) { + continue; + } + if (lastup > maxLastUp) { + maxLastUp = lastup; + } + } + if (message->getCircuit() != lastCircuit) { + if (lastCircuit.length() > 0) { + result << "\n },"; + } + lastCircuit = message->getCircuit(); + result << "\n \"" << lastCircuit << "\": {"; + first = true; + } + if (first) { + first = false; + } else { + result << ","; + } + result << "\n \"" << message->getName() << "\": {"; + result << "\n \"lastup\": " << setw(0) << dec << static_cast(lastup); + if (lastup != 0) { + result << ",\n \"zz\": \"" << setfill('0') << setw(2) << hex << static_cast(dstAddress) << "\""; + size_t pos = (size_t)result.tellp(); + result << ",\n \"fields\": {"; + result_t dret = message->decodeLastData(result, verbosity|(numeric?OF_NUMERIC:0)|OF_JSON); + if (dret == RESULT_OK) { + result << "\n }"; + } else { + string prefix = result.str().substr(0, pos); + result.str(""); + result.clear(); // remove written fields + result << prefix << ",\n \"decodeerror\": \"" << getResultCode(dret) << "\""; + } + } + result << ",\n \"passive\": " << (message->isPassive() ? "true" : "false"); + result << ",\n \"write\": " << (message->isWrite() ? "true" : "false"); + result << "\n }"; + } - if (ret == RESULT_OK) { - if (lastCircuit.length() > 0) { - result << "\n },"; - } - result << "\n \"global\": {"; - result << "\n \"signal\": " << (m_busHandler->hasSignal() ? "1" : "0"); - result << ",\n \"lastup\": " << setw(0) << dec << static_cast(maxLastUp); - result << "\n }"; - result << "\n}"; - type = 6; - } - } else { - if (uri.length() < 1 || uri[0] != '/' || uri.find("//") != string::npos || uri.find("..") != string::npos) { - ret = RESULT_ERR_INVALID_ARG; - } else { - string filename = m_htmlPath+uri; - if (uri[uri.length()-1] == '/') { - filename += "index.html"; - } - size_t pos = filename.find_last_of('.'); - if (pos != string::npos && pos != filename.length() - 1 && pos >= filename.length() - 5) { - string ext = filename.substr(pos+1); - if (ext == "html") { - type = 0; - } else if (ext == "css") { - type = 1; - } else if (ext == "js") { - type = 2; - } else if (ext == "png") { - type = 3; - } else if (ext == "jpg" || ext == "jpeg") { - type = 4; - } else if (ext == "svg") { - type = 5; - } else if (ext == "json") { - type = 6; - } - } - if (type < 0) { - ret = RESULT_ERR_NOTFOUND; - } else { - ifstream ifs; - ifs.open(filename.c_str(), ifstream::in | ifstream::binary); - if (!ifs.is_open()) { - ret = RESULT_ERR_NOTFOUND; - } else { - ifs >> result.rdbuf(); - ifs.close(); - } - } - } - } + if (ret == RESULT_OK) { + if (lastCircuit.length() > 0) { + result << "\n },"; + } + result << "\n \"global\": {"; + result << "\n \"signal\": " << (m_busHandler->hasSignal() ? "1" : "0"); + result << ",\n \"lastup\": " << setw(0) << dec << static_cast(maxLastUp); + result << "\n }"; + result << "\n}"; + type = 6; + } + } else { + if (uri.length() < 1 || uri[0] != '/' || uri.find("//") != string::npos || uri.find("..") != string::npos) { + ret = RESULT_ERR_INVALID_ARG; + } else { + string filename = m_htmlPath+uri; + if (uri[uri.length()-1] == '/') { + filename += "index.html"; + } + size_t pos = filename.find_last_of('.'); + if (pos != string::npos && pos != filename.length() - 1 && pos >= filename.length() - 5) { + string ext = filename.substr(pos+1); + if (ext == "html") { + type = 0; + } else if (ext == "css") { + type = 1; + } else if (ext == "js") { + type = 2; + } else if (ext == "png") { + type = 3; + } else if (ext == "jpg" || ext == "jpeg") { + type = 4; + } else if (ext == "svg") { + type = 5; + } else if (ext == "json") { + type = 6; + } + } + if (type < 0) { + ret = RESULT_ERR_NOTFOUND; + } else { + ifstream ifs; + ifs.open(filename.c_str(), ifstream::in | ifstream::binary); + if (!ifs.is_open()) { + ret = RESULT_ERR_NOTFOUND; + } else { + ifs >> result.rdbuf(); + ifs.close(); + } + } + } + } - string data = ret == RESULT_OK ? result.str() : ""; - result.str(""); - result.clear(); - result << "HTTP/1.0 "; - switch (ret) { - case RESULT_OK: - result << "200 OK\r\nContent-Type: "; - switch (type) { - case 1: - result << "text/css"; - break; - case 2: - result << "application/javascript"; - break; - case 3: - result << "image/png"; - break; - case 4: - result << "image/jpeg"; - break; - case 5: - result << "image/svg+xml"; - break; - case 6: - result << "application/json;charset=utf-8"; - break; - default: - result << "text/html"; - break; - } - result << "\r\nContent-Length: " << setw(0) << dec << static_cast(data.length()); - break; - case RESULT_ERR_NOTFOUND: - result << "404 Not Found"; - break; - case RESULT_ERR_INVALID_ARG: - case RESULT_ERR_INVALID_NUM: - case RESULT_ERR_OUT_OF_RANGE: - result << "400 Bad Request"; - break; - default: - result << "500 Internal Server Error"; - break; - } - result << "\r\nServer: " PACKAGE_NAME "/" PACKAGE_VERSION "\r\n\r\n"; - result << data; - connected = false; - return result.str(); + string data = ret == RESULT_OK ? result.str() : ""; + result.str(""); + result.clear(); + result << "HTTP/1.0 "; + switch (ret) { + case RESULT_OK: + result << "200 OK\r\nContent-Type: "; + switch (type) { + case 1: + result << "text/css"; + break; + case 2: + result << "application/javascript"; + break; + case 3: + result << "image/png"; + break; + case 4: + result << "image/jpeg"; + break; + case 5: + result << "image/svg+xml"; + break; + case 6: + result << "application/json;charset=utf-8"; + break; + default: + result << "text/html"; + break; + } + result << "\r\nContent-Length: " << setw(0) << dec << static_cast(data.length()); + break; + case RESULT_ERR_NOTFOUND: + result << "404 Not Found"; + break; + case RESULT_ERR_INVALID_ARG: + case RESULT_ERR_INVALID_NUM: + case RESULT_ERR_OUT_OF_RANGE: + result << "400 Bad Request"; + break; + default: + result << "500 Internal Server Error"; + break; + } + result << "\r\nServer: " PACKAGE_NAME "/" PACKAGE_VERSION "\r\n\r\n"; + result << data; + connected = false; + return result.str(); } } // namespace ebusd diff --git a/src/ebusd/mainloop.h b/src/ebusd/mainloop.h index 237ac0e3..37218185 100644 --- a/src/ebusd/mainloop.h +++ b/src/ebusd/mainloop.h @@ -36,220 +36,220 @@ namespace ebusd { * The main loop handling requests from connected clients. */ class MainLoop : public Thread, DeviceListener { - public: - /** - * Construct the main loop and create network and bus handling components. - * @param opt the program options. - * @param device the @a Device instance. - * @param messages the @a MessageMap instance. - */ - MainLoop(const struct options opt, Device *device, MessageMap* messages); + public: + /** + * Construct the main loop and create network and bus handling components. + * @param opt the program options. + * @param device the @a Device instance. + * @param messages the @a MessageMap instance. + */ + MainLoop(const struct options opt, Device *device, MessageMap* messages); - /** - * Destructor. - */ - ~MainLoop(); + /** + * Destructor. + */ + ~MainLoop(); - /** - * Get the @a BusHandler instance. - * @return the created @a BusHandler instance. - */ - BusHandler* getBusHandler() { return m_busHandler; } + /** + * Get the @a BusHandler instance. + * @return the created @a BusHandler instance. + */ + BusHandler* getBusHandler() { return m_busHandler; } - /** - * Add a client @a NetMessage to the queue. - * @param message the client @a NetMessage to handle. - */ - void addMessage(NetMessage* message) { m_netQueue.push(message); } + /** + * Add a client @a NetMessage to the queue. + * @param message the client @a NetMessage to handle. + */ + void addMessage(NetMessage* message) { m_netQueue.push(message); } - // @copydoc - virtual void notifyDeviceData(const unsigned char byte, bool received); + // @copydoc + virtual void notifyDeviceData(const unsigned char byte, bool received); - protected: - // @copydoc - virtual void run(); + protected: + // @copydoc + virtual void run(); - private: - /** - * Decode and execute client message. - * @param data the data string to decode (may be empty). - * @param connected set to false when the client connection shall be closed. - * @param isHttp true for HTTP message. - * @param listening set to true when the client is in listening mode. - * @param reload set to true when the configuration files were reloaded. - * @return result string to send back to the client. - */ - string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload); + private: + /** + * Decode and execute client message. + * @param data the data string to decode (may be empty). + * @param connected set to false when the client connection shall be closed. + * @param isHttp true for HTTP message. + * @param listening set to true when the client is in listening mode. + * @param reload set to true when the configuration files were reloaded. + * @return result string to send back to the client. + */ + string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload); - /** - * Parse the hex master message from the remaining arguments. - * @param args the arguments passed to the command. - * @param argPos the index of the first argument to parse. - * @param master the master @a SymbolString to write the data to. - * @return the result from parsing the arguments. - */ - result_t parseHexMaster(vector &args, size_t argPos, SymbolString& master); + /** + * Parse the hex master message from the remaining arguments. + * @param args the arguments passed to the command. + * @param argPos the index of the first argument to parse. + * @param master the master @a SymbolString to write the data to. + * @return the result from parsing the arguments. + */ + result_t parseHexMaster(vector &args, size_t argPos, SymbolString& master); - /** - * Execute the read command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeRead(vector &args); + /** + * Execute the read command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeRead(vector &args); - /** - * Execute the write command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeWrite(vector &args); + /** + * Execute the write command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeWrite(vector &args); - /** - * Execute the hex command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeHex(vector &args); + /** + * Execute the hex command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeHex(vector &args); - /** - * Execute the find command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeFind(vector &args); + /** + * Execute the find command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeFind(vector &args); - /** - * Execute the listen command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @param listening set to true when the client is in listening mode. - * @return the result string. - */ - string executeListen(vector &args, bool& listening); + /** + * Execute the listen command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param listening set to true when the client is in listening mode. + * @return the result string. + */ + string executeListen(vector &args, bool& listening); - /** - * Execute the state command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeState(vector &args); + /** + * Execute the state command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeState(vector &args); - /** - * Execute the grab command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeGrab(vector &args); + /** + * Execute the grab command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeGrab(vector &args); - /** - * Execute the scan command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeScan(vector &args); + /** + * Execute the scan command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeScan(vector &args); - /** - * Execute the log command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeLog(vector &args); + /** + * Execute the log command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeLog(vector &args); - /** - * Execute the raw command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeRaw(vector &args); + /** + * Execute the raw command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeRaw(vector &args); - /** - * Execute the dump command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeDump(vector &args); + /** + * Execute the dump command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeDump(vector &args); - /** - * Execute the reload command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeReload(vector &args); + /** + * Execute the reload command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeReload(vector &args); - /** - * Execute the info command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @return the result string. - */ - string executeInfo(vector &args); + /** + * Execute the info command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @return the result string. + */ + string executeInfo(vector &args); - /** - * Execute the quit command. - * @param args the arguments passed to the command (starting with the command itself), or empty for help. - * @param connected set to false when the client connection shall be closed. - * @return the result string. - */ - string executeQuit(vector &args, bool& connected); + /** + * Execute the quit command. + * @param args the arguments passed to the command (starting with the command itself), or empty for help. + * @param connected set to false when the client connection shall be closed. + * @return the result string. + */ + string executeQuit(vector &args, bool& connected); - /** - * Execute the help command. - * @return the result string. - */ - string executeHelp(); + /** + * Execute the help command. + * @return the result string. + */ + string executeHelp(); - /** - * Execute the HTTP GET command. - * @param args the arguments passed to the command (starting with the command itself). - * @param connected set to false when the client connection shall be closed. - * @return the result string. - */ - string executeGet(vector &args, bool& connected); + /** + * Execute the HTTP GET command. + * @param args the arguments passed to the command (starting with the command itself). + * @param connected set to false when the client connection shall be closed. + * @return the result string. + */ + string executeGet(vector &args, bool& connected); - /** the @a Device instance. */ - Device* m_device; + /** the @a Device instance. */ + Device* m_device; - /** the number of reconnects requested from the @a Device. */ - unsigned int m_reconnectCount; + /** the number of reconnects requested from the @a Device. */ + unsigned int m_reconnectCount; - /** the @a RotateFile for writing sent/received bytes in log format, or NULL. */ - RotateFile* m_logRawFile; + /** the @a RotateFile for writing sent/received bytes in log format, or NULL. */ + RotateFile* m_logRawFile; - /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */ - bool m_logRawEnabled; + /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */ + bool m_logRawEnabled; - /** the @a RotateFile for dumping received data, or NULL. */ - RotateFile* m_dumpFile; + /** the @a RotateFile for dumping received data, or NULL. */ + RotateFile* m_dumpFile; - /** the @a MessageMap instance. */ - MessageMap* m_messages; + /** the @a MessageMap instance. */ + MessageMap* m_messages; - /** the own master address for sending on the bus. */ - const unsigned char m_address; + /** the own master address for sending on the bus. */ + const unsigned char m_address; - /** whether to pick configuration files matching initial scan. */ - const bool m_scanConfig; + /** whether to pick configuration files matching initial scan. */ + const bool m_scanConfig; - /** the initial address to scan for @a m_scanConfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */ - const unsigned char m_initialScan; + /** the initial address to scan for @a m_scanConfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */ + const unsigned char m_initialScan; - /** whether to enable the hex command. */ - const bool m_enableHex; + /** whether to enable the hex command. */ + const bool m_enableHex; - /** the created @a BusHandler instance. */ - BusHandler* m_busHandler; + /** the created @a BusHandler instance. */ + BusHandler* m_busHandler; - /** the created @a Network instance. */ - Network* m_network; + /** the created @a Network instance. */ + Network* m_network; - /** the @a NetMessage @a Queue. */ - Queue m_netQueue; + /** the @a NetMessage @a Queue. */ + Queue m_netQueue; - /** the path for HTML files served by the HTTP port. */ - string m_htmlPath; + /** the path for HTML files served by the HTTP port. */ + string m_htmlPath; - /** the registered @a DataHandler instances. */ - list m_dataHandlers; + /** the registered @a DataHandler instances. */ + list m_dataHandlers; }; } // namespace ebusd diff --git a/src/ebusd/mqtthandler.cpp b/src/ebusd/mqtthandler.cpp index 1b6e4725..0130d4fe 100644 --- a/src/ebusd/mqtthandler.cpp +++ b/src/ebusd/mqtthandler.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "mqtthandler.h" @@ -29,12 +29,12 @@ using std::dec; /** the definition of the MQTT arguments. */ static const struct argp_option g_mqtt_argp_options[] = { - {NULL, 0, NULL, 0, "MQTT options:", 1 }, - {"mqtthost", 1, "HOST", 0, "Connect to MQTT broker on HOST [localhost]", 0 }, - {"mqttport", 2, "PORT", 0, "Connect to MQTT broker on PORT (usually 1883), 0 to disable [0]", 0 }, - {"mqtttopic", 3, "TOPIC", 0, "Use MQTT TOPIC (prefix before /%circuit/%name or complete format) [ebusd]", 0 }, + {NULL, 0, NULL, 0, "MQTT options:", 1 }, + {"mqtthost", 1, "HOST", 0, "Connect to MQTT broker on HOST [localhost]", 0 }, + {"mqttport", 2, "PORT", 0, "Connect to MQTT broker on PORT (usually 1883), 0 to disable [0]", 0 }, + {"mqtttopic", 3, "TOPIC", 0, "Use MQTT TOPIC (prefix before /%circuit/%name or complete format) [ebusd]", 0 }, - {NULL, 0, NULL, 0, NULL, 0 }, + {NULL, 0, NULL, 0, NULL, 0 }, }; static const char* g_host = "localhost"; //!< MQTT Host to use [localhost] @@ -49,61 +49,61 @@ static const char* g_topic = PACKAGE; //!< MQTT topic to use (prefix if without * @param state the parsing state. */ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) { - result_t result = RESULT_OK; + result_t result = RESULT_OK; - switch (key) { - case 1: // --mqtthost=localhost - if (arg == NULL || arg[0] == 0) { - argp_error(state, "invalid mqtthost"); - return EINVAL; - } - g_host = arg; - break; + switch (key) { + case 1: // --mqtthost=localhost + if (arg == NULL || arg[0] == 0) { + argp_error(state, "invalid mqtthost"); + return EINVAL; + } + g_host = arg; + break; - case 2: // --mqttport=1883 - g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result); - if (result != RESULT_OK) { - argp_error(state, "invalid mqttport"); - return EINVAL; - } - break; + case 2: // --mqttport=1883 + g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result); + if (result != RESULT_OK) { + argp_error(state, "invalid mqttport"); + return EINVAL; + } + break; - case 3: // --mqtttopic=ebusd - if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') { - argp_error(state, "invalid mqtttopic"); - return EINVAL; - } - g_topic = arg; - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + case 3: // --mqtttopic=ebusd + if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') { + argp_error(state, "invalid mqtttopic"); + return EINVAL; + } + g_topic = arg; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } static const struct argp g_mqtt_argp = { g_mqtt_argp_options, mqtt_parse_opt, NULL, NULL, NULL, NULL, NULL }; static const struct argp_child g_mqtt_argp_child = {&g_mqtt_argp, 0, "", 1}; const struct argp_child* mqtthandler_getargs() { - return &g_mqtt_argp_child; + return &g_mqtt_argp_child; } DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages) { - return new MqttHandler(busHandler, messages); + return new MqttHandler(busHandler, messages); } /** the known topic column names. */ static const char* columnNames[] = { - "circuit", - "name", - "field", + "circuit", + "name", + "field", }; /** the known topic column IDs. */ static const size_t columnIds[] = { - COLUMN_CIRCUIT, - COLUMN_NAME, - COLUMN_FIELDS, + COLUMN_CIRCUIT, + COLUMN_NAME, + COLUMN_FIELDS, }; /** the number of known column names. */ @@ -118,344 +118,344 @@ static const size_t columnCount = sizeof(columnNames) / sizeof(char*); * @return true on success, false on malformed topic template. */ bool parseTopic(const string topic, vector &strs, vector &cols) { - size_t lastpos = 0; - size_t end = topic.length(); - vector columns; - for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) { - size_t col = columnCount; - size_t len = 0; - for (size_t i = 0; i < columnCount; i++) { - len = strlen(columnNames[i]); - if (topic.substr(pos+1, len) == columnNames[i]) { - col = columnIds[i]; - break; - } - } - if (col == columnCount) { - return false; - } - for (vector::iterator it=cols.begin(); it != cols.end(); it++) { - if (*it == col) { - return false; // duplicate column - } - } - strs.push_back(topic.substr(lastpos, pos-lastpos)); - cols.push_back(col); - lastpos = pos+1+len; - pos = topic.find('%', lastpos); - } - if (lastpos < end) { - strs.push_back(topic.substr(lastpos, end-lastpos)); - } - return true; + size_t lastpos = 0; + size_t end = topic.length(); + vector columns; + for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) { + size_t col = columnCount; + size_t len = 0; + for (size_t i = 0; i < columnCount; i++) { + len = strlen(columnNames[i]); + if (topic.substr(pos+1, len) == columnNames[i]) { + col = columnIds[i]; + break; + } + } + if (col == columnCount) { + return false; + } + for (vector::iterator it=cols.begin(); it != cols.end(); it++) { + if (*it == col) { + return false; // duplicate column + } + } + strs.push_back(topic.substr(lastpos, pos-lastpos)); + cols.push_back(col); + lastpos = pos+1+len; + pos = topic.find('%', lastpos); + } + if (lastpos < end) { + strs.push_back(topic.substr(lastpos, end-lastpos)); + } + return true; } MqttHandler::MqttHandler(BusHandler* busHandler, MessageMap* messages) - : DataSink(), DataSource(busHandler), Thread(), m_messages(messages) { - bool enabled = g_port != 0; - m_publishByField = false; - m_mosquitto = NULL; - if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) { - logOtherError("mqtt", "malformed topic %s", g_topic); - return; - } - if (!enabled) { - return; - } - int major = -1; - mosquitto_lib_version(&major, NULL, NULL); - if (major != LIBMOSQUITTO_MAJOR) { - logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR); - return; - } - if (m_topicCols.empty()) { - if (m_topicStrs.empty()) { - m_topicStrs.push_back(""); - } else { - string str = m_topicStrs[0]; - if (str.empty() || str[str.length()-1] != '/') { - m_topicStrs[0] = str+"/"; - } - } - m_topicCols.push_back(COLUMN_CIRCUIT); // circuit - m_topicStrs.push_back("/"); - m_topicCols.push_back(COLUMN_NAME); // name - } else { - for (size_t i = 0; i < m_topicCols.size(); i++) { - if (m_topicCols[i] == COLUMN_FIELDS) { // fields - m_publishByField = true; - break; - } - } - } - m_globalTopic = getTopic(NULL)+"global/"; - m_mosquitto = NULL; - if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) { - logOtherError("mqtt", "unable to initialize"); - } else { - string clientId = PACKAGE_STRING; - clientId += " "+static_cast(getpid()); - m_mosquitto = mosquitto_new(clientId.c_str(), + : DataSink(), DataSource(busHandler), Thread(), m_messages(messages) { + bool enabled = g_port != 0; + m_publishByField = false; + m_mosquitto = NULL; + if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) { + logOtherError("mqtt", "malformed topic %s", g_topic); + return; + } + if (!enabled) { + return; + } + int major = -1; + mosquitto_lib_version(&major, NULL, NULL); + if (major != LIBMOSQUITTO_MAJOR) { + logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR); + return; + } + if (m_topicCols.empty()) { + if (m_topicStrs.empty()) { + m_topicStrs.push_back(""); + } else { + string str = m_topicStrs[0]; + if (str.empty() || str[str.length()-1] != '/') { + m_topicStrs[0] = str+"/"; + } + } + m_topicCols.push_back(COLUMN_CIRCUIT); // circuit + m_topicStrs.push_back("/"); + m_topicCols.push_back(COLUMN_NAME); // name + } else { + for (size_t i = 0; i < m_topicCols.size(); i++) { + if (m_topicCols[i] == COLUMN_FIELDS) { // fields + m_publishByField = true; + break; + } + } + } + m_globalTopic = getTopic(NULL)+"global/"; + m_mosquitto = NULL; + if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) { + logOtherError("mqtt", "unable to initialize"); + } else { + string clientId = PACKAGE_STRING; + clientId += " "+static_cast(getpid()); + m_mosquitto = mosquitto_new(clientId.c_str(), #if (LIBMOSQUITTO_MAJOR >= 1) - true, + true, #endif - this); - if (!m_mosquitto) { - logOtherError("mqtt", "unable to instantiate"); - } - } - if (m_mosquitto) { - /*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING - | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/ - string willTopic = m_globalTopic+"running"; - string willData = "false"; - size_t len = willData.length(); - mosquitto_will_set(m_mosquitto, + this); + if (!m_mosquitto) { + logOtherError("mqtt", "unable to instantiate"); + } + } + if (m_mosquitto) { + /*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING + | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/ + string willTopic = m_globalTopic+"running"; + string willData = "false"; + size_t len = willData.length(); + mosquitto_will_set(m_mosquitto, #if (LIBMOSQUITTO_MAJOR < 1) - true, + true, #endif - willTopic.c_str(), (uint32_t)len, reinterpret_cast(willData.c_str()), 0, true); - if (mosquitto_connect(m_mosquitto, g_host, g_port, 60 + willTopic.c_str(), (uint32_t)len, reinterpret_cast(willData.c_str()), 0, true); + if (mosquitto_connect(m_mosquitto, g_host, g_port, 60 #if (LIBMOSQUITTO_MAJOR < 1) - , true + , true #endif - ) != MOSQ_ERR_SUCCESS) { - logOtherError("mqtt", "unable to connect"); - mosquitto_destroy(m_mosquitto); - m_mosquitto = NULL; - } else { - logOtherNotice("mqtt", "connection established"); - } - } + ) != MOSQ_ERR_SUCCESS) { + logOtherError("mqtt", "unable to connect"); + mosquitto_destroy(m_mosquitto); + m_mosquitto = NULL; + } else { + logOtherNotice("mqtt", "connection established"); + } + } } MqttHandler::~MqttHandler() { - join(); - if (m_mosquitto) { - mosquitto_destroy(m_mosquitto); - m_mosquitto = NULL; - } - mosquitto_lib_cleanup(); + join(); + if (m_mosquitto) { + mosquitto_destroy(m_mosquitto); + m_mosquitto = NULL; + } + mosquitto_lib_cleanup(); } void MqttHandler::start() { - if (m_mosquitto) { - Thread::start("MQTT"); - } + if (m_mosquitto) { + Thread::start("MQTT"); + } } void on_message( #if (LIBMOSQUITTO_MAJOR >= 1) - struct mosquitto *mosq, + struct mosquitto *mosq, #endif - void *obj, const struct mosquitto_message *message) { - MqttHandler* handler = reinterpret_cast(obj); - if (!handler || !message || !handler->isRunning()) { - return; - } - string topic(message->topic); - string data(message->payloadlen > 0 ? reinterpret_cast(message->payload) : ""); - handler->notifyTopic(topic, data); + void *obj, const struct mosquitto_message *message) { + MqttHandler* handler = reinterpret_cast(obj); + if (!handler || !message || !handler->isRunning()) { + return; + } + string topic(message->topic); + string data(message->payloadlen > 0 ? reinterpret_cast(message->payload) : ""); + handler->notifyTopic(topic, data); } void MqttHandler::notifyTopic(string topic, string data) { - size_t pos = topic.rfind('/'); - if (pos == string::npos) { - return; - } - string suffix = topic.substr(pos+1); - bool isWrite = false; - if (suffix.empty()) { - return; - } - string direction = suffix.substr(0, 3); - isWrite = direction == "set"; - if (!isWrite && direction != "get") { - return; - } - suffix = suffix.substr(3); // security level - logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str()); - string remain = topic.substr(0, pos); - size_t last = 0; - string circuit, name; - size_t idx; - for (idx = 0; idx < m_topicStrs.size()+1; idx++) { - string field; - string chk; - if (idx < m_topicStrs.size()) { - chk = m_topicStrs[idx]; - pos = remain.find(chk, last); - if (pos == string::npos) { - return; - } - } else if (idx-1 < m_topicCols.size()) { - pos = remain.size(); - } else if (last < remain.size()) { - return; - } else { - break; - } - field = remain.substr(last, pos-last); - last = pos+chk.size(); - if (idx == 0) { - if (pos > 0) { - return; - } - } else { - if (field.empty()) { - return; - } - switch (m_topicCols[idx-1]) { - case COLUMN_CIRCUIT: - circuit = field; - break; - case COLUMN_NAME: - name = field; - break; - case COLUMN_FIELDS: - //field = field; // TODO add support for writing a single field - break; - default: - return; - } - } - } - if (circuit.empty() || name.empty()) { - return; - } - logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str()); - if (suffix.length() > 0) { - circuit += "#"+suffix; - } - Message* message = m_messages->find(circuit, name, isWrite); - if (message == NULL) { - message = m_messages->find(circuit, name, isWrite, true); - } - if (message == NULL) { - logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str()); - return; - } - if (!message->isPassive()) { - result_t result = m_busHandler->readFromBus(message, data); - if (result != RESULT_OK) { - logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result)); - return; - } - logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str()); - } - ostringstream ostream; - publishMessage(message, ostream); + size_t pos = topic.rfind('/'); + if (pos == string::npos) { + return; + } + string suffix = topic.substr(pos+1); + bool isWrite = false; + if (suffix.empty()) { + return; + } + string direction = suffix.substr(0, 3); + isWrite = direction == "set"; + if (!isWrite && direction != "get") { + return; + } + suffix = suffix.substr(3); // security level + logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str()); + string remain = topic.substr(0, pos); + size_t last = 0; + string circuit, name; + size_t idx; + for (idx = 0; idx < m_topicStrs.size()+1; idx++) { + string field; + string chk; + if (idx < m_topicStrs.size()) { + chk = m_topicStrs[idx]; + pos = remain.find(chk, last); + if (pos == string::npos) { + return; + } + } else if (idx-1 < m_topicCols.size()) { + pos = remain.size(); + } else if (last < remain.size()) { + return; + } else { + break; + } + field = remain.substr(last, pos-last); + last = pos+chk.size(); + if (idx == 0) { + if (pos > 0) { + return; + } + } else { + if (field.empty()) { + return; + } + switch (m_topicCols[idx-1]) { + case COLUMN_CIRCUIT: + circuit = field; + break; + case COLUMN_NAME: + name = field; + break; + case COLUMN_FIELDS: + //field = field; // TODO add support for writing a single field + break; + default: + return; + } + } + } + if (circuit.empty() || name.empty()) { + return; + } + logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str()); + if (suffix.length() > 0) { + circuit += "#"+suffix; + } + Message* message = m_messages->find(circuit, name, isWrite); + if (message == NULL) { + message = m_messages->find(circuit, name, isWrite, true); + } + if (message == NULL) { + logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str()); + return; + } + if (!message->isPassive()) { + result_t result = m_busHandler->readFromBus(message, data); + if (result != RESULT_OK) { + logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result)); + return; + } + logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str()); + } + ostringstream ostream; + publishMessage(message, ostream); } void MqttHandler::run() { - time_t lastTaskRun, now, start, lastSignal = 0; - bool signal = false; - string signalTopic = m_globalTopic+"signal"; - string uptimeTopic = m_globalTopic+"uptime"; - ostringstream updates; + time_t lastTaskRun, now, start, lastSignal = 0; + bool signal = false; + string signalTopic = m_globalTopic+"signal"; + string uptimeTopic = m_globalTopic+"uptime"; + ostringstream updates; - time(&now); - start = lastTaskRun = now; - publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION); - publishTopic(m_globalTopic+"running", "true"); - publishTopic(signalTopic, "false"); - mosquitto_message_callback_set(m_mosquitto, on_message); - string subTopic = getTopic(NULL)+"#"; - mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0); - while (isRunning()) { - handleTraffic(); - time(&now); - if (now < start) { - // clock skew - if (now < lastSignal) { - lastSignal -= lastTaskRun-now; - } - lastTaskRun = now; - } else if (now > lastTaskRun+15) { - if (m_busHandler->hasSignal()) { - lastSignal = now; - if (!signal) { - signal = true; - publishTopic(signalTopic, "true"); - } - } else { - if (signal) { - signal = false; - publishTopic(signalTopic, "false"); - } - } - time_t uptime = now-start; - updates.str(""); - updates.clear(); - updates << dec << static_cast(uptime); - publishTopic(uptimeTopic, updates.str()); - time(&lastTaskRun); - } - if (!m_updatedMessages.empty()) { - for (map::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) { - Message* message = it->first; - updates.str(""); - updates.clear(); - updates << dec; - publishMessage(message, updates); - } - m_updatedMessages.clear(); - } - } + time(&now); + start = lastTaskRun = now; + publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION); + publishTopic(m_globalTopic+"running", "true"); + publishTopic(signalTopic, "false"); + mosquitto_message_callback_set(m_mosquitto, on_message); + string subTopic = getTopic(NULL)+"#"; + mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0); + while (isRunning()) { + handleTraffic(); + time(&now); + if (now < start) { + // clock skew + if (now < lastSignal) { + lastSignal -= lastTaskRun-now; + } + lastTaskRun = now; + } else if (now > lastTaskRun+15) { + if (m_busHandler->hasSignal()) { + lastSignal = now; + if (!signal) { + signal = true; + publishTopic(signalTopic, "true"); + } + } else { + if (signal) { + signal = false; + publishTopic(signalTopic, "false"); + } + } + time_t uptime = now-start; + updates.str(""); + updates.clear(); + updates << dec << static_cast(uptime); + publishTopic(uptimeTopic, updates.str()); + time(&lastTaskRun); + } + if (!m_updatedMessages.empty()) { + for (map::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) { + Message* message = it->first; + updates.str(""); + updates.clear(); + updates << dec; + publishMessage(message, updates); + } + m_updatedMessages.clear(); + } + } } void MqttHandler::handleTraffic() { - if (m_mosquitto) { - mosquitto_loop(m_mosquitto, -1 + if (m_mosquitto) { + mosquitto_loop(m_mosquitto, -1 #if (LIBMOSQUITTO_MAJOR >= 1) - , 1 + , 1 #endif - ); - } + ); + } } string MqttHandler::getTopic(Message* message, signed char fieldIndex) { - ostringstream ret; - for (size_t i = 0; i < m_topicStrs.size(); i++) { - ret << m_topicStrs[i]; - if (!message) { - break; - } - if (i < m_topicCols.size()) { - if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) { - ret << message->getFieldName(fieldIndex); - } else { - message->dumpColumn(ret, m_topicCols[i]); - } - } - } - return ret.str(); + ostringstream ret; + for (size_t i = 0; i < m_topicStrs.size(); i++) { + ret << m_topicStrs[i]; + if (!message) { + break; + } + if (i < m_topicCols.size()) { + if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) { + ret << message->getFieldName(fieldIndex); + } else { + message->dumpColumn(ret, m_topicCols[i]); + } + } + } + return ret.str(); } void MqttHandler::publishMessage(Message* message, ostringstream& updates) { - result_t result = message->decodeLastData(updates); - if (result != RESULT_OK) { - logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result)); - return; - } - if (m_publishByField) { - signed char index = 0; - istringstream input(updates.str()); - string token; - while (getline(input, token, UI_FIELD_SEPARATOR)) { - string topic = getTopic(message, index); - publishTopic(topic, token); - index++; - } - } else { - publishTopic(getTopic(message), updates.str()); - } + result_t result = message->decodeLastData(updates); + if (result != RESULT_OK) { + logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result)); + return; + } + if (m_publishByField) { + signed char index = 0; + istringstream input(updates.str()); + string token; + while (getline(input, token, UI_FIELD_SEPARATOR)) { + string topic = getTopic(message, index); + publishTopic(topic, token); + index++; + } + } else { + publishTopic(getTopic(message), updates.str()); + } } void MqttHandler::publishTopic(string topic, string data, bool retain) { - logOtherDebug("mqtt", "publish %s %s", topic.c_str(), data.c_str()); - mosquitto_publish(m_mosquitto, NULL, topic.c_str(), (uint32_t)data.size(), reinterpret_cast(data.c_str()), 0, retain); + logOtherDebug("mqtt", "publish %s %s", topic.c_str(), data.c_str()); + mosquitto_publish(m_mosquitto, NULL, topic.c_str(), (uint32_t)data.size(), reinterpret_cast(data.c_str()), 0, retain); } } // namespace ebusd diff --git a/src/ebusd/mqtthandler.h b/src/ebusd/mqtthandler.h index 3e26d73b..c6e41cd7 100644 --- a/src/ebusd/mqtthandler.h +++ b/src/ebusd/mqtthandler.h @@ -55,81 +55,81 @@ DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages); * The main class supporting MQTT data handling. */ class MqttHandler : public DataSink, public DataSource, public Thread { - public: - /** - * Constructor. - * @param busHandler the @a BusHandler instance. - * @param messages the @a MessageMap instance. - */ - MqttHandler(BusHandler* busHandler, MessageMap* messages); + public: + /** + * Constructor. + * @param busHandler the @a BusHandler instance. + * @param messages the @a MessageMap instance. + */ + MqttHandler(BusHandler* busHandler, MessageMap* messages); - /** - * Destructor. - */ - virtual ~MqttHandler(); + /** + * Destructor. + */ + virtual ~MqttHandler(); - // @copydoc - virtual void start(); + // @copydoc + virtual void start(); - /** - * Notify the handler of a received MQTT message. - * @param topic the topic string. - * @param data the data string. - */ - void notifyTopic(string topic, string data); + /** + * Notify the handler of a received MQTT message. + * @param topic the topic string. + * @param data the data string. + */ + void notifyTopic(string topic, string data); - protected: - // @copydoc - virtual void run(); + protected: + // @copydoc + virtual void run(); - private: - /** - * Called regularly to handle MQTT traffic. - */ - void handleTraffic(); + private: + /** + * Called regularly to handle MQTT traffic. + */ + void handleTraffic(); - /** - * Build the MQTT topic string for the @a Message. - * @param message the @a Message to build the topic string for. - * @param fieldIndex the optional field index for the field column, or -1. - * @return the topic string. - */ - string getTopic(Message* message, signed char fieldIndex = -1); + /** + * Build the MQTT topic string for the @a Message. + * @param message the @a Message to build the topic string for. + * @param fieldIndex the optional field index for the field column, or -1. + * @return the topic string. + */ + string getTopic(Message* message, signed char fieldIndex = -1); - /** - * Prepare a @a Message and publish as topic. - * @param message the @a Message to publish. - * @param updates the @a ostringstream for preparation. - */ - void publishMessage(Message* message, ostringstream& updates); + /** + * Prepare a @a Message and publish as topic. + * @param message the @a Message to publish. + * @param updates the @a ostringstream for preparation. + */ + void publishMessage(Message* message, ostringstream& updates); - /** - * Publish a topic update to MQTT. - * @param topic the topic string. - * @param data the data string. - * @param retain whether the topic shall be retained. - */ - void publishTopic(string topic, string data, bool retain = true); + /** + * Publish a topic update to MQTT. + * @param topic the topic string. + * @param data the data string. + * @param retain whether the topic shall be retained. + */ + void publishTopic(string topic, string data, bool retain = true); - /** the @a MessageMap instance. */ - MessageMap* m_messages; + /** the @a MessageMap instance. */ + MessageMap* m_messages; - /** the MQTT topic string parts. */ - vector m_topicStrs; + /** the MQTT topic string parts. */ + vector m_topicStrs; - /** the MQTT topic column parts. */ - vector m_topicCols; + /** the MQTT topic column parts. */ + vector m_topicCols; - /** the global topic prefix. */ - string m_globalTopic; + /** the global topic prefix. */ + string m_globalTopic; - /** whether to publish a separate topic for each message field. */ - bool m_publishByField; + /** whether to publish a separate topic for each message field. */ + bool m_publishByField; - /** the mosquitto structure if initialized, or NULL. */ - struct mosquitto* m_mosquitto; + /** the mosquitto structure if initialized, or NULL. */ + struct mosquitto* m_mosquitto; }; } // namespace ebusd diff --git a/src/ebusd/network.cpp b/src/ebusd/network.cpp index 9dd66995..040edbf2 100644 --- a/src/ebusd/network.cpp +++ b/src/ebusd/network.cpp @@ -17,12 +17,12 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "network.h" #ifdef HAVE_PPOLL -# include +# include #endif #include #include "log.h" @@ -32,271 +32,271 @@ namespace ebusd { int Connection::m_ids = 0; void Connection::run() { - int ret; - struct timespec tdiff; + int ret; + struct timespec tdiff; - // set timeout - tdiff.tv_sec = 2; - tdiff.tv_nsec = 0; - int notifyFD = m_notify.notifyFD(); - int sockFD = m_socket->getFD(); + // set timeout + tdiff.tv_sec = 2; + tdiff.tv_nsec = 0; + int notifyFD = m_notify.notifyFD(); + int sockFD = m_socket->getFD(); #ifdef HAVE_PPOLL - int nfds = 2; - struct pollfd fds[nfds]; + int nfds = 2; + struct pollfd fds[nfds]; - memset(fds, 0, sizeof(fds)); + memset(fds, 0, sizeof(fds)); - fds[0].fd = notifyFD; - fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; + fds[0].fd = notifyFD; + fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; - fds[1].fd = sockFD; - fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; + fds[1].fd = sockFD; + fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; #else #ifdef HAVE_PSELECT - int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD; - fd_set checkfds, exceptfds; + int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD; + fd_set checkfds, exceptfds; - FD_ZERO(&checkfds); - FD_SET(notifyFD, &checkfds); - FD_SET(sockFD, &checkfds); + FD_ZERO(&checkfds); + FD_SET(notifyFD, &checkfds); + FD_SET(sockFD, &checkfds); - FD_ZERO(&exceptfds); - FD_SET(notifyFD, &exceptfds); - FD_SET(sockFD, &exceptfds); + FD_ZERO(&exceptfds); + FD_SET(notifyFD, &exceptfds); + FD_SET(sockFD, &exceptfds); #endif #endif - bool closed = false; - NetMessage message(m_isHttp); + bool closed = false; + NetMessage message(m_isHttp); - while (!closed) { + while (!closed) { #ifdef HAVE_PPOLL - // wait for new fd event - ret = ppoll(fds, nfds, &tdiff, NULL); + // wait for new fd event + ret = ppoll(fds, nfds, &tdiff, NULL); #else #ifdef HAVE_PSELECT - // set readfds to inital checkfds - fd_set readfds = checkfds; - // wait for new fd event - ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL); + // set readfds to inital checkfds + fd_set readfds = checkfds; + // wait for new fd event + ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL); #endif #endif - bool newData = false; - if (ret != 0) { + bool newData = false; + if (ret != 0) { #ifdef HAVE_PPOLL - // new data from notify - if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) { - break; - } - // new data from socket - newData = fds[1].revents & POLLIN; - closed = fds[1].revents & POLLRDHUP; + // new data from notify + if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) { + break; + } + // new data from socket + newData = fds[1].revents & POLLIN; + closed = fds[1].revents & POLLRDHUP; #else #ifdef HAVE_PSELECT - // new data from notify - if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) { - break; - } - // new data from socket - newData = FD_ISSET(sockFD, &readfds); - closed = FD_ISSET(sockFD, &exceptfds); + // new data from notify + if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) { + break; + } + // new data from socket + newData = FD_ISSET(sockFD, &readfds); + closed = FD_ISSET(sockFD, &exceptfds); #endif #endif - } + } - if (newData || message.isListening()) { - char data[256]; + if (newData || message.isListening()) { + char data[256]; - if (!m_socket->isValid()) { - break; - } - if (newData) { - size_t datalen = m_socket->recv(data, sizeof(data)-1); + if (!m_socket->isValid()) { + break; + } + if (newData) { + size_t datalen = m_socket->recv(data, sizeof(data)-1); - // remove closed socket - if (datalen <= 0) { - break; - } - data[datalen] = '\0'; - } else { - data[0] = '\0'; - } + // remove closed socket + if (datalen <= 0) { + break; + } + data[datalen] = '\0'; + } else { + data[0] = '\0'; + } - // decode client data - if (message.add(data)) { - m_netQueue->push(&message); + // decode client data + if (message.add(data)) { + m_netQueue->push(&message); - // wait for result - logDebug(lf_network, "[%05d] wait for result", getID()); - string result = message.getResult(); + // wait for result + logDebug(lf_network, "[%05d] wait for result", getID()); + string result = message.getResult(); - if (!m_socket->isValid()) { - break; - } - m_socket->send(result.c_str(), result.size()); - } + if (!m_socket->isValid()) { + break; + } + m_socket->send(result.c_str(), result.size()); + } - if (message.isDisconnect() || !m_socket->isValid()) { - break; - } - } - } + if (message.isDisconnect() || !m_socket->isValid()) { + break; + } + } + } - delete m_socket; - m_socket = NULL; - logInfo(lf_network, "[%05d] connection closed", getID()); + delete m_socket; + m_socket = NULL; + logInfo(lf_network, "[%05d] connection closed", getID()); } Network::Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue* netQueue) - : Thread(), m_netQueue(netQueue), m_listening(false) { - m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0"); + : Thread(), m_netQueue(netQueue), m_listening(false) { + m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0"); - if (m_tcpServer != NULL && m_tcpServer->start() == 0) { - m_listening = true; - } - if (httpPort > 0) { - m_httpServer = new TCPServer(httpPort, "0.0.0.0"); - m_httpServer->start(); - } else { - m_httpServer = NULL; - } + if (m_tcpServer != NULL && m_tcpServer->start() == 0) { + m_listening = true; + } + if (httpPort > 0) { + m_httpServer = new TCPServer(httpPort, "0.0.0.0"); + m_httpServer->start(); + } else { + m_httpServer = NULL; + } } Network::~Network() { - stop(); - while (!m_connections.empty()) { - Connection* connection = m_connections.back(); - m_connections.pop_back(); - connection->stop(); - connection->join(); - delete connection; - } + stop(); + while (!m_connections.empty()) { + Connection* connection = m_connections.back(); + m_connections.pop_back(); + connection->stop(); + connection->join(); + delete connection; + } - if (m_tcpServer != NULL) { - delete m_tcpServer; - } - if (m_httpServer != NULL) { - delete m_httpServer; - } - join(); + if (m_tcpServer != NULL) { + delete m_tcpServer; + } + if (m_httpServer != NULL) { + delete m_httpServer; + } + join(); } void Network::run() { - if (!m_listening) { - return; - } - int ret; - struct timespec tdiff; + if (!m_listening) { + return; + } + int ret; + struct timespec tdiff; - // set timeout - tdiff.tv_sec = 1; - tdiff.tv_nsec = 0; + // set timeout + tdiff.tv_sec = 1; + tdiff.tv_nsec = 0; #ifdef HAVE_PPOLL - int socketCount = m_httpServer ? 2 : 1; - int nfds = 1+socketCount; - struct pollfd fds[nfds]; + int socketCount = m_httpServer ? 2 : 1; + int nfds = 1+socketCount; + struct pollfd fds[nfds]; - memset(fds, 0, sizeof(fds)); + memset(fds, 0, sizeof(fds)); - fds[0].fd = m_notify.notifyFD(); - fds[0].events = POLLIN; + fds[0].fd = m_notify.notifyFD(); + fds[0].events = POLLIN; - fds[1].fd = m_tcpServer->getFD(); - fds[1].events = POLLIN; + fds[1].fd = m_tcpServer->getFD(); + fds[1].events = POLLIN; - if (m_httpServer) { - fds[2].fd = m_httpServer->getFD(); - fds[2].events = POLLIN; - } + if (m_httpServer) { + fds[2].fd = m_httpServer->getFD(); + fds[2].events = POLLIN; + } #else #ifdef HAVE_PSELECT - int maxfd; - fd_set checkfds; + int maxfd; + fd_set checkfds; - FD_ZERO(&checkfds); - FD_SET(m_notify.notifyFD(), &checkfds); - FD_SET(m_tcpServer->getFD(), &checkfds); - if (m_httpServer) { - FD_SET(m_httpServer->getFD(), &checkfds); - } + FD_ZERO(&checkfds); + FD_SET(m_notify.notifyFD(), &checkfds); + FD_SET(m_tcpServer->getFD(), &checkfds); + if (m_httpServer) { + FD_SET(m_httpServer->getFD(), &checkfds); + } - maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ? - m_notify.notifyFD() : m_tcpServer->getFD(); - if (m_httpServer && m_httpServer->getFD() > maxfd) { - maxfd = m_httpServer->getFD(); - } + maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ? + m_notify.notifyFD() : m_tcpServer->getFD(); + if (m_httpServer && m_httpServer->getFD() > maxfd) { + maxfd = m_httpServer->getFD(); + } #endif #endif - while (true) { + while (true) { #ifdef HAVE_PPOLL - // wait for new fd event - ret = ppoll(fds, nfds, &tdiff, NULL); + // wait for new fd event + ret = ppoll(fds, nfds, &tdiff, NULL); #else #ifdef HAVE_PSELECT - // set readfds to inital checkfds - fd_set readfds = checkfds; - // wait for new fd event - ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); + // set readfds to inital checkfds + fd_set readfds = checkfds; + // wait for new fd event + ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); #endif #endif - if (ret == 0) { - cleanConnections(); - continue; - } - bool newData = false, isHttp = false; + if (ret == 0) { + cleanConnections(); + continue; + } + bool newData = false, isHttp = false; #ifdef HAVE_PPOLL - // new data from notify - if (fds[0].revents & POLLIN) { - return; - } - // new data from socket - if (fds[1].revents & POLLIN) { - newData = true; - } else if (m_httpServer && fds[2].revents & POLLIN) { - newData = isHttp = true; - } + // new data from notify + if (fds[0].revents & POLLIN) { + return; + } + // new data from socket + if (fds[1].revents & POLLIN) { + newData = true; + } else if (m_httpServer && fds[2].revents & POLLIN) { + newData = isHttp = true; + } #else #ifdef HAVE_PSELECT - // new data from notify - if (FD_ISSET(m_notify.notifyFD(), &readfds)) { - return; - } - // new data from socket - if (FD_ISSET(m_tcpServer->getFD(), &readfds)) { - newData = true; - } else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) { - newData = isHttp = true; - } + // new data from notify + if (FD_ISSET(m_notify.notifyFD(), &readfds)) { + return; + } + // new data from socket + if (FD_ISSET(m_tcpServer->getFD(), &readfds)) { + newData = true; + } else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) { + newData = isHttp = true; + } #endif #endif - if (newData) { - TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket(); - if (socket == NULL) { - continue; - } - Connection* connection = new Connection(socket, isHttp, m_netQueue); - if (connection == NULL) { - continue; - } - connection->start("connection"); - m_connections.push_back(connection); - logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str()); - } - } + if (newData) { + TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket(); + if (socket == NULL) { + continue; + } + Connection* connection = new Connection(socket, isHttp, m_netQueue); + if (connection == NULL) { + continue; + } + connection->start("connection"); + m_connections.push_back(connection); + logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str()); + } + } } void Network::cleanConnections() { - list::iterator c_it; - for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) { - if (!(*c_it)->isRunning()) { - Connection* connection = *c_it; - c_it = m_connections.erase(c_it); - delete connection; - logDebug(lf_network, "dead connection removed - %d", m_connections.size()); - } - } + list::iterator c_it; + for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) { + if (!(*c_it)->isRunning()) { + Connection* connection = *c_it; + c_it = m_connections.erase(c_it); + delete connection; + logDebug(lf_network, "dead connection removed - %d", m_connections.size()); + } + } } } // namespace ebusd diff --git a/src/ebusd/network.h b/src/ebusd/network.h index 5ff91d13..45e4cf95 100644 --- a/src/ebusd/network.h +++ b/src/ebusd/network.h @@ -39,271 +39,271 @@ class Connection; * Class for data/message transfer between @a Connection and @a MainLoop. */ class NetMessage { - public: - /** - * Constructor. - * @param isHttp whether this is a HTTP message. - */ - explicit NetMessage(const bool isHttp) - : m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) { - pthread_mutex_init(&m_mutex, NULL); - pthread_cond_init(&m_cond, NULL); - } + public: + /** + * Constructor. + * @param isHttp whether this is a HTTP message. + */ + explicit NetMessage(const bool isHttp) + : m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) { + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); + } - /** - * Destructor. - */ - ~NetMessage() { - pthread_mutex_destroy(&m_mutex); - pthread_cond_destroy(&m_cond); - } + /** + * Destructor. + */ + ~NetMessage() { + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_cond); + } - private: - /** - * Hidden copy constructor. - * @param src the object to copy from. - */ - NetMessage(const NetMessage& src); + private: + /** + * Hidden copy constructor. + * @param src the object to copy from. + */ + NetMessage(const NetMessage& src); - public: - /** - * Add request data received from the client. - * @param request the request data from the client. - * @return true when the request is complete and the response shall be prepared. - */ - bool add(string request) { - if (request.length() > 0) { - request.erase(remove(request.begin(), request.end(), '\r'), request.end()); - m_request.append(request); - } - size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n"); - if (pos != string::npos) { - if (m_isHttp) { - pos = m_request.find("\n"); - m_request.resize(pos); // reduce to first line - // typical first line: GET /ehp/outsidetemp HTTP/1.1 - pos = m_request.rfind(" HTTP/"); - if (pos != string::npos) { - m_request.resize(pos); // remove "HTTP/x.x" suffix - } - pos = 0; - while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) { - unsigned int value1, value2; - if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) { - break; - } - m_request[pos] = static_cast(((value1&0x0f) << 4) | (value2&0x0f)); - m_request.erase(pos+1, 2); - } - } else if (pos+1 == m_request.length()) { - m_request.resize(pos); // reduce to complete lines - } - return true; - } - return m_request.length() == 0 && m_listening; - } + public: + /** + * Add request data received from the client. + * @param request the request data from the client. + * @return true when the request is complete and the response shall be prepared. + */ + bool add(string request) { + if (request.length() > 0) { + request.erase(remove(request.begin(), request.end(), '\r'), request.end()); + m_request.append(request); + } + size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n"); + if (pos != string::npos) { + if (m_isHttp) { + pos = m_request.find("\n"); + m_request.resize(pos); // reduce to first line + // typical first line: GET /ehp/outsidetemp HTTP/1.1 + pos = m_request.rfind(" HTTP/"); + if (pos != string::npos) { + m_request.resize(pos); // remove "HTTP/x.x" suffix + } + pos = 0; + while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) { + unsigned int value1, value2; + if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) { + break; + } + m_request[pos] = static_cast(((value1&0x0f) << 4) | (value2&0x0f)); + m_request.erase(pos+1, 2); + } + } else if (pos+1 == m_request.length()) { + m_request.resize(pos); // reduce to complete lines + } + return true; + } + return m_request.length() == 0 && m_listening; + } - /** - * Return whether this is a HTTP message. - * @return whether this is a HTTP message. - */ - bool isHttp() const { return m_isHttp; } + /** + * Return whether this is a HTTP message. + * @return whether this is a HTTP message. + */ + bool isHttp() const { return m_isHttp; } - /** - * Return the request string. - * @return the request string. - */ - string getRequest() const { return m_request; } + /** + * Return the request string. + * @return the request string. + */ + string getRequest() const { return m_request; } - /** - * Wait for the result being set and return the result string. - * @return the result string. - */ - string getResult() { - pthread_mutex_lock(&m_mutex); + /** + * Wait for the result being set and return the result string. + * @return the result string. + */ + string getResult() { + pthread_mutex_lock(&m_mutex); - while (!m_resultSet) - pthread_cond_wait(&m_cond, &m_mutex); + while (!m_resultSet) + pthread_cond_wait(&m_cond, &m_mutex); - m_request.clear(); - string result = m_result; - m_result.clear(); - m_resultSet = false; - pthread_mutex_unlock(&m_mutex); + m_request.clear(); + string result = m_result; + m_result.clear(); + m_resultSet = false; + pthread_mutex_unlock(&m_mutex); - return result; - } + return result; + } - /** - * Set the result string and notify the waiting thread. - * @param result the result string. - * @param listening whether the client is in listening mode. - * @param listenUntil the end time to which to updates were added (exclusive). - * @param disconnect true when the client shall be disconnected. - */ - void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) { - pthread_mutex_lock(&m_mutex); - m_result = result; - m_disconnect = disconnect; - m_listening = listening; - m_listenSince = listenUntil; - m_resultSet = true; - pthread_cond_signal(&m_cond); - pthread_mutex_unlock(&m_mutex); - } + /** + * Set the result string and notify the waiting thread. + * @param result the result string. + * @param listening whether the client is in listening mode. + * @param listenUntil the end time to which to updates were added (exclusive). + * @param disconnect true when the client shall be disconnected. + */ + void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) { + pthread_mutex_lock(&m_mutex); + m_result = result; + m_disconnect = disconnect; + m_listening = listening; + m_listenSince = listenUntil; + m_resultSet = true; + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + } - /** - * Return whether the client is in listening mode. - * @param listenSince set to the start time from which to add updates (inclusive). - * @return whether the client is in listening mode. - */ - bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; } + /** + * Return whether the client is in listening mode. + * @param listenSince set to the start time from which to add updates (inclusive). + * @return whether the client is in listening mode. + */ + bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; } - /** - * Return whether the client shall be disconnected. - * @return true when the client shall be disconnected. - */ - bool isDisconnect() { return m_disconnect; } + /** + * Return whether the client shall be disconnected. + * @return true when the client shall be disconnected. + */ + bool isDisconnect() { return m_disconnect; } - private: - /** whether this is a HTTP message. */ - const bool m_isHttp; + private: + /** whether this is a HTTP message. */ + const bool m_isHttp; - /** the request string. */ - string m_request; + /** the request string. */ + string m_request; - /** whether the result was already set. */ - bool m_resultSet; + /** whether the result was already set. */ + bool m_resultSet; - /** the result string. */ - string m_result; + /** the result string. */ + string m_result; - /** set to true when the client shall be disconnected. */ - bool m_disconnect; + /** set to true when the client shall be disconnected. */ + bool m_disconnect; - /** mutex variable for exclusive lock. */ - pthread_mutex_t m_mutex; + /** mutex variable for exclusive lock. */ + pthread_mutex_t m_mutex; - /** condition variable for exclusive lock. */ - pthread_cond_t m_cond; + /** condition variable for exclusive lock. */ + pthread_cond_t m_cond; - /** whether the client is in listening mode. */ - bool m_listening; + /** whether the client is in listening mode. */ + bool m_listening; - /** start timestamp of listening update. */ - time_t m_listenSince; + /** start timestamp of listening update. */ + time_t m_listenSince; }; /** * class connection which handle client and baseloop communication. */ class Connection : public Thread { - public: - /** - * Constructor. - * @param socket the @a TCPSocket for communication. - * @param isHttp whether this is a HTTP message. - * @param netQueue the reference to the @a NetMessage @a Queue. - */ - Connection(TCPSocket* socket, const bool isHttp, Queue* netQueue) - : Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) { - m_id = ++m_ids; - } + public: + /** + * Constructor. + * @param socket the @a TCPSocket for communication. + * @param isHttp whether this is a HTTP message. + * @param netQueue the reference to the @a NetMessage @a Queue. + */ + Connection(TCPSocket* socket, const bool isHttp, Queue* netQueue) + : Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) { + m_id = ++m_ids; + } - virtual ~Connection() { if (m_socket) delete m_socket; } - /** - * endless loop for connection instance. - */ - virtual void run(); + virtual ~Connection() { if (m_socket) delete m_socket; } + /** + * endless loop for connection instance. + */ + virtual void run(); - /** - * Stop this connection. - */ - virtual void stop() { m_notify.notify(); Thread::stop(); } + /** + * Stop this connection. + */ + virtual void stop() { m_notify.notify(); Thread::stop(); } - /** - * Return the ID of this connection. - * @return the ID of this connection. - */ - int getID() { return m_id; } + /** + * Return the ID of this connection. + * @return the ID of this connection. + */ + int getID() { return m_id; } - private: - /** whether this is a HTTP connection. */ - const bool m_isHttp; + private: + /** whether this is a HTTP connection. */ + const bool m_isHttp; - /** the @a TCPSocket for communication. */ - TCPSocket* m_socket; + /** the @a TCPSocket for communication. */ + TCPSocket* m_socket; - /** the reference to the @a NetMessage @a Queue. */ - Queue* m_netQueue; + /** the reference to the @a NetMessage @a Queue. */ + Queue* m_netQueue; - /** notification object for shutdown procedure. */ - Notify m_notify; + /** notification object for shutdown procedure. */ + Notify m_notify; - /** the ID of this connection. */ - int m_id; + /** the ID of this connection. */ + int m_id; - /** the IF of the last opened connection. */ - static int m_ids; + /** the IF of the last opened connection. */ + static int m_ids; }; /** * class network which listening on tcp socket for incoming connections. */ class Network : public Thread { - public: - /** - * create a network instance and listening for incoming connections. - * @param local true to accept connections only for local host. - * @param port the port to listen for command line connections. - * @param httpPort the port to listen for HTTP connections, or 0. - * @param netQueue the reference to the @a NetMessage @a Queue. - */ - Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue* netQueue); + public: + /** + * create a network instance and listening for incoming connections. + * @param local true to accept connections only for local host. + * @param port the port to listen for command line connections. + * @param httpPort the port to listen for HTTP connections, or 0. + * @param netQueue the reference to the @a NetMessage @a Queue. + */ + Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue* netQueue); - /** - * destructor. - */ - ~Network(); + /** + * destructor. + */ + ~Network(); - /** - * endless loop for network instance. - */ - virtual void run(); + /** + * endless loop for network instance. + */ + virtual void run(); - /** - * shutdown network subsystem. - */ - void stop() const { m_notify.notify(); usleep(100000); } + /** + * shutdown network subsystem. + */ + void stop() const { m_notify.notify(); usleep(100000); } - private: - /** the list of active @a Connection instances. */ - list m_connections; + private: + /** the list of active @a Connection instances. */ + list m_connections; - /** the reference to the @a NetMessage @a Queue. */ - Queue* m_netQueue; + /** the reference to the @a NetMessage @a Queue. */ + Queue* m_netQueue; - /** the command line @a TCPServer instance. */ - TCPServer* m_tcpServer; + /** the command line @a TCPServer instance. */ + TCPServer* m_tcpServer; - /** the HTTP @a TCPServer instance, or NULL. */ - TCPServer* m_httpServer; + /** the HTTP @a TCPServer instance, or NULL. */ + TCPServer* m_httpServer; - /** @a Notify object for shutdown procedure. */ - Notify m_notify; + /** @a Notify object for shutdown procedure. */ + Notify m_notify; - /** true if this instance is listening */ - bool m_listening; + /** true if this instance is listening */ + bool m_listening; - /** - * clean inactive connections from container. - */ - void cleanConnections(); + /** + * clean inactive connections from container. + */ + void cleanConnections(); }; } // namespace ebusd diff --git a/src/lib/ebus/contrib/contrib.cpp b/src/lib/ebus/contrib/contrib.cpp index 0af485d1..81744b0c 100644 --- a/src/lib/ebus/contrib/contrib.cpp +++ b/src/lib/ebus/contrib/contrib.cpp @@ -22,8 +22,8 @@ namespace ebusd { bool libebus_contrib_register() { - contrib_tem_register(); - return true; + contrib_tem_register(); + return true; } } // namespace ebusd diff --git a/src/lib/ebus/contrib/tem.cpp b/src/lib/ebus/contrib/tem.cpp index 70212851..ae818da2 100644 --- a/src/lib/ebus/contrib/tem.cpp +++ b/src/lib/ebus/contrib/tem.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "tem.h" @@ -36,108 +36,108 @@ using std::setw; using std::dec; void contrib_tem_register() { - DataTypeList::getInstance()->add(new TemParamDataType("TEM_P")); + DataTypeList::getInstance()->add(new TemParamDataType("TEM_P")); } result_t TemParamDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived) { - if (divisor == 0) { - divisor = 1; - } - if (bitCount == 0) { - bitCount = m_bitCount; - } - if (divisor == 1 && bitCount == 16) { - derived = this; - return RESULT_OK; - } - return RESULT_ERR_INVALID_ARG; + if (divisor == 0) { + divisor = 1; + } + if (bitCount == 0) { + bitCount = m_bitCount; + } + if (divisor == 1 && bitCount == 16) { + derived = this; + return RESULT_OK; + } + return RESULT_ERR_INVALID_ARG; } result_t TemParamDataType::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, const unsigned char length, - ostringstream& output, OutputFormat outputFormat) { - unsigned int value = 0; + const unsigned char offset, const unsigned char length, + ostringstream& output, OutputFormat outputFormat) { + unsigned int value = 0; - result_t result = readRawValue(input, offset, length, value); - if (result != RESULT_OK) { - return result; - } + result_t result = readRawValue(input, offset, length, value); + if (result != RESULT_OK) { + return result; + } - if (value == m_replacement) { - if (outputFormat & OF_JSON) { - output << "null"; - } else { - output << NULL_VALUE; - } - return RESULT_OK; - } - int grp = 0, num = 0; - if (isMaster) { - grp = (value & 0x1f); // grp in bits 0...5 - num = ((value >> 8) & 0x7f); // num in bits 8...13 - } else { - grp = ((value >> 7) & 0x1f); // grp in bits 7...11 - num = (value & 0x7f); // num in bits 0...6 - } - if (outputFormat & OF_JSON) { - output << '"'; - } - output << setfill('0') << setw(2) << dec << static_cast(grp) << '-' << setw(3) << static_cast(num); - if (outputFormat & OF_JSON) { - output << '"'; - } - output << setfill(' ') << setw(0); // reset - return RESULT_OK; + if (value == m_replacement) { + if (outputFormat & OF_JSON) { + output << "null"; + } else { + output << NULL_VALUE; + } + return RESULT_OK; + } + int grp = 0, num = 0; + if (isMaster) { + grp = (value & 0x1f); // grp in bits 0...5 + num = ((value >> 8) & 0x7f); // num in bits 8...13 + } else { + grp = ((value >> 7) & 0x1f); // grp in bits 7...11 + num = (value & 0x7f); // num in bits 0...6 + } + if (outputFormat & OF_JSON) { + output << '"'; + } + output << setfill('0') << setw(2) << dec << static_cast(grp) << '-' << setw(3) << static_cast(num); + if (outputFormat & OF_JSON) { + output << '"'; + } + output << setfill(' ') << setw(0); // reset + return RESULT_OK; } result_t TemParamDataType::writeSymbols(istringstream& input, - const unsigned char offset, const unsigned char length, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - unsigned int value; - int grp, num; - string token; + const unsigned char offset, const unsigned char length, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + unsigned int value; + int grp, num; + string token; - const char* str = input.str().c_str(); - if (strcasecmp(str, NULL_VALUE) == 0) { - value = m_replacement; // replacement value - } else { - if (input.eof() || !getline(input, token, '-')) { - return RESULT_ERR_EOF; // incomplete - } - str = token.c_str(); - if (str == NULL || *str == 0) { - return RESULT_ERR_EOF; // input too short - } - char* strEnd = NULL; - grp = (unsigned int)strtoul(str, &strEnd, 10); - if (strEnd == NULL || strEnd == str || *strEnd != 0) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - if (input.eof() || !getline(input, token, '-')) { - return RESULT_ERR_EOF; // incomplete - } - str = token.c_str(); - if (str == NULL || *str == 0) { - return RESULT_ERR_EOF; // input too short - } - strEnd = NULL; - num = (unsigned int)strtoul(str, &strEnd, 10); - if (strEnd == NULL || strEnd == str || *strEnd != 0) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - if (isMaster) { - value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13 - } else { - value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6 - } - } - if (value < getMinValue() || value > getMaxValue()) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - return writeRawValue(value, offset, length, output, usedLength); + const char* str = input.str().c_str(); + if (strcasecmp(str, NULL_VALUE) == 0) { + value = m_replacement; // replacement value + } else { + if (input.eof() || !getline(input, token, '-')) { + return RESULT_ERR_EOF; // incomplete + } + str = token.c_str(); + if (str == NULL || *str == 0) { + return RESULT_ERR_EOF; // input too short + } + char* strEnd = NULL; + grp = (unsigned int)strtoul(str, &strEnd, 10); + if (strEnd == NULL || strEnd == str || *strEnd != 0) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + if (input.eof() || !getline(input, token, '-')) { + return RESULT_ERR_EOF; // incomplete + } + str = token.c_str(); + if (str == NULL || *str == 0) { + return RESULT_ERR_EOF; // input too short + } + strEnd = NULL; + num = (unsigned int)strtoul(str, &strEnd, 10); + if (strEnd == NULL || strEnd == str || *strEnd != 0) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + if (isMaster) { + value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13 + } else { + value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6 + } + } + if (value < getMinValue() || value > getMaxValue()) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + return writeRawValue(value, offset, length, output, usedLength); } } // namespace ebusd diff --git a/src/lib/ebus/contrib/tem.h b/src/lib/ebus/contrib/tem.h index 41a2b09d..7395f541 100644 --- a/src/lib/ebus/contrib/tem.h +++ b/src/lib/ebus/contrib/tem.h @@ -41,26 +41,26 @@ namespace ebusd { * data. */ class TemParamDataType : public NumberDataType { - public: - /** - * Constructs a new instance. - * @param id the type identifier. - */ - explicit TemParamDataType(const string id) - : NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {} + public: + /** + * Constructs a new instance. + * @param id the type identifier. + */ + explicit TemParamDataType(const string id) + : NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {} - // @copydoc - virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived); + // @copydoc + virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived); - // @copydoc - virtual result_t readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, const unsigned char length, - ostringstream& output, OutputFormat outputFormat); + // @copydoc + virtual result_t readSymbols(SymbolString& input, const bool isMaster, + const unsigned char offset, const unsigned char length, + ostringstream& output, OutputFormat outputFormat); - // @copydoc - virtual result_t writeSymbols(istringstream& input, - const unsigned char offset, const unsigned char length, - SymbolString& output, const bool isMaster, unsigned char* usedLength); + // @copydoc + virtual result_t writeSymbols(istringstream& input, + const unsigned char offset, const unsigned char length, + SymbolString& output, const bool isMaster, unsigned char* usedLength); }; /** diff --git a/src/lib/ebus/contrib/test/test_tem.cpp b/src/lib/ebus/contrib/test/test_tem.cpp index 12216b46..7e40f512 100644 --- a/src/lib/ebus/contrib/test/test_tem.cpp +++ b/src/lib/ebus/contrib/test/test_tem.cpp @@ -28,167 +28,167 @@ using namespace ebusd; static bool error = false; void verify(bool expectFailMatch, string type, string input, - bool match, string expectStr, string gotStr) { - match = match && expectStr == gotStr; - if (expectFailMatch) { - if (match) { - cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed " << type << " match >" << input << "< OK" << endl; - } - } else if (match) { - cout << " " << type << " match >" << input << "< OK" << endl; - } else { - cout << " " << type << " match >" << input << "< error: got >" - << gotStr << "<, expected >" << expectStr << "<" << endl; - error = true; - } + bool match, string expectStr, string gotStr) { + match = match && expectStr == gotStr; + if (expectFailMatch) { + if (match) { + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + } else if (match) { + cout << " " << type << " match >" << input << "< OK" << endl; + } else { + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; + error = true; + } } int main() { - DataType* type = DataTypeList::getInstance()->get("TEM_P"); - if (type == NULL) { - cout << "datatype not registered" << endl; - return 1; - } + DataType* type = DataTypeList::getInstance()->get("TEM_P"); + if (type == NULL) { + cout << "datatype not registered" << endl; + return 1; + } - string checks[][5] = { - // entry: definition, decoded value, master data, slave data, flags - // definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] - {"x,,TEM_P", "04-033", "10fe0700020421", "00", ""}, - {"x,,TEM_P", "00-000", "10fe0700020000", "00", ""}, - {"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""}, - {"x,,TEM_P", "-", "10fe070002ffff", "00", ""}, - {"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"}, - {"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"}, + string checks[][5] = { + // entry: definition, decoded value, master data, slave data, flags + // definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] + {"x,,TEM_P", "04-033", "10fe0700020421", "00", ""}, + {"x,,TEM_P", "00-000", "10fe0700020000", "00", ""}, + {"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""}, + {"x,,TEM_P", "-", "10fe070002ffff", "00", ""}, + {"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"}, + {"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"}, - {"x,,TEM_P", "04-033", "1015070000", "022102", ""}, - {"x,,TEM_P", "00-000", "1015070000", "020000", ""}, - {"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""}, - {"x,,TEM_P", "-", "1015070000", "02ffff", ""}, - {"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"}, - {"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"}, - }; - DataFieldTemplates* templates = new DataFieldTemplates(); - DataField* fields = NULL; - for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { - string check[5] = checks[i]; - istringstream isstr(check[0]); - string expectStr = check[1]; - SymbolString mstr(false); - result_t result = mstr.parseHex(check[2]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; - error = true; - continue; - } - SymbolString sstr(false); - result = sstr.parseHex(check[3]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; - error = true; - continue; - } - string flags = check[4]; - bool isSet = flags.find('s') != string::npos; - bool failedRead = flags.find('r') != string::npos; - bool failedReadMatch = flags.find('R') != string::npos; - bool failedWrite = flags.find('w') != string::npos; - bool failedWriteMatch = flags.find('W') != string::npos; - string item; - vector entries; + {"x,,TEM_P", "04-033", "1015070000", "022102", ""}, + {"x,,TEM_P", "00-000", "1015070000", "020000", ""}, + {"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""}, + {"x,,TEM_P", "-", "1015070000", "02ffff", ""}, + {"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"}, + {"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"}, + }; + DataFieldTemplates* templates = new DataFieldTemplates(); + DataField* fields = NULL; + for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { + string check[5] = checks[i]; + istringstream isstr(check[0]); + string expectStr = check[1]; + SymbolString mstr(false); + result_t result = mstr.parseHex(check[2]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; + error = true; + continue; + } + SymbolString sstr(false); + result = sstr.parseHex(check[3]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; + error = true; + continue; + } + string flags = check[4]; + bool isSet = flags.find('s') != string::npos; + bool failedRead = flags.find('r') != string::npos; + bool failedReadMatch = flags.find('R') != string::npos; + bool failedWrite = flags.find('w') != string::npos; + bool failedWriteMatch = flags.find('W') != string::npos; + string item; + vector entries; - while (getline(isstr, item, FIELD_SEPARATOR)) - entries.push_back(item); + while (getline(isstr, item, FIELD_SEPARATOR)) + entries.push_back(item); - if (fields != NULL) { - delete fields; - fields = NULL; - } - vector::iterator it = entries.begin(); - result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1]))); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; - error = true; - continue; - } - if (fields == NULL) { - cout << "\"" << check[0] << "\": create error: NULL" << endl; - error = true; - continue; - } - if (it != entries.end()) { - cout << "\"" << check[0] << "\": create error: trailing input" << endl; - error = true; - continue; - } - cout << "\"" << check[0] << "\"=\""; - fields->dump(cout); - cout << "\": create OK" << endl; + if (fields != NULL) { + delete fields; + fields = NULL; + } + vector::iterator it = entries.begin(); + result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1]))); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; + error = true; + continue; + } + if (fields == NULL) { + cout << "\"" << check[0] << "\": create error: NULL" << endl; + error = true; + continue; + } + if (it != entries.end()) { + cout << "\"" << check[0] << "\": create error: trailing input" << endl; + error = true; + continue; + } + cout << "\"" << check[0] << "\"=\""; + fields->dump(cout); + cout << "\": create OK" << endl; - ostringstream output; - SymbolString writeMstr(false); - result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10)); - if (result != RESULT_OK) { - cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl; - error = true; - } - SymbolString writeSstr(false); - result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2)); - if (result != RESULT_OK) { - cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl; - error = true; - } - result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false); - if (result >= RESULT_OK) { - result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty()); - } - if (failedRead) { - if (result >= RESULT_OK) { - cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< OK" << endl; - } - } else if (result < RESULT_OK) { - cout << " read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< error: " << getResultCode(result) << endl; - error = true; - } else { - bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; - verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); - } + ostringstream output; + SymbolString writeMstr(false); + result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10)); + if (result != RESULT_OK) { + cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl; + error = true; + } + SymbolString writeSstr(false); + result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2)); + if (result != RESULT_OK) { + cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl; + error = true; + } + result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false); + if (result >= RESULT_OK) { + result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty()); + } + if (failedRead) { + if (result >= RESULT_OK) { + cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< OK" << endl; + } + } else if (result < RESULT_OK) { + cout << " read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< error: " << getResultCode(result) << endl; + error = true; + } else { + bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; + verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); + } - istringstream input(expectStr); - result = fields->write(input, pt_masterData, writeMstr, 0); - if (result >= RESULT_OK) { - result = fields->write(input, pt_slaveData, writeSstr, 0); - } - if (failedWrite) { - if (result >= RESULT_OK) { - cout << " failed write " << fields->getName() << " >" - << expectStr << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed write " << fields->getName() << " >" - << expectStr << "< OK" << endl; - } - } else if (result < RESULT_OK) { - cout << " write " << fields->getName() << " >" << expectStr - << "< error: " << getResultCode(result) << endl; - error = true; - } else { - bool match = mstr == writeMstr && sstr == writeSstr; - verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false)); - } - delete fields; - fields = NULL; - } + istringstream input(expectStr); + result = fields->write(input, pt_masterData, writeMstr, 0); + if (result >= RESULT_OK) { + result = fields->write(input, pt_slaveData, writeSstr, 0); + } + if (failedWrite) { + if (result >= RESULT_OK) { + cout << " failed write " << fields->getName() << " >" + << expectStr << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed write " << fields->getName() << " >" + << expectStr << "< OK" << endl; + } + } else if (result < RESULT_OK) { + cout << " write " << fields->getName() << " >" << expectStr + << "< error: " << getResultCode(result) << endl; + error = true; + } else { + bool match = mstr == writeMstr && sstr == writeSstr; + verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false)); + } + delete fields; + fields = NULL; + } - delete templates; - return error ? 1 : 0; + delete templates; + return error ? 1 : 0; } diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index acd96ca5..e3e1170d 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "data.h" @@ -40,1031 +40,1031 @@ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" result_t DataField::create(vector::iterator& it, - const vector::iterator end, - DataFieldTemplates* templates, DataField*& returnField, - const bool isWriteMessage, - const bool isTemplate, const bool isBroadcastOrMasterDestination, - const unsigned char maxFieldLength) { - vector fields; - string firstName, firstComment; - result_t result = RESULT_OK; - if (it == end) { - return RESULT_ERR_EOF; - } - while (it != end && result == RESULT_OK) { - string unit, comment; - PartType partType; - int divisor = 0; - bool hasPartStr = false; - string token; + const vector::iterator end, + DataFieldTemplates* templates, DataField*& returnField, + const bool isWriteMessage, + const bool isTemplate, const bool isBroadcastOrMasterDestination, + const unsigned char maxFieldLength) { + vector fields; + string firstName, firstComment; + result_t result = RESULT_OK; + if (it == end) { + return RESULT_ERR_EOF; + } + while (it != end && result == RESULT_OK) { + string unit, comment; + PartType partType; + int divisor = 0; + bool hasPartStr = false; + string token; - // template: name,basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] - // std: name,part,basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] - const string name = *it++; // name - if (it == end) { - if (!name.empty()) { - result = RESULT_ERR_MISSING_TYPE; - } - break; - } + // template: name,basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] + // std: name,part,basetype[:len]|template[:name][,[divisor|values][,[unit][,[comment]]]] + const string name = *it++; // name + if (it == end) { + if (!name.empty()) { + result = RESULT_ERR_MISSING_TYPE; + } + break; + } - if (isTemplate) { - partType = pt_any; - } else { - const char* partStr = (*it++).c_str(); // part - hasPartStr = partStr[0] != 0; - if (it == end) { - if (!name.empty() || hasPartStr) { - result = RESULT_ERR_MISSING_TYPE; - } - break; - } - if (isBroadcastOrMasterDestination - || (isWriteMessage && !hasPartStr) - || strcasecmp(partStr, "M") == 0) { // master data - partType = pt_masterData; - } else if ((!isWriteMessage && !hasPartStr) - || strcasecmp(partStr, "S") == 0) { // slave data - partType = pt_slaveData; - } else { - result = RESULT_ERR_INVALID_PART; - break; - } - } + if (isTemplate) { + partType = pt_any; + } else { + const char* partStr = (*it++).c_str(); // part + hasPartStr = partStr[0] != 0; + if (it == end) { + if (!name.empty() || hasPartStr) { + result = RESULT_ERR_MISSING_TYPE; + } + break; + } + if (isBroadcastOrMasterDestination + || (isWriteMessage && !hasPartStr) + || strcasecmp(partStr, "M") == 0) { // master data + partType = pt_masterData; + } else if ((!isWriteMessage && !hasPartStr) + || strcasecmp(partStr, "S") == 0) { // slave data + partType = pt_slaveData; + } else { + result = RESULT_ERR_INVALID_PART; + break; + } + } - if (fields.empty()) { - firstName = name; - firstComment = comment; - } + if (fields.empty()) { + firstName = name; + firstComment = comment; + } - const string typeStr = *it++; // basetype[:len]|template[:name] - vector::iterator typePos = it; - if (typeStr.empty()) { - if (!name.empty() || hasPartStr) { - result = RESULT_ERR_MISSING_TYPE; - } - break; - } + const string typeStr = *it++; // basetype[:len]|template[:name] + vector::iterator typePos = it; + if (typeStr.empty()) { + if (!name.empty() || hasPartStr) { + result = RESULT_ERR_MISSING_TYPE; + } + break; + } - map values; - string constantValue; - bool verifyValue = false; - if (it != end) { - const string divisorStr = *it++; // [divisor|values] - if (!divisorStr.empty()) { - size_t equalPos = divisorStr.find('='); - if (equalPos == string::npos) { - divisor = parseSignedInt(divisorStr.c_str(), 10, -MAX_DIVISOR, MAX_DIVISOR, result); - } else if (equalPos == 0 && divisorStr.length() > 1) { - verifyValue = divisorStr[1] == '='; // == forced verification of constant value - if (verifyValue && divisorStr.length() == 1) { - result = RESULT_ERR_INVALID_LIST; - break; - } - constantValue = divisorStr.substr(equalPos+(verifyValue?2:1)); - } else { - istringstream stream(divisorStr); - while (getline(stream, token, VALUE_SEPARATOR)) { - FileReader::trim(token); - const char* str = token.c_str(); - char* strEnd = NULL; - unsigned long id; - if (strncasecmp(str, "0x", 2) == 0) { - str += 2; - id = strtoul(str, &strEnd, 16); // hexadecimal - } else { - id = strtoul(str, &strEnd, 10); // decimal - } - if (strEnd == NULL || strEnd == str || id > MAX_VALUE) { - result = RESULT_ERR_INVALID_LIST; - break; - } - // remove blanks around '=' sign - while (*strEnd == ' ') strEnd++; - if (*strEnd != '=') { - result = RESULT_ERR_INVALID_LIST; - break; - } - token = string(strEnd + 1); - FileReader::trim(token); - values[(unsigned int)id] = token; - } - } - if (result != RESULT_OK) { - break; - } - } - } + map values; + string constantValue; + bool verifyValue = false; + if (it != end) { + const string divisorStr = *it++; // [divisor|values] + if (!divisorStr.empty()) { + size_t equalPos = divisorStr.find('='); + if (equalPos == string::npos) { + divisor = parseSignedInt(divisorStr.c_str(), 10, -MAX_DIVISOR, MAX_DIVISOR, result); + } else if (equalPos == 0 && divisorStr.length() > 1) { + verifyValue = divisorStr[1] == '='; // == forced verification of constant value + if (verifyValue && divisorStr.length() == 1) { + result = RESULT_ERR_INVALID_LIST; + break; + } + constantValue = divisorStr.substr(equalPos+(verifyValue?2:1)); + } else { + istringstream stream(divisorStr); + while (getline(stream, token, VALUE_SEPARATOR)) { + FileReader::trim(token); + const char* str = token.c_str(); + char* strEnd = NULL; + unsigned long id; + if (strncasecmp(str, "0x", 2) == 0) { + str += 2; + id = strtoul(str, &strEnd, 16); // hexadecimal + } else { + id = strtoul(str, &strEnd, 10); // decimal + } + if (strEnd == NULL || strEnd == str || id > MAX_VALUE) { + result = RESULT_ERR_INVALID_LIST; + break; + } + // remove blanks around '=' sign + while (*strEnd == ' ') strEnd++; + if (*strEnd != '=') { + result = RESULT_ERR_INVALID_LIST; + break; + } + token = string(strEnd + 1); + FileReader::trim(token); + values[(unsigned int)id] = token; + } + } + if (result != RESULT_OK) { + break; + } + } + } - if (it == end) { - unit = ""; - } else { - const string str = *it++; // [unit] - if (strcasecmp(str.c_str(), NULL_VALUE) == 0) { - unit = ""; - } else { - unit = str; - } - } + if (it == end) { + unit = ""; + } else { + const string str = *it++; // [unit] + if (strcasecmp(str.c_str(), NULL_VALUE) == 0) { + unit = ""; + } else { + unit = str; + } + } - if (it == end) { - comment = ""; - } else { - const string str = *it++; // [comment] - if (strcasecmp(str.c_str(), NULL_VALUE) == 0) { - comment = ""; - } else { - comment = str; - } - } + if (it == end) { + comment = ""; + } else { + const string str = *it++; // [comment] + if (strcasecmp(str.c_str(), NULL_VALUE) == 0) { + comment = ""; + } else { + comment = str; + } + } - bool firstType = true; - istringstream stream(typeStr); - while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR)) { - FileReader::trim(token); - DataField* templ = templates->get(token); - size_t pos = token.find(LENGTH_SEPARATOR); - if (templ == NULL && pos != string::npos) { - templ = templates->get(token.substr(0, pos)); - } - if (templ == NULL) { // basetype[:len] - unsigned char length; - string typeName; - if (pos == string::npos) { - length = 0; // no length specified - typeName = token; - } else { - if (pos+2 == token.length() && token[pos+1] == '*') { - length = REMAIN_LEN; - } else { - length = (unsigned char)parseInt(token.substr(pos+1).c_str(), 10, 1, maxFieldLength, result); - if (result != RESULT_OK) { - break; - } - } - typeName = token.substr(0, pos); - } - transform(typeName.begin(), typeName.end(), typeName.begin(), ::toupper); - SingleDataField* add = NULL; - result = SingleDataField::create(typeName, length, firstType ? name : "", firstType ? comment : "", firstType ? unit : "", partType, divisor, values, constantValue, verifyValue, add); - if (add != NULL) { - fields.push_back(add); - } else { - it = typePos; // back to type - if (result == RESULT_OK) { - result = RESULT_ERR_NOTFOUND; // type not found - } - } - } else if (!constantValue.empty()) { - it = typePos; // back to type - result = RESULT_ERR_INVALID_ARG; // invalid value list - } else { // template[:name] - string fieldName; - bool lastType = stream.eof(); - if (pos != string::npos) { // replacement name specified - fieldName = token.substr(pos+1); - } else { - fieldName = (firstType && lastType) ? name : ""; - } - result = templ->derive(fieldName, firstType ? comment : "", firstType ? unit : "", partType, divisor, values, fields); - if (result != RESULT_OK) { - it = typePos; // back to type - } - } - firstType = false; - } - } + bool firstType = true; + istringstream stream(typeStr); + while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR)) { + FileReader::trim(token); + DataField* templ = templates->get(token); + size_t pos = token.find(LENGTH_SEPARATOR); + if (templ == NULL && pos != string::npos) { + templ = templates->get(token.substr(0, pos)); + } + if (templ == NULL) { // basetype[:len] + unsigned char length; + string typeName; + if (pos == string::npos) { + length = 0; // no length specified + typeName = token; + } else { + if (pos+2 == token.length() && token[pos+1] == '*') { + length = REMAIN_LEN; + } else { + length = (unsigned char)parseInt(token.substr(pos+1).c_str(), 10, 1, maxFieldLength, result); + if (result != RESULT_OK) { + break; + } + } + typeName = token.substr(0, pos); + } + transform(typeName.begin(), typeName.end(), typeName.begin(), ::toupper); + SingleDataField* add = NULL; + result = SingleDataField::create(typeName, length, firstType ? name : "", firstType ? comment : "", firstType ? unit : "", partType, divisor, values, constantValue, verifyValue, add); + if (add != NULL) { + fields.push_back(add); + } else { + it = typePos; // back to type + if (result == RESULT_OK) { + result = RESULT_ERR_NOTFOUND; // type not found + } + } + } else if (!constantValue.empty()) { + it = typePos; // back to type + result = RESULT_ERR_INVALID_ARG; // invalid value list + } else { // template[:name] + string fieldName; + bool lastType = stream.eof(); + if (pos != string::npos) { // replacement name specified + fieldName = token.substr(pos+1); + } else { + fieldName = (firstType && lastType) ? name : ""; + } + result = templ->derive(fieldName, firstType ? comment : "", firstType ? unit : "", partType, divisor, values, fields); + if (result != RESULT_OK) { + it = typePos; // back to type + } + } + firstType = false; + } + } - if (result != RESULT_OK) { - while (!fields.empty()) { // cleanup already created fields - delete fields.back(); - fields.pop_back(); - } - return result; - } + if (result != RESULT_OK) { + while (!fields.empty()) { // cleanup already created fields + delete fields.back(); + fields.pop_back(); + } + return result; + } - if (fields.size() == 1) { - returnField = fields[0]; - } else { - returnField = new DataFieldSet(firstName, firstComment, fields); - } - return RESULT_OK; + if (fields.size() == 1) { + returnField = fields[0]; + } else { + returnField = new DataFieldSet(firstName, firstComment, fields); + } + return RESULT_OK; } void DataField::dumpString(ostream& output, const string str, const bool prependFieldSeparator) { - if (prependFieldSeparator) { - output << FIELD_SEPARATOR; - } - if (str.find_first_of(FIELD_SEPARATOR) == string::npos) { - output << str; - } else { - output << TEXT_SEPARATOR << str << TEXT_SEPARATOR; - } + if (prependFieldSeparator) { + output << FIELD_SEPARATOR; + } + if (str.find_first_of(FIELD_SEPARATOR) == string::npos) { + output << str; + } else { + output << TEXT_SEPARATOR << str << TEXT_SEPARATOR; + } } result_t SingleDataField::create(const string id, const unsigned char length, - const string name, const string comment, const string unit, - const PartType partType, int divisor, map values, - const string constantValue, const bool verifyValue, SingleDataField* &returnField) { - DataType* dataType = DataTypeList::getInstance()->get(id, length == REMAIN_LEN ? (unsigned char)0 : length); - if (!dataType) { - return RESULT_ERR_NOTFOUND; - } - unsigned char bitCount = dataType->getBitCount(); - unsigned char byteCount = (unsigned char)((bitCount + 7) / 8); - if (dataType->isAdjustableLength()) { - // check length - if ((bitCount % 8) != 0) { - if (length == 0) { - bitCount = 1; // default bit count: 1 bit - } else if (length <= bitCount) { - bitCount = length; - } else { - return RESULT_ERR_OUT_OF_RANGE; // invalid length - } - byteCount = (unsigned char)((bitCount + 7) / 8); - } else if (length == 0) { - byteCount = 1; //default byte count: 1 byte - } else if (length <= byteCount || length == REMAIN_LEN) { - byteCount = length; - } else { - return RESULT_ERR_OUT_OF_RANGE; // invalid length - } - } - if (!constantValue.empty()) { - returnField = new ConstantDataField(name, comment, unit, dataType, partType, byteCount, constantValue, verifyValue); - return RESULT_OK; - } - if (dataType->isNumeric()) { - NumberDataType* numType = reinterpret_cast(dataType); - if (values.empty() && numType->hasFlag(DAY)) { - for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++) - values[numType->getMinValue() + i] = dayNames[i]; - } - result_t result = numType->derive(divisor, bitCount, numType); - if (result != RESULT_OK) { - return result; - } - if (values.empty()) { - returnField = new SingleDataField(name, comment, unit, numType, partType, byteCount); - return RESULT_OK; - } - if (values.begin()->first < numType->getMinValue() || values.rbegin()->first > numType->getMaxValue()) { - return RESULT_ERR_OUT_OF_RANGE; - } - returnField = new ValueListDataField(name, comment, unit, numType, partType, byteCount, values); - return RESULT_OK; - } - if (divisor != 0 || !values.empty()) { - return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for string field - } - returnField = new SingleDataField(name, comment, unit, dataType, partType, byteCount); - return RESULT_OK; + const string name, const string comment, const string unit, + const PartType partType, int divisor, map values, + const string constantValue, const bool verifyValue, SingleDataField* &returnField) { + DataType* dataType = DataTypeList::getInstance()->get(id, length == REMAIN_LEN ? (unsigned char)0 : length); + if (!dataType) { + return RESULT_ERR_NOTFOUND; + } + unsigned char bitCount = dataType->getBitCount(); + unsigned char byteCount = (unsigned char)((bitCount + 7) / 8); + if (dataType->isAdjustableLength()) { + // check length + if ((bitCount % 8) != 0) { + if (length == 0) { + bitCount = 1; // default bit count: 1 bit + } else if (length <= bitCount) { + bitCount = length; + } else { + return RESULT_ERR_OUT_OF_RANGE; // invalid length + } + byteCount = (unsigned char)((bitCount + 7) / 8); + } else if (length == 0) { + byteCount = 1; //default byte count: 1 byte + } else if (length <= byteCount || length == REMAIN_LEN) { + byteCount = length; + } else { + return RESULT_ERR_OUT_OF_RANGE; // invalid length + } + } + if (!constantValue.empty()) { + returnField = new ConstantDataField(name, comment, unit, dataType, partType, byteCount, constantValue, verifyValue); + return RESULT_OK; + } + if (dataType->isNumeric()) { + NumberDataType* numType = reinterpret_cast(dataType); + if (values.empty() && numType->hasFlag(DAY)) { + for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++) + values[numType->getMinValue() + i] = dayNames[i]; + } + result_t result = numType->derive(divisor, bitCount, numType); + if (result != RESULT_OK) { + return result; + } + if (values.empty()) { + returnField = new SingleDataField(name, comment, unit, numType, partType, byteCount); + return RESULT_OK; + } + if (values.begin()->first < numType->getMinValue() || values.rbegin()->first > numType->getMaxValue()) { + return RESULT_ERR_OUT_OF_RANGE; + } + returnField = new ValueListDataField(name, comment, unit, numType, partType, byteCount, values); + return RESULT_OK; + } + if (divisor != 0 || !values.empty()) { + return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for string field + } + returnField = new SingleDataField(name, comment, unit, dataType, partType, byteCount); + return RESULT_OK; } void SingleDataField::dump(ostream& output) { - output << setw(0) << dec; // initialize formatting - dumpString(output, m_name, false); - output << FIELD_SEPARATOR; - if (m_partType == pt_masterData) { - output << "m"; - } else if (m_partType == pt_slaveData) { - output << "s"; - } - output << FIELD_SEPARATOR; - m_dataType->dump(output, m_length); - dumpString(output, m_unit); - dumpString(output, m_comment); + output << setw(0) << dec; // initialize formatting + dumpString(output, m_name, false); + output << FIELD_SEPARATOR; + if (m_partType == pt_masterData) { + output << "m"; + } else if (m_partType == pt_slaveData) { + output << "s"; + } + output << FIELD_SEPARATOR; + m_dataType->dump(output, m_length); + dumpString(output, m_unit); + dumpString(output, m_comment); } result_t SingleDataField::read(const PartType partType, - SymbolString& data, unsigned char offset, - unsigned int& output, const char* fieldName, signed char fieldIndex) { - if (partType != m_partType) { - return RESULT_EMPTY; - } - switch (m_partType) { - case pt_masterData: - offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN - break; - case pt_slaveData: - offset++; // skip NN - break; - default: - return RESULT_ERR_INVALID_PART; - } - bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); - if (offset + (remainder?1:m_length) > data.size()) { - return RESULT_ERR_INVALID_POS; - } - if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { - return RESULT_EMPTY; - } - return m_dataType->readRawValue(data, offset, m_length, output); + SymbolString& data, unsigned char offset, + unsigned int& output, const char* fieldName, signed char fieldIndex) { + if (partType != m_partType) { + return RESULT_EMPTY; + } + switch (m_partType) { + case pt_masterData: + offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN + break; + case pt_slaveData: + offset++; // skip NN + break; + default: + return RESULT_ERR_INVALID_PART; + } + bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); + if (offset + (remainder?1:m_length) > data.size()) { + return RESULT_ERR_INVALID_POS; + } + if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { + return RESULT_EMPTY; + } + return m_dataType->readRawValue(data, offset, m_length, output); } result_t SingleDataField::read(const PartType partType, - SymbolString& data, unsigned char offset, - ostringstream& output, OutputFormat outputFormat, signed char outputIndex, - bool leadingSeparator, const char* fieldName, signed char fieldIndex) { - if (partType != m_partType) { - return RESULT_OK; - } - switch (m_partType) { - case pt_masterData: - offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN - break; - case pt_slaveData: - offset++; // skip NN - break; - default: - return RESULT_ERR_INVALID_PART; - } - bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); - if (offset + (remainder?1:m_length) > data.size()) { - return RESULT_ERR_INVALID_POS; - } - if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { - return RESULT_EMPTY; - } + SymbolString& data, unsigned char offset, + ostringstream& output, OutputFormat outputFormat, signed char outputIndex, + bool leadingSeparator, const char* fieldName, signed char fieldIndex) { + if (partType != m_partType) { + return RESULT_OK; + } + switch (m_partType) { + case pt_masterData: + offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN + break; + case pt_slaveData: + offset++; // skip NN + break; + default: + return RESULT_ERR_INVALID_PART; + } + bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); + if (offset + (remainder?1:m_length) > data.size()) { + return RESULT_ERR_INVALID_POS; + } + if (isIgnored() || (fieldName != NULL && (m_name != fieldName || fieldIndex > 0))) { + return RESULT_EMPTY; + } - if (outputFormat & OF_JSON) { - if (leadingSeparator) { - output << ","; - } - if (outputIndex >= 0 || m_name.empty() || !(outputFormat & OF_NAMES)) { - output << "\n \"" << static_cast(outputIndex < 0 ? 0 : outputIndex) << "\": {\"name\": \"" << m_name << "\"" << ", \"value\": "; - } else { - output << "\n \"" << m_name << "\": {\"value\": "; - } - } else { - if (leadingSeparator) { - output << UI_FIELD_SEPARATOR; - } - if (outputFormat & OF_NAMES) { - output << m_name << "="; - } - } + if (outputFormat & OF_JSON) { + if (leadingSeparator) { + output << ","; + } + if (outputIndex >= 0 || m_name.empty() || !(outputFormat & OF_NAMES)) { + output << "\n \"" << static_cast(outputIndex < 0 ? 0 : outputIndex) << "\": {\"name\": \"" << m_name << "\"" << ", \"value\": "; + } else { + output << "\n \"" << m_name << "\": {\"value\": "; + } + } else { + if (leadingSeparator) { + output << UI_FIELD_SEPARATOR; + } + if (outputFormat & OF_NAMES) { + output << m_name << "="; + } + } - result_t result = readSymbols(data, m_partType == pt_masterData, offset, output, outputFormat); - if (result != RESULT_OK) { - return result; - } - if ((outputFormat & OF_UNITS) && m_unit.length() > 0) { - if (outputFormat & OF_JSON) { - output << ", \"unit\": \"" << m_unit << '"'; - } else { - output << " " << m_unit; - } - } - if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { - if (outputFormat & OF_JSON) { - output << ", \"comment\": \"" << m_comment << '"'; - } else { - output << " [" << m_comment << "]"; - } - } - if (outputFormat & OF_JSON) { - output << "}"; - } - return RESULT_OK; + result_t result = readSymbols(data, m_partType == pt_masterData, offset, output, outputFormat); + if (result != RESULT_OK) { + return result; + } + if ((outputFormat & OF_UNITS) && m_unit.length() > 0) { + if (outputFormat & OF_JSON) { + output << ", \"unit\": \"" << m_unit << '"'; + } else { + output << " " << m_unit; + } + } + if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { + if (outputFormat & OF_JSON) { + output << ", \"comment\": \"" << m_comment << '"'; + } else { + output << " [" << m_comment << "]"; + } + } + if (outputFormat & OF_JSON) { + output << "}"; + } + return RESULT_OK; } result_t SingleDataField::write(istringstream& input, - const PartType partType, SymbolString& data, - unsigned char offset, char separator, unsigned char* length) { - if (partType != m_partType) { - return RESULT_OK; - } - switch (m_partType) { - case pt_masterData: - offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN - break; - case pt_slaveData: - offset++; // skip NN - break; - default: - return RESULT_ERR_INVALID_PART; - } - return writeSymbols(input, (const unsigned char)offset, data, m_partType == pt_masterData, length); + const PartType partType, SymbolString& data, + unsigned char offset, char separator, unsigned char* length) { + if (partType != m_partType) { + return RESULT_OK; + } + switch (m_partType) { + case pt_masterData: + offset = (unsigned char)(offset + 5); // skip QQ ZZ PB SB NN + break; + case pt_slaveData: + offset++; // skip NN + break; + default: + return RESULT_ERR_INVALID_PART; + } + return writeSymbols(input, (const unsigned char)offset, data, m_partType == pt_masterData, length); } result_t SingleDataField::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat) { - return m_dataType->readSymbols(input, isMaster, offset, m_length, output, outputFormat); + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat) { + return m_dataType->readSymbols(input, isMaster, offset, m_length, output, outputFormat); } result_t SingleDataField::writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - return m_dataType->writeSymbols(input, offset, m_length, output, isMaster, usedLength); + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + return m_dataType->writeSymbols(input, offset, m_length, output, isMaster, usedLength); } SingleDataField* SingleDataField::clone() { - return new SingleDataField(*this); + return new SingleDataField(*this); } result_t SingleDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { - if (m_partType != pt_any && partType == pt_any) { - return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance - } - bool numeric = m_dataType->isNumeric(); - if (!numeric && (divisor != 0 || !values.empty())) { - return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for non-numeric field - } - if (name.empty()) { - name = m_name; - } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; - } - DataType* dataType = m_dataType; - if (numeric) { - NumberDataType* numType = reinterpret_cast(dataType); - result_t result = numType->derive(divisor, 0, numType); - if (result != RESULT_OK) { - return result; - } - dataType = numType; - } - if (values.empty()) { - fields.push_back(new SingleDataField(name, comment, unit, dataType, partType, m_length)); - } else if (numeric) { - fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(dataType), partType, m_length, values)); - } else { - return RESULT_ERR_INVALID_ARG; - } - return RESULT_OK; + string unit, const PartType partType, + int divisor, map values, + vector& fields) { + if (m_partType != pt_any && partType == pt_any) { + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance + } + bool numeric = m_dataType->isNumeric(); + if (!numeric && (divisor != 0 || !values.empty())) { + return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for non-numeric field + } + if (name.empty()) { + name = m_name; + } + if (comment.empty()) { + comment = m_comment; + } + if (unit.empty()) { + unit = m_unit; + } + DataType* dataType = m_dataType; + if (numeric) { + NumberDataType* numType = reinterpret_cast(dataType); + result_t result = numType->derive(divisor, 0, numType); + if (result != RESULT_OK) { + return result; + } + dataType = numType; + } + if (values.empty()) { + fields.push_back(new SingleDataField(name, comment, unit, dataType, partType, m_length)); + } else if (numeric) { + fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(dataType), partType, m_length, values)); + } else { + return RESULT_ERR_INVALID_ARG; + } + return RESULT_OK; } bool SingleDataField::hasField(const char* fieldName, bool numeric) { - bool numericType = m_dataType->isNumeric(); - return numeric == numericType && (fieldName == NULL || fieldName == m_name); + bool numericType = m_dataType->isNumeric(); + return numeric == numericType && (fieldName == NULL || fieldName == m_name); } unsigned char SingleDataField::getLength(PartType partType, unsigned char maxLength) { - if (partType != m_partType) { - return (unsigned char)0; - } - bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); - return remainder ? maxLength : m_length; + if (partType != m_partType) { + return (unsigned char)0; + } + bool remainder = m_length == REMAIN_LEN && m_dataType->isAdjustableLength(); + return remainder ? maxLength : m_length; } bool SingleDataField::hasFullByteOffset(bool after) { - if (m_length > 1 || !m_dataType->isNumeric()) { - return true; - } - NumberDataType* num = reinterpret_cast(m_dataType); - return (num->getBitCount() % 8) == 0 - || (after && num->getFirstBit() + (num->getBitCount() % 8) >= 8); + if (m_length > 1 || !m_dataType->isNumeric()) { + return true; + } + NumberDataType* num = reinterpret_cast(m_dataType); + return (num->getBitCount() % 8) == 0 + || (after && num->getFirstBit() + (num->getBitCount() % 8) >= 8); } ValueListDataField* ValueListDataField::clone() { - return new ValueListDataField(*this); + return new ValueListDataField(*this); } result_t ValueListDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { - if (m_partType != pt_any && partType == pt_any) { - return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance - } - if (name.empty()) { - name = m_name; - } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; - } - if (divisor != 0 && divisor != 1) { - return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field - } - if (!m_dataType->isNumeric()) { - return RESULT_ERR_INVALID_ARG; - } - if (!values.empty()) { - NumberDataType* num = reinterpret_cast(m_dataType); - if (values.begin()->first < num->getMinValue() || values.rbegin()->first > num->getMaxValue()) { - return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field - } - } else { - values = m_values; - } - fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(m_dataType), partType, m_length, values)); - return RESULT_OK; + string unit, const PartType partType, + int divisor, map values, + vector& fields) { + if (m_partType != pt_any && partType == pt_any) { + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance + } + if (name.empty()) { + name = m_name; + } + if (comment.empty()) { + comment = m_comment; + } + if (unit.empty()) { + unit = m_unit; + } + if (divisor != 0 && divisor != 1) { + return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field + } + if (!m_dataType->isNumeric()) { + return RESULT_ERR_INVALID_ARG; + } + if (!values.empty()) { + NumberDataType* num = reinterpret_cast(m_dataType); + if (values.begin()->first < num->getMinValue() || values.rbegin()->first > num->getMaxValue()) { + return RESULT_ERR_INVALID_ARG; // cannot use divisor != 1 for value list field + } + } else { + values = m_values; + } + fields.push_back(new ValueListDataField(name, comment, unit, reinterpret_cast(m_dataType), partType, m_length, values)); + return RESULT_OK; } void ValueListDataField::dump(ostream& output) { - output << setw(0) << dec; // initialize formatting - dumpString(output, m_name, false); - output << FIELD_SEPARATOR; - if (m_partType == pt_masterData) { - output << "m"; - } else if (m_partType == pt_slaveData) { - output << "s"; - } - output << FIELD_SEPARATOR; - if (!m_dataType->dump(output, m_length)) { // no divisor appended - for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { - if (it != m_values.begin()) { - output << VALUE_SEPARATOR; - } - output << static_cast(it->first) << "=" << it->second; - } - } // else: impossible since divisor is not allowed for ValueListDataField - dumpString(output, m_unit); - dumpString(output, m_comment); + output << setw(0) << dec; // initialize formatting + dumpString(output, m_name, false); + output << FIELD_SEPARATOR; + if (m_partType == pt_masterData) { + output << "m"; + } else if (m_partType == pt_slaveData) { + output << "s"; + } + output << FIELD_SEPARATOR; + if (!m_dataType->dump(output, m_length)) { // no divisor appended + for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { + if (it != m_values.begin()) { + output << VALUE_SEPARATOR; + } + output << static_cast(it->first) << "=" << it->second; + } + } // else: impossible since divisor is not allowed for ValueListDataField + dumpString(output, m_unit); + dumpString(output, m_comment); } result_t ValueListDataField::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat) { - unsigned int value = 0; + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat) { + unsigned int value = 0; - result_t result = m_dataType->readRawValue(input, offset, m_length, value); - if (result != RESULT_OK) { - return result; - } - map::iterator it = m_values.find(value); - if (it == m_values.end() && value != m_dataType->getReplacement()) { - // fall back to raw value in input - output << setw(0) << dec << static_cast(value); - return RESULT_OK; - } - if (it == m_values.end()) { - if (outputFormat & OF_JSON) { - output << "null"; - } else if (value == m_dataType->getReplacement()) { - output << NULL_VALUE; - } - } else if (outputFormat & OF_NUMERIC) { - output << setw(0) << dec << static_cast(value); - } else if (outputFormat & OF_JSON) { - output << '"' << it->second << '"'; - } else { - output << it->second; - } - return RESULT_OK; + result_t result = m_dataType->readRawValue(input, offset, m_length, value); + if (result != RESULT_OK) { + return result; + } + map::iterator it = m_values.find(value); + if (it == m_values.end() && value != m_dataType->getReplacement()) { + // fall back to raw value in input + output << setw(0) << dec << static_cast(value); + return RESULT_OK; + } + if (it == m_values.end()) { + if (outputFormat & OF_JSON) { + output << "null"; + } else if (value == m_dataType->getReplacement()) { + output << NULL_VALUE; + } + } else if (outputFormat & OF_NUMERIC) { + output << setw(0) << dec << static_cast(value); + } else if (outputFormat & OF_JSON) { + output << '"' << it->second << '"'; + } else { + output << it->second; + } + return RESULT_OK; } result_t ValueListDataField::writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - NumberDataType* numType = reinterpret_cast(m_dataType); - if (isIgnored()) { - return numType->writeRawValue(numType->getReplacement(), offset, m_length, output, usedLength); // replacement value - } - const char* str = input.str().c_str(); + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + NumberDataType* numType = reinterpret_cast(m_dataType); + if (isIgnored()) { + return numType->writeRawValue(numType->getReplacement(), offset, m_length, output, usedLength); // replacement value + } + const char* str = input.str().c_str(); - for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { - if (it->second.compare(str) == 0) { - return numType->writeRawValue(it->first, offset, m_length, output, usedLength); - } - } - if (strcasecmp(str, NULL_VALUE) == 0) { - return numType->writeRawValue(numType->getReplacement(), offset, m_length, output, usedLength); // replacement value - } - char* strEnd = NULL; // fall back to raw value in input - unsigned int value; - value = (unsigned int)strtoul(str, &strEnd, 10); - if (strEnd == NULL || strEnd == str || (*strEnd != 0 && *strEnd != '.')) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - if (m_values.find(value) != m_values.end()) { - return numType->writeRawValue(value, offset, m_length, output, usedLength); - } - return RESULT_ERR_NOTFOUND; // value assignment not found + for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { + if (it->second.compare(str) == 0) { + return numType->writeRawValue(it->first, offset, m_length, output, usedLength); + } + } + if (strcasecmp(str, NULL_VALUE) == 0) { + return numType->writeRawValue(numType->getReplacement(), offset, m_length, output, usedLength); // replacement value + } + char* strEnd = NULL; // fall back to raw value in input + unsigned int value; + value = (unsigned int)strtoul(str, &strEnd, 10); + if (strEnd == NULL || strEnd == str || (*strEnd != 0 && *strEnd != '.')) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + if (m_values.find(value) != m_values.end()) { + return numType->writeRawValue(value, offset, m_length, output, usedLength); + } + return RESULT_ERR_NOTFOUND; // value assignment not found } ConstantDataField* ConstantDataField::clone() { - return new ConstantDataField(*this); + return new ConstantDataField(*this); } result_t ConstantDataField::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { - if (m_partType != pt_any && partType == pt_any) { - return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance - } - if (name.empty()) { - name = m_name; - } - if (comment.empty()) { - comment = m_comment; - } - if (unit.empty()) { - unit = m_unit; - } - if (divisor != 0) { - return RESULT_ERR_INVALID_ARG; // cannot use other than current divisor for constant value field - } - if (!values.empty()) { - return RESULT_ERR_INVALID_ARG; // cannot use value list for constant value field - } - fields.push_back(new ConstantDataField(name, comment, unit, m_dataType, partType, m_length, m_value, m_verify)); - return RESULT_OK; + string unit, const PartType partType, + int divisor, map values, + vector& fields) { + if (m_partType != pt_any && partType == pt_any) { + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance + } + if (name.empty()) { + name = m_name; + } + if (comment.empty()) { + comment = m_comment; + } + if (unit.empty()) { + unit = m_unit; + } + if (divisor != 0) { + return RESULT_ERR_INVALID_ARG; // cannot use other than current divisor for constant value field + } + if (!values.empty()) { + return RESULT_ERR_INVALID_ARG; // cannot use value list for constant value field + } + fields.push_back(new ConstantDataField(name, comment, unit, m_dataType, partType, m_length, m_value, m_verify)); + return RESULT_OK; } void ConstantDataField::dump(ostream& output) { - output << setw(0) << dec; // initialize formatting - dumpString(output, m_name, false); - output << FIELD_SEPARATOR; - if (m_partType == pt_masterData) { - output << "m"; - } else if (m_partType == pt_slaveData) { - output << "s"; - } - output << FIELD_SEPARATOR; - if (!m_dataType->dump(output, m_length)) { // no divisor appended - output << (m_verify?"==":"=") << m_value; - } // else: impossible since divisor is not allowed for ConstantDataField - dumpString(output, m_unit); - dumpString(output, m_comment); + output << setw(0) << dec; // initialize formatting + dumpString(output, m_name, false); + output << FIELD_SEPARATOR; + if (m_partType == pt_masterData) { + output << "m"; + } else if (m_partType == pt_slaveData) { + output << "s"; + } + output << FIELD_SEPARATOR; + if (!m_dataType->dump(output, m_length)) { // no divisor appended + output << (m_verify?"==":"=") << m_value; + } // else: impossible since divisor is not allowed for ConstantDataField + dumpString(output, m_unit); + dumpString(output, m_comment); } result_t ConstantDataField::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat) { - ostringstream coutput; - result_t result = SingleDataField::readSymbols(input, isMaster, offset, coutput, 0); - if (result != RESULT_OK) { - return result; - } - if (m_verify) { - string value = coutput.str(); - FileReader::trim(value); - if (value != m_value) { - return RESULT_ERR_OUT_OF_RANGE; - } - } - return RESULT_OK; + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat) { + ostringstream coutput; + result_t result = SingleDataField::readSymbols(input, isMaster, offset, coutput, 0); + if (result != RESULT_OK) { + return result; + } + if (m_verify) { + string value = coutput.str(); + FileReader::trim(value); + if (value != m_value) { + return RESULT_ERR_OUT_OF_RANGE; + } + } + return RESULT_OK; } result_t ConstantDataField::writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - istringstream cinput(m_value); - return SingleDataField::writeSymbols(cinput, offset, output, isMaster, usedLength); + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + istringstream cinput(m_value); + return SingleDataField::writeSymbols(cinput, offset, output, isMaster, usedLength); } DataFieldSet* DataFieldSet::s_identFields = NULL; DataFieldSet* DataFieldSet::getIdentFields() { - if (s_identFields == NULL) { - NumberDataType* uchDataType = reinterpret_cast(DataTypeList::getInstance()->get("UCH")); - StringDataType* stringDataType = reinterpret_cast(DataTypeList::getInstance()->get("STR")); - NumberDataType* pinDataType = reinterpret_cast(DataTypeList::getInstance()->get("PIN")); - map manufacturers; - manufacturers[0x06] = "Dungs"; - manufacturers[0x0f] = "FH Ostfalia"; - manufacturers[0x10] = "TEM"; - manufacturers[0x11] = "Lamberti"; - manufacturers[0x14] = "CEB"; - manufacturers[0x15] = "Landis-Staefa"; - manufacturers[0x16] = "FERRO"; - manufacturers[0x17] = "MONDIAL"; - manufacturers[0x18] = "Wikon"; - manufacturers[0x19] = "Wolf"; - manufacturers[0x20] = "RAWE"; - manufacturers[0x30] = "Satronic"; - manufacturers[0x40] = "ENCON"; - manufacturers[0x50] = "Kromschroeder"; - manufacturers[0x60] = "Eberle"; - manufacturers[0x65] = "EBV"; - manufacturers[0x75] = "Graesslin"; - manufacturers[0x85] = "ebm-papst"; - manufacturers[0x95] = "SIG"; - manufacturers[0xa5] = "Theben"; - manufacturers[0xa7] = "Thermowatt"; - manufacturers[0xb5] = "Vaillant"; - manufacturers[0xc0] = "Toby"; - manufacturers[0xc5] = "Weishaupt"; - manufacturers[0xfd] = "ebusd.eu"; - vector fields; - fields.push_back(new ValueListDataField("MF", "", "", uchDataType, pt_slaveData, 1, manufacturers)); - fields.push_back(new SingleDataField("ID", "", "", stringDataType, pt_slaveData, 5)); - fields.push_back(new SingleDataField("SW", "", "", pinDataType, pt_slaveData, 2)); - fields.push_back(new SingleDataField("HW", "", "", pinDataType, pt_slaveData, 2)); - s_identFields = new DataFieldSet("ident", "", fields); - } - return s_identFields; + if (s_identFields == NULL) { + NumberDataType* uchDataType = reinterpret_cast(DataTypeList::getInstance()->get("UCH")); + StringDataType* stringDataType = reinterpret_cast(DataTypeList::getInstance()->get("STR")); + NumberDataType* pinDataType = reinterpret_cast(DataTypeList::getInstance()->get("PIN")); + map manufacturers; + manufacturers[0x06] = "Dungs"; + manufacturers[0x0f] = "FH Ostfalia"; + manufacturers[0x10] = "TEM"; + manufacturers[0x11] = "Lamberti"; + manufacturers[0x14] = "CEB"; + manufacturers[0x15] = "Landis-Staefa"; + manufacturers[0x16] = "FERRO"; + manufacturers[0x17] = "MONDIAL"; + manufacturers[0x18] = "Wikon"; + manufacturers[0x19] = "Wolf"; + manufacturers[0x20] = "RAWE"; + manufacturers[0x30] = "Satronic"; + manufacturers[0x40] = "ENCON"; + manufacturers[0x50] = "Kromschroeder"; + manufacturers[0x60] = "Eberle"; + manufacturers[0x65] = "EBV"; + manufacturers[0x75] = "Graesslin"; + manufacturers[0x85] = "ebm-papst"; + manufacturers[0x95] = "SIG"; + manufacturers[0xa5] = "Theben"; + manufacturers[0xa7] = "Thermowatt"; + manufacturers[0xb5] = "Vaillant"; + manufacturers[0xc0] = "Toby"; + manufacturers[0xc5] = "Weishaupt"; + manufacturers[0xfd] = "ebusd.eu"; + vector fields; + fields.push_back(new ValueListDataField("MF", "", "", uchDataType, pt_slaveData, 1, manufacturers)); + fields.push_back(new SingleDataField("ID", "", "", stringDataType, pt_slaveData, 5)); + fields.push_back(new SingleDataField("SW", "", "", pinDataType, pt_slaveData, 2)); + fields.push_back(new SingleDataField("HW", "", "", pinDataType, pt_slaveData, 2)); + s_identFields = new DataFieldSet("ident", "", fields); + } + return s_identFields; } DataFieldSet::~DataFieldSet() { - while (!m_fields.empty()) { - delete m_fields.back(); - m_fields.pop_back(); - } + while (!m_fields.empty()) { + delete m_fields.back(); + m_fields.pop_back(); + } } DataFieldSet* DataFieldSet::clone() { - vector fields; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - fields.push_back((*it)->clone()); - } - return new DataFieldSet(m_name, m_comment, fields); + vector fields; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + fields.push_back((*it)->clone()); + } + return new DataFieldSet(m_name, m_comment, fields); } unsigned char DataFieldSet::getLength(PartType partType, unsigned char maxLength) { - unsigned char length = 0; - bool previousFullByteOffset[] = { true, true, true, true }; + unsigned char length = 0; + bool previousFullByteOffset[] = { true, true, true, true }; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; - if (field->getPartType() == partType) { - if (!previousFullByteOffset[partType] && !field->hasFullByteOffset(false)) { - length--; - } - unsigned char fieldLength = field->getLength(partType, maxLength); - if (fieldLength >= maxLength) { - maxLength = 0; - } else { - maxLength = (unsigned char)(maxLength-fieldLength); - } - length = (unsigned char)(length + fieldLength); + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (field->getPartType() == partType) { + if (!previousFullByteOffset[partType] && !field->hasFullByteOffset(false)) { + length--; + } + unsigned char fieldLength = field->getLength(partType, maxLength); + if (fieldLength >= maxLength) { + maxLength = 0; + } else { + maxLength = (unsigned char)(maxLength-fieldLength); + } + length = (unsigned char)(length + fieldLength); - previousFullByteOffset[partType] = field->hasFullByteOffset(true); - } - } + previousFullByteOffset[partType] = field->hasFullByteOffset(true); + } + } - return length; + return length; } string DataFieldSet::getName(signed char fieldIndex) { - if (fieldIndex < 0) { - return m_name; - } - if ((unsigned char)fieldIndex >= m_fields.size()) { - return ""; - } - if (m_uniqueNames) { - return m_fields[fieldIndex]->getName(); - } - ostringstream ostream; - ostream << static_cast(fieldIndex); - return ostream.str(); + if (fieldIndex < 0) { + return m_name; + } + if ((unsigned char)fieldIndex >= m_fields.size()) { + return ""; + } + if (m_uniqueNames) { + return m_fields[fieldIndex]->getName(); + } + ostringstream ostream; + ostream << static_cast(fieldIndex); + return ostream.str(); } result_t DataFieldSet::derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) { - if (!values.empty()) { - return RESULT_ERR_INVALID_ARG; // value list not allowed in set derive - } - bool first = true; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - result_t result = (*it)->derive("", first?comment:"", first?unit:"", partType, divisor, values, fields); - if (result != RESULT_OK) { - return result; - } - first = false; - } + string unit, const PartType partType, + int divisor, map values, + vector& fields) { + if (!values.empty()) { + return RESULT_ERR_INVALID_ARG; // value list not allowed in set derive + } + bool first = true; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + result_t result = (*it)->derive("", first?comment:"", first?unit:"", partType, divisor, values, fields); + if (result != RESULT_OK) { + return result; + } + first = false; + } - return RESULT_OK; + return RESULT_OK; } bool DataFieldSet::hasField(const char* fieldName, bool numeric) { - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; - if (field->hasField(fieldName, numeric) == 0) { - return true; - } - } - return false; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (field->hasField(fieldName, numeric) == 0) { + return true; + } + } + return false; } void DataFieldSet::dump(ostream& output) { - bool first = true; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - if (first) { - first = false; - } else { - output << FIELD_SEPARATOR; - } - (*it)->dump(output); - } + bool first = true; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + if (first) { + first = false; + } else { + output << FIELD_SEPARATOR; + } + (*it)->dump(output); + } } result_t DataFieldSet::read(const PartType partType, - SymbolString& data, unsigned char offset, - unsigned int& output, const char* fieldName, signed char fieldIndex) { - bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; - if (partType != pt_any && field->getPartType() != partType) { - continue; - } - if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { - offset--; - } - result_t result = field->read(partType, data, offset, output, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); - previousFullByteOffset = field->hasFullByteOffset(true); - if (result != RESULT_EMPTY) { - found = true; - } - if (findFieldIndex && fieldName == field->getName()) { - if (fieldIndex == 0) { - if (!found) { - return RESULT_ERR_NOTFOUND; - } - break; - } - fieldIndex--; - } - } + SymbolString& data, unsigned char offset, + unsigned int& output, const char* fieldName, signed char fieldIndex) { + bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (partType != pt_any && field->getPartType() != partType) { + continue; + } + if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { + offset--; + } + result_t result = field->read(partType, data, offset, output, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); + previousFullByteOffset = field->hasFullByteOffset(true); + if (result != RESULT_EMPTY) { + found = true; + } + if (findFieldIndex && fieldName == field->getName()) { + if (fieldIndex == 0) { + if (!found) { + return RESULT_ERR_NOTFOUND; + } + break; + } + fieldIndex--; + } + } - if (!found) { - return RESULT_EMPTY; - } + if (!found) { + return RESULT_EMPTY; + } - return RESULT_OK; + return RESULT_OK; } result_t DataFieldSet::read(const PartType partType, - SymbolString& data, unsigned char offset, - ostringstream& output, OutputFormat outputFormat, signed char outputIndex, - bool leadingSeparator, const char* fieldName, signed char fieldIndex) { - bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; - if (!m_uniqueNames && outputIndex < 0) { - outputIndex = 0; - } - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; - if (partType != pt_any && field->getPartType() != partType) { - if (outputIndex >= 0 && !field->isIgnored()) { - outputIndex++; - } - continue; - } - if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { - offset--; - } - result_t result = field->read(partType, data, offset, output, outputFormat, outputIndex, leadingSeparator, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); - previousFullByteOffset = field->hasFullByteOffset(true); - if (result != RESULT_EMPTY) { - found = true; - leadingSeparator = true; - } - if (findFieldIndex && fieldName == field->getName()) { - if (fieldIndex == 0) { - if (!found) { - return RESULT_ERR_NOTFOUND; - } - break; - } - fieldIndex--; - } - if (outputIndex >= 0 && !field->isIgnored()) { - outputIndex++; - } - } + SymbolString& data, unsigned char offset, + ostringstream& output, OutputFormat outputFormat, signed char outputIndex, + bool leadingSeparator, const char* fieldName, signed char fieldIndex) { + bool previousFullByteOffset = true, found = false, findFieldIndex = fieldName != NULL && fieldIndex >= 0; + if (!m_uniqueNames && outputIndex < 0) { + outputIndex = 0; + } + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (partType != pt_any && field->getPartType() != partType) { + if (outputIndex >= 0 && !field->isIgnored()) { + outputIndex++; + } + continue; + } + if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { + offset--; + } + result_t result = field->read(partType, data, offset, output, outputFormat, outputIndex, leadingSeparator, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + offset = (unsigned char)(offset + field->getLength(partType, (unsigned char)(data.size()-offset))); + previousFullByteOffset = field->hasFullByteOffset(true); + if (result != RESULT_EMPTY) { + found = true; + leadingSeparator = true; + } + if (findFieldIndex && fieldName == field->getName()) { + if (fieldIndex == 0) { + if (!found) { + return RESULT_ERR_NOTFOUND; + } + break; + } + fieldIndex--; + } + if (outputIndex >= 0 && !field->isIgnored()) { + outputIndex++; + } + } - if (!found) { - return RESULT_EMPTY; - } - if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { - if (outputFormat & OF_JSON) { - output << ",\"comment\": \"" << m_comment << '"'; - } else { - output << " [" << m_comment << "]"; - } - } + if (!found) { + return RESULT_EMPTY; + } + if ((outputFormat & OF_COMMENTS) && m_comment.length() > 0) { + if (outputFormat & OF_JSON) { + output << ",\"comment\": \"" << m_comment << '"'; + } else { + output << " [" << m_comment << "]"; + } + } - return RESULT_OK; + return RESULT_OK; } result_t DataFieldSet::write(istringstream& input, - const PartType partType, SymbolString& data, - unsigned char offset, char separator, unsigned char* length) { - string token; + const PartType partType, SymbolString& data, + unsigned char offset, char separator, unsigned char* length) { + string token; - bool previousFullByteOffset = true; - unsigned char baseOffset = offset; - for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { - SingleDataField* field = *it; - if (partType != pt_any && field->getPartType() != partType) { - continue; - } - if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { - offset--; - } - result_t result; - unsigned char fieldLength; - if (m_fields.size() > 1) { - if (field->isIgnored()) { - token.clear(); - } else if (!getline(input, token, separator)) { - token.clear(); - } - istringstream single(token); - result = (*it)->write(single, partType, data, offset, separator, &fieldLength); - } else { - result = (*it)->write(input, partType, data, offset, separator, &fieldLength); - } - if (result != RESULT_OK) { - return result; - } - offset = (unsigned char)(offset+fieldLength); - previousFullByteOffset = field->hasFullByteOffset(true); - } + bool previousFullByteOffset = true; + unsigned char baseOffset = offset; + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { + SingleDataField* field = *it; + if (partType != pt_any && field->getPartType() != partType) { + continue; + } + if (!previousFullByteOffset && !field->hasFullByteOffset(false)) { + offset--; + } + result_t result; + unsigned char fieldLength; + if (m_fields.size() > 1) { + if (field->isIgnored()) { + token.clear(); + } else if (!getline(input, token, separator)) { + token.clear(); + } + istringstream single(token); + result = (*it)->write(single, partType, data, offset, separator, &fieldLength); + } else { + result = (*it)->write(input, partType, data, offset, separator, &fieldLength); + } + if (result != RESULT_OK) { + return result; + } + offset = (unsigned char)(offset+fieldLength); + previousFullByteOffset = field->hasFullByteOffset(true); + } - if (length != NULL) { - *length = (unsigned char)(offset-baseOffset); - } - return RESULT_OK; + if (length != NULL) { + *length = (unsigned char)(offset-baseOffset); + } + return RESULT_OK; } DataFieldTemplates::DataFieldTemplates(DataFieldTemplates& other) - : FileReader::FileReader(false) { - for (map::iterator it = other.m_fieldsByName.begin(); it != other.m_fieldsByName.end(); it++) { - m_fieldsByName[it->first] = it->second->clone(); - } + : FileReader::FileReader(false) { + for (map::iterator it = other.m_fieldsByName.begin(); it != other.m_fieldsByName.end(); it++) { + m_fieldsByName[it->first] = it->second->clone(); + } } void DataFieldTemplates::clear() { - for (map::iterator it = m_fieldsByName.begin(); it != m_fieldsByName.end(); it++) { - delete it->second; - it->second = NULL; - } - m_fieldsByName.clear(); + for (map::iterator it = m_fieldsByName.begin(); it != m_fieldsByName.end(); it++) { + delete it->second; + it->second = NULL; + } + m_fieldsByName.clear(); } result_t DataFieldTemplates::add(DataField* field, string name, bool replace) { - if (name.length() == 0) { - name = field->getName(); - } - map::iterator it = m_fieldsByName.find(name); - if (it != m_fieldsByName.end()) { - if (!replace) { - return RESULT_ERR_DUPLICATE_NAME; // duplicate key - } - delete it->second; - it->second = field; - return RESULT_OK; - } + if (name.length() == 0) { + name = field->getName(); + } + map::iterator it = m_fieldsByName.find(name); + if (it != m_fieldsByName.end()) { + if (!replace) { + return RESULT_ERR_DUPLICATE_NAME; // duplicate key + } + delete it->second; + it->second = field; + return RESULT_OK; + } - m_fieldsByName[name] = field; - return RESULT_OK; + m_fieldsByName[name] = field; + return RESULT_OK; } result_t DataFieldTemplates::addFromFile(vector::iterator& begin, const vector::iterator end, - vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - const string& filename, unsigned int lineNo) { - vector::iterator restart = begin; - DataField* field = NULL; - string name; - if (begin != end) { - size_t colon = begin->find(':'); - if (colon != string::npos) { - name = begin->substr(0, colon); - begin->erase(0, colon+1); - } - } - result_t result = DataField::create(begin, end, this, field, false, true, false); - if (result != RESULT_OK) { - return result; - } - result = add(field, name, true); - if (result == RESULT_ERR_DUPLICATE_NAME) { - begin = restart+1; // mark name as invalid - } - if (result != RESULT_OK) { - delete field; - } - return result; + vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + const string& filename, unsigned int lineNo) { + vector::iterator restart = begin; + DataField* field = NULL; + string name; + if (begin != end) { + size_t colon = begin->find(':'); + if (colon != string::npos) { + name = begin->substr(0, colon); + begin->erase(0, colon+1); + } + } + result_t result = DataField::create(begin, end, this, field, false, true, false); + if (result != RESULT_OK) { + return result; + } + result = add(field, name, true); + if (result == RESULT_ERR_DUPLICATE_NAME) { + begin = restart+1; // mark name as invalid + } + if (result != RESULT_OK) { + delete field; + } + return result; } DataField* DataFieldTemplates::get(const string name) { - map::const_iterator ref = m_fieldsByName.find(name); - if (ref == m_fieldsByName.end()) { - return NULL; - } - return ref->second; + map::const_iterator ref = m_fieldsByName.find(name); + if (ref == m_fieldsByName.end()) { + return NULL; + } + return ref->second; } } // namespace ebusd diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index d3dd09dd..2b353eaa 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -55,162 +55,162 @@ class SingleDataField; * Base class for all kinds of data fields. */ class DataField { - public: - /** - * Constructs a new instance. - * @param name the field name. - * @param comment the field comment. - */ - DataField(const string name, const string comment) - : m_name(name), m_comment(comment) {} + public: + /** + * Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + */ + DataField(const string name, const string comment) + : m_name(name), m_comment(comment) {} - /** - * Destructor. - */ - virtual ~DataField() {} + /** + * Destructor. + */ + virtual ~DataField() {} - /** - * Clone this instance. - * @return a clone of this instance. - */ - virtual DataField* clone() = 0; + /** + * Clone this instance. + * @return a clone of this instance. + */ + virtual DataField* clone() = 0; - /** - * Factory method for creating new instances. - * @param it the iterator to traverse for the definition parts. - * @param end the iterator pointing to the end of the definition parts. - * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. - * @param returnField the variable in which to store the created instance. - * @param isWriteMessage whether the field is part of a write message (default false). - * @param isTemplate true for creating a template @a DataField. - * @param isBroadcastOrMasterDestination true if the destination bus address is @a BRODCAST or a master address. - * @param maxFieldLength the maximum allowed length of a single field (default @a MAX_POS). - * @return @a RESULT_OK on success, or an error code. - * Note: the caller needs to free the created instance. - */ - static result_t create(vector::iterator& it, const vector::iterator end, - DataFieldTemplates* templates, DataField*& returnField, - const bool isWriteMessage, - const bool isTemplate, const bool isBroadcastOrMasterDestination, - const unsigned char maxFieldLength = MAX_POS); + /** + * Factory method for creating new instances. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. + * @param returnField the variable in which to store the created instance. + * @param isWriteMessage whether the field is part of a write message (default false). + * @param isTemplate true for creating a template @a DataField. + * @param isBroadcastOrMasterDestination true if the destination bus address is @a BRODCAST or a master address. + * @param maxFieldLength the maximum allowed length of a single field (default @a MAX_POS). + * @return @a RESULT_OK on success, or an error code. + * Note: the caller needs to free the created instance. + */ + static result_t create(vector::iterator& it, const vector::iterator end, + DataFieldTemplates* templates, DataField*& returnField, + const bool isWriteMessage, + const bool isTemplate, const bool isBroadcastOrMasterDestination, + const unsigned char maxFieldLength = MAX_POS); - /** - * Dump the @a string optionally embedded in @a TEXT_SEPARATOR to the output. - * @param output the @a ostream to dump to. - * @param str the @a string to dump. - * @param prependFieldSeparator whether to start with a @a FIELD_SEPARATOR. - */ - static void dumpString(ostream& output, const string str, const bool prependFieldSeparator = true); + /** + * Dump the @a string optionally embedded in @a TEXT_SEPARATOR to the output. + * @param output the @a ostream to dump to. + * @param str the @a string to dump. + * @param prependFieldSeparator whether to start with a @a FIELD_SEPARATOR. + */ + static void dumpString(ostream& output, const string str, const bool prependFieldSeparator = true); - /** - * Returns the length of this field (or contained fields) in bytes. - * @param partType the message part of the contained fields to limit the length calculation to. - * @param maxLength the maximum length for calculating remainder of input. - * @return the length of this field (or contained fields) in bytes. - */ - virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN) = 0; + /** + * Returns the length of this field (or contained fields) in bytes. + * @param partType the message part of the contained fields to limit the length calculation to. + * @param maxLength the maximum length for calculating remainder of input. + * @return the length of this field (or contained fields) in bytes. + */ + virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN) = 0; - /** - * Derive a new @a DataField from this field. - * @param name the field name, or empty to use this fields name. - * @param comment the field comment, or empty to use this fields comment. - * @param unit the value unit, or empty to use this fields unit (if applicable). - * @param partType the message part in which the field is stored. - * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). - * @param values the value=text assignments, or empty to use this fields assignments (if applicable). - * @param fields the @a vector to which created @a SingleDataField instances shall be added. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields) = 0; + /** + * Derive a new @a DataField from this field. + * @param name the field name, or empty to use this fields name. + * @param comment the field comment, or empty to use this fields comment. + * @param unit the value unit, or empty to use this fields unit (if applicable). + * @param partType the message part in which the field is stored. + * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). + * @param values the value=text assignments, or empty to use this fields assignments (if applicable). + * @param fields the @a vector to which created @a SingleDataField instances shall be added. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + int divisor, map values, + vector& fields) = 0; - /** - * Get the specified field name. - * @param fieldIndex the index of the field, or -1 for this. - * @return the field name, or the index as string if not unique or not available. - */ - virtual string getName(signed char fieldIndex = -1) { return m_name; } + /** + * Get the specified field name. + * @param fieldIndex the index of the field, or -1 for this. + * @return the field name, or the index as string if not unique or not available. + */ + virtual string getName(signed char fieldIndex = -1) { return m_name; } - /** - * Get the field comment. - * @return the field comment. - */ - string getComment() const { return m_comment; } + /** + * Get the field comment. + * @return the field comment. + */ + string getComment() const { return m_comment; } - /** - * Dump the field settings to the output. - * @param output the @a ostream to dump to. - */ - virtual void dump(ostream& output) = 0; + /** + * Dump the field settings to the output. + * @param output the @a ostream to dump to. + */ + virtual void dump(ostream& output) = 0; - /** - * Return whether the field is available. - * @param fieldName the name of the field to find, or NULL for any. - * @param numeric true for a numeric field, false for a string field. - * @return true if the field is available. - */ - virtual bool hasField(const char* fieldName, bool numeric) = 0; + /** + * Return whether the field is available. + * @param fieldName the name of the field to find, or NULL for any. + * @param numeric true for a numeric field, false for a string field. + * @return true if the field is available. + */ + virtual bool hasField(const char* fieldName, bool numeric) = 0; - /** - * Reads the numeric value from the @a SymbolString. - * @param partType the @a PartType of the data. - * @param data the unescaped data @a SymbolString for reading binary data. - * @param offset the additional offset to add for reading binary data. - * @param output the variable in which to store the numeric value. - * @param fieldName the name of the field to read, or NULL for the first field. - * @param fieldIndex the optional index of the named field, or -1. - * @return @a RESULT_OK on success, - * or @a RESULT_EMPTY if the field was skipped (either if the partType does - * not match or ignored, or due to @a fieldName or @a fieldIndex), - * or an error code. - */ - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1) = 0; + /** + * Reads the numeric value from the @a SymbolString. + * @param partType the @a PartType of the data. + * @param data the unescaped data @a SymbolString for reading binary data. + * @param offset the additional offset to add for reading binary data. + * @param output the variable in which to store the numeric value. + * @param fieldName the name of the field to read, or NULL for the first field. + * @param fieldIndex the optional index of the named field, or -1. + * @return @a RESULT_OK on success, + * or @a RESULT_EMPTY if the field was skipped (either if the partType does + * not match or ignored, or due to @a fieldName or @a fieldIndex), + * or an error code. + */ + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1) = 0; - /** - * Reads the value from the @a SymbolString. - * @param partType the @a PartType of the data. - * @param data the unescaped data @a SymbolString for reading binary data. - * @param offset the additional offset to add for reading binary data. - * @param output the @a ostringstream to append the formatted value to. - * @param outputFormat the @a OutputFormat options to use. - * @param outputIndex the optional index of the field when using an indexed output format, or -1. - * @param leadingSeparator whether to prepend a separator before the formatted value. - * @param fieldName the optional name of a field to limit the output to. - * @param fieldIndex the optional index of the named field to limit the output to, or -1. - * @return @a RESULT_OK on success (or if the partType does not match), - * or @a RESULT_EMPTY if the field was skipped (either ignored or due to @a fieldName or @a fieldIndex), - * or an error code. - */ - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1) = 0; + /** + * Reads the value from the @a SymbolString. + * @param partType the @a PartType of the data. + * @param data the unescaped data @a SymbolString for reading binary data. + * @param offset the additional offset to add for reading binary data. + * @param output the @a ostringstream to append the formatted value to. + * @param outputFormat the @a OutputFormat options to use. + * @param outputIndex the optional index of the field when using an indexed output format, or -1. + * @param leadingSeparator whether to prepend a separator before the formatted value. + * @param fieldName the optional name of a field to limit the output to. + * @param fieldIndex the optional index of the named field to limit the output to, or -1. + * @return @a RESULT_OK on success (or if the partType does not match), + * or @a RESULT_EMPTY if the field was skipped (either ignored or due to @a fieldName or @a fieldIndex), + * or an error code. + */ + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1) = 0; - /** - * Writes the value to the master or slave @a SymbolString. - * @param input the @a istringstream to parse the formatted value from. - * @param partType the @a PartType of the data. - * @param data the unescaped data @a SymbolString for writing binary data. - * @param offset the additional offset to add for writing binary data. - * @param separator the separator character between multiple fields. - * @param length the variable in which to store the used length in bytes, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t write(istringstream& input, - const PartType partType, SymbolString& data, - unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL) = 0; + /** + * Writes the value to the master or slave @a SymbolString. + * @param input the @a istringstream to parse the formatted value from. + * @param partType the @a PartType of the data. + * @param data the unescaped data @a SymbolString for writing binary data. + * @param offset the additional offset to add for writing binary data. + * @param separator the separator character between multiple fields. + * @param length the variable in which to store the used length in bytes, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t write(istringstream& input, + const PartType partType, SymbolString& data, + unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL) = 0; - protected: - /** the field name. */ - const string m_name; + protected: + /** the field name. */ + const string m_name; - /** the field comment. */ - const string m_comment; + /** the field comment. */ + const string m_comment; }; @@ -218,148 +218,148 @@ class DataField { * A single @a DataField holding a value. */ class SingleDataField : public DataField { - public: - /** - * Constructs a new instance. - * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. - * @param dataType the data type definition. - * @param partType the message part in which the field is stored. - * @param length the number of symbols in the message part in which the field is stored. - */ - SingleDataField(const string name, const string comment, - const string unit, DataType* dataType, const PartType partType, - const unsigned char length) - : DataField(name, comment), - m_unit(unit), m_dataType(dataType), m_partType(partType), - m_length(length) {} + public: + /** + * Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @param unit the value unit. + * @param dataType the data type definition. + * @param partType the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + */ + SingleDataField(const string name, const string comment, + const string unit, DataType* dataType, const PartType partType, + const unsigned char length) + : DataField(name, comment), + m_unit(unit), m_dataType(dataType), m_partType(partType), + m_length(length) {} - /** - * Destructor. - */ - virtual ~SingleDataField() {} + /** + * Destructor. + */ + virtual ~SingleDataField() {} - // @copydoc - virtual SingleDataField* clone(); + // @copydoc + virtual SingleDataField* clone(); - /** - * Factory method for creating a new @a SingleDataField instance derived from a base type. - * @param id the ID string (excluding optional length suffix). - * @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part. - * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. - * @param partType the message part in which the field is stored. - * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). - * @param values the value=text assignments. - * @param constantValue the constant value as string, or empty. - * @param verifyValue whether to verify the read value against the constant value. - * @param returnField the variable in which the created @a SingleDataField instance shall be stored. - * @return @a RESULT_OK on success, or an error code. - * Note: the caller needs to free the created instance. - */ - static result_t create(const string id, const unsigned char length, - const string name, const string comment, const string unit, - const PartType partType, int divisor, map values, - const string constantValue, const bool verifyValue, SingleDataField* &returnField); + /** + * Factory method for creating a new @a SingleDataField instance derived from a base type. + * @param id the ID string (excluding optional length suffix). + * @param length the base type length, or 0 for default, or @a REMAIN_LEN for remainder within same message part. + * @param name the field name. + * @param comment the field comment. + * @param unit the value unit. + * @param partType the message part in which the field is stored. + * @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable). + * @param values the value=text assignments. + * @param constantValue the constant value as string, or empty. + * @param verifyValue whether to verify the read value against the constant value. + * @param returnField the variable in which the created @a SingleDataField instance shall be stored. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller needs to free the created instance. + */ + static result_t create(const string id, const unsigned char length, + const string name, const string comment, const string unit, + const PartType partType, int divisor, map values, + const string constantValue, const bool verifyValue, SingleDataField* &returnField); - /** - * Get the value unit. - * @return the value unit. - */ - string getUnit() const { return m_unit; } + /** + * Get the value unit. + * @return the value unit. + */ + string getUnit() const { return m_unit; } - /** - * Get whether this field is ignored. - * @return whether this field is ignored. - */ - bool isIgnored() const { return m_dataType->isIgnored(); } + /** + * Get whether this field is ignored. + * @return whether this field is ignored. + */ + bool isIgnored() const { return m_dataType->isIgnored(); } - /** - * Get the message part in which the field is stored. - * @return the message part in which the field is stored. - */ - PartType getPartType() const { return m_partType; } + /** + * Get the message part in which the field is stored. + * @return the message part in which the field is stored. + */ + PartType getPartType() const { return m_partType; } - // @copydoc - virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN); + // @copydoc + virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN); - // @copydoc - virtual result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields); + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + int divisor, map values, + vector& fields); - /** - * Get whether this field uses a full byte offset. - * @param after @p true to check after consuming the bits, @p false to check before. - * @return @p true if this field uses a full byte offset, @p false if this field - * only consumes a part of a byte and a subsequent field may re-use the same offset. - */ - bool hasFullByteOffset(bool after); + /** + * Get whether this field uses a full byte offset. + * @param after @p true to check after consuming the bits, @p false to check before. + * @return @p true if this field uses a full byte offset, @p false if this field + * only consumes a part of a byte and a subsequent field may re-use the same offset. + */ + bool hasFullByteOffset(bool after); - // @copydoc - virtual void dump(ostream& output); + // @copydoc + virtual void dump(ostream& output); - // @copydoc - virtual bool hasField(const char* fieldName, bool numeric); + // @copydoc + virtual bool hasField(const char* fieldName, bool numeric); - // @copydoc - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1); - // @copydoc - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); - // @copydoc - virtual result_t write(istringstream& input, - const PartType partType, SymbolString& data, - unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL); + // @copydoc + virtual result_t write(istringstream& input, + const PartType partType, SymbolString& data, + unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL); - protected: - /** - * Internal method for reading the field from a @a SymbolString. - * @param input the unescaped @a SymbolString to read the binary value from. - * @param isMaster whether the @a SymbolString is the master part. - * @param offset the offset in the @a SymbolString. - * @param output the ostringstream to append the formatted value to. - * @param outputFormat the @a OutputFormat options to use. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat); + protected: + /** + * Internal method for reading the field from a @a SymbolString. + * @param input the unescaped @a SymbolString to read the binary value from. + * @param isMaster whether the @a SymbolString is the master part. + * @param offset the offset in the @a SymbolString. + * @param output the ostringstream to append the formatted value to. + * @param outputFormat the @a OutputFormat options to use. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t readSymbols(SymbolString& input, const bool isMaster, + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat); - /** - * Internal method for writing the field to a @a SymbolString. - * @param input the @a istringstream to parse the formatted value from. - * @param offset the offset in the @a SymbolString. - * @param output the unescaped @a SymbolString to write the binary value to. - * @param isMaster whether the @a SymbolString is the master part. - * @param usedLength the variable in which to store the used length in bytes, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength); + /** + * Internal method for writing the field to a @a SymbolString. + * @param input the @a istringstream to parse the formatted value from. + * @param offset the offset in the @a SymbolString. + * @param output the unescaped @a SymbolString to write the binary value to. + * @param isMaster whether the @a SymbolString is the master part. + * @param usedLength the variable in which to store the used length in bytes, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t writeSymbols(istringstream& input, + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength); - /** the value unit. */ - const string m_unit; + /** the value unit. */ + const string m_unit; - /** the data type definition. */ - DataType* m_dataType; + /** the data type definition. */ + DataType* m_dataType; - /** the message part in which the field is stored. */ - const PartType m_partType; + /** the message part in which the field is stored. */ + const PartType m_partType; - /** the number of symbols in the message part in which the field is stored. */ - const unsigned char m_length; + /** the number of symbols in the message part in which the field is stored. */ + const unsigned char m_length; }; @@ -367,56 +367,56 @@ class SingleDataField : public DataField { * A numeric data field with a list of value=text assignments and a string representation. */ class ValueListDataField : public SingleDataField { - public: - /** - * Constructs a new instance. - * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. - * @param dataType the data type definition. - * @param partType the message part in which the field is stored. - * @param length the number of symbols in the message part in which the field is stored. - * @param values the value=text assignments. - */ - ValueListDataField(const string name, const string comment, - const string unit, NumberDataType* dataType, const PartType partType, - const unsigned char length, const map values) - : SingleDataField(name, comment, unit, dataType, partType, length), - m_values(values) {} + public: + /** + * Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @param unit the value unit. + * @param dataType the data type definition. + * @param partType the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param values the value=text assignments. + */ + ValueListDataField(const string name, const string comment, + const string unit, NumberDataType* dataType, const PartType partType, + const unsigned char length, const map values) + : SingleDataField(name, comment, unit, dataType, partType, length), + m_values(values) {} - /** - * Destructor. - */ - virtual ~ValueListDataField() {} + /** + * Destructor. + */ + virtual ~ValueListDataField() {} - // @copydoc - virtual ValueListDataField* clone(); + // @copydoc + virtual ValueListDataField* clone(); - // @copydoc - virtual result_t derive(string name, string comment, - string unit, const PartType partType, int divisor, - map values, - vector& fields); + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, int divisor, + map values, + vector& fields); - // @copydoc - virtual void dump(ostream& output); + // @copydoc + virtual void dump(ostream& output); - protected: - // @copydoc - virtual result_t readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat); + protected: + // @copydoc + virtual result_t readSymbols(SymbolString& input, const bool isMaster, + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat); - // @copydoc - virtual result_t writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength); + // @copydoc + virtual result_t writeSymbols(istringstream& input, + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength); - private: - /** the value=text assignments. */ - map m_values; + private: + /** the value=text assignments. */ + map m_values; }; @@ -424,60 +424,60 @@ class ValueListDataField : public SingleDataField { * A data field with a constant value. */ class ConstantDataField : public SingleDataField { - public: - /** - * Constructs a new instance. - * @param name the field name. - * @param comment the field comment. - * @param unit the value unit. - * @param dataType the data type definition. - * @param partType the message part in which the field is stored. - * @param length the number of symbols in the message part in which the field is stored. - * @param value the constant value. - * @param verify whether to verify the read value against the constant value. - */ - ConstantDataField(const string name, const string comment, - const string unit, DataType* dataType, const PartType partType, - const unsigned char length, const string value, const bool verify) - : SingleDataField(name, comment, unit, dataType, partType, length), - m_value(value), m_verify(verify) {} + public: + /** + * Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @param unit the value unit. + * @param dataType the data type definition. + * @param partType the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param value the constant value. + * @param verify whether to verify the read value against the constant value. + */ + ConstantDataField(const string name, const string comment, + const string unit, DataType* dataType, const PartType partType, + const unsigned char length, const string value, const bool verify) + : SingleDataField(name, comment, unit, dataType, partType, length), + m_value(value), m_verify(verify) {} - /** - * Destructor. - */ - virtual ~ConstantDataField() {} + /** + * Destructor. + */ + virtual ~ConstantDataField() {} - // @copydoc - virtual ConstantDataField* clone(); + // @copydoc + virtual ConstantDataField* clone(); - // @copydoc - virtual result_t derive(string name, string comment, - string unit, const PartType partType, int divisor, - map values, - vector& fields); + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, int divisor, + map values, + vector& fields); - // @copydoc - virtual void dump(ostream& output); + // @copydoc + virtual void dump(ostream& output); - protected: - // @copydoc - virtual result_t readSymbols(SymbolString& input, const bool isMaster, - const unsigned char offset, - ostringstream& output, OutputFormat outputFormat); + protected: + // @copydoc + virtual result_t readSymbols(SymbolString& input, const bool isMaster, + const unsigned char offset, + ostringstream& output, OutputFormat outputFormat); - // @copydoc - virtual result_t writeSymbols(istringstream& input, - const unsigned char offset, - SymbolString& output, const bool isMaster, unsigned char* usedLength); + // @copydoc + virtual result_t writeSymbols(istringstream& input, + const unsigned char offset, + SymbolString& output, const bool isMaster, unsigned char* usedLength); - private: - /** the constant value. */ - const string m_value; + private: + /** the constant value. */ + const string m_value; - /** whether to verify the read value against the constant value. */ - const bool m_verify; + /** whether to verify the read value against the constant value. */ + const bool m_verify; }; @@ -485,118 +485,118 @@ class ConstantDataField : public SingleDataField { * A set of @a DataField instances. */ class DataFieldSet : public DataField { - public: - /** - * Get the @a DataFieldSet for parsing the identification message (service 0x07 0x04). - * @return the @a DataFieldSet for parsing the identification message. This is:
    - *
  • manufacturer name (list of known values)
  • - *
  • identification string (5 characters)
  • - *
  • software version number (0000-9999)
  • - *
  • hardware version number (0000-9999)
  • - *
- * Note: the returned value may only be deleted once. - */ - static DataFieldSet* getIdentFields(); + public: + /** + * Get the @a DataFieldSet for parsing the identification message (service 0x07 0x04). + * @return the @a DataFieldSet for parsing the identification message. This is:
    + *
  • manufacturer name (list of known values)
  • + *
  • identification string (5 characters)
  • + *
  • software version number (0000-9999)
  • + *
  • hardware version number (0000-9999)
  • + *
+ * Note: the returned value may only be deleted once. + */ + static DataFieldSet* getIdentFields(); - /** - * Constructs a new instance. - * @param name the field name. - * @param comment the field comment. - * @param fields the @a vector of @a SingleDataField instances part of this set. - */ - DataFieldSet(const string name, const string comment, - const vector fields) - : DataField(name, comment), - m_fields(fields) { - bool uniqueNames = true; - map names; - for (vector::const_iterator it = fields.begin(); it != fields.end(); it++) { - SingleDataField* field = *it; - if (field->isIgnored()) { - continue; - } - string name = field->getName(); - if (name.empty() || names.find(name) != names.end()) { - uniqueNames = false; - break; - } - names[name] = name; - } - m_uniqueNames = uniqueNames; - } + /** + * Constructs a new instance. + * @param name the field name. + * @param comment the field comment. + * @param fields the @a vector of @a SingleDataField instances part of this set. + */ + DataFieldSet(const string name, const string comment, + const vector fields) + : DataField(name, comment), + m_fields(fields) { + bool uniqueNames = true; + map names; + for (vector::const_iterator it = fields.begin(); it != fields.end(); it++) { + SingleDataField* field = *it; + if (field->isIgnored()) { + continue; + } + string name = field->getName(); + if (name.empty() || names.find(name) != names.end()) { + uniqueNames = false; + break; + } + names[name] = name; + } + m_uniqueNames = uniqueNames; + } - /** - * Destructor. - */ - virtual ~DataFieldSet(); + /** + * Destructor. + */ + virtual ~DataFieldSet(); - // @copydoc - virtual DataFieldSet* clone(); + // @copydoc + virtual DataFieldSet* clone(); - // @copydoc - virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN); + // @copydoc + virtual unsigned char getLength(PartType partType, unsigned char maxLength = MAX_LEN); - // @copydoc - virtual string getName(signed char fieldIndex = -1); + // @copydoc + virtual string getName(signed char fieldIndex = -1); - // @copydoc - virtual result_t derive(string name, string comment, - string unit, const PartType partType, - int divisor, map values, - vector& fields); + // @copydoc + virtual result_t derive(string name, string comment, + string unit, const PartType partType, + int divisor, map values, + vector& fields); - /** - * Returns the @a SingleDataField at the specified index. - * @param index the index of the @a SingleDataField to return. - * @return the @a SingleDataField at the specified index, or NULL. - */ - SingleDataField* operator[](const size_t index) { if (index >= m_fields.size()) { return NULL; } return m_fields[index]; } + /** + * Returns the @a SingleDataField at the specified index. + * @param index the index of the @a SingleDataField to return. + * @return the @a SingleDataField at the specified index, or NULL. + */ + SingleDataField* operator[](const size_t index) { if (index >= m_fields.size()) { return NULL; } return m_fields[index]; } - /** - * Returns the @a SingleDataField at the specified index. - * @param index the index of the @a SingleDataField to return. - * @return the @a SingleDataField at the specified index, or NULL. - */ - const SingleDataField* operator[](const size_t index) const { if (index >= m_fields.size()) { return NULL; } return m_fields[index]; } + /** + * Returns the @a SingleDataField at the specified index. + * @param index the index of the @a SingleDataField to return. + * @return the @a SingleDataField at the specified index, or NULL. + */ + const SingleDataField* operator[](const size_t index) const { if (index >= m_fields.size()) { return NULL; } return m_fields[index]; } - /** - * Returns the number of @a SingleDataFields instances in this set. - * @return the number of available @a SingleDataField instances. - */ - size_t size() const { return m_fields.size(); } + /** + * Returns the number of @a SingleDataFields instances in this set. + * @return the number of available @a SingleDataField instances. + */ + size_t size() const { return m_fields.size(); } - // @copydoc - virtual bool hasField(const char* fieldName, bool numeric); + // @copydoc + virtual bool hasField(const char* fieldName, bool numeric); - // @copydoc - virtual void dump(ostream& output); + // @copydoc + virtual void dump(ostream& output); - // @copydoc - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + unsigned int& output, const char* fieldName = NULL, signed char fieldIndex = -1); - // @copydoc - virtual result_t read(const PartType partType, - SymbolString& data, unsigned char offset, - ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, - bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, OutputFormat outputFormat, signed char outputIndex = -1, + bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); - // @copydoc - virtual result_t write(istringstream& input, - const PartType partType, SymbolString& data, - unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL); + // @copydoc + virtual result_t write(istringstream& input, + const PartType partType, SymbolString& data, + unsigned char offset, char separator = UI_FIELD_SEPARATOR, unsigned char* length = NULL); - private: - /** the @a DataFieldSet containing the ident message @a SingleDataField instances, or NULL. */ - static DataFieldSet* s_identFields; + private: + /** the @a DataFieldSet containing the ident message @a SingleDataField instances, or NULL. */ + static DataFieldSet* s_identFields; - /** the @a vector of @a SingleDataField instances part of this set. */ - vector m_fields; + /** the @a vector of @a SingleDataField instances part of this set. */ + vector m_fields; - /** whether all fields have a unique name. */ - bool m_uniqueNames; + /** whether all fields have a unique name. */ + bool m_uniqueNames; }; @@ -604,57 +604,57 @@ class DataFieldSet : public DataField { * A map of template @a DataField instances. */ class DataFieldTemplates : public FileReader { - public: - /** - * Constructs a new instance. - */ - DataFieldTemplates() : FileReader::FileReader(false) {} + public: + /** + * Constructs a new instance. + */ + DataFieldTemplates() : FileReader::FileReader(false) {} - /** - * Constructs a new copied instance. - * @param other the @a DataFieldTemplates to copy from. - */ - DataFieldTemplates(DataFieldTemplates& other); + /** + * Constructs a new copied instance. + * @param other the @a DataFieldTemplates to copy from. + */ + DataFieldTemplates(DataFieldTemplates& other); - /** - * Destructor. - */ - virtual ~DataFieldTemplates() { - clear(); - } + /** + * Destructor. + */ + virtual ~DataFieldTemplates() { + clear(); + } - /** - * Removes all @a DataField instances. - */ - void clear(); + /** + * Removes all @a DataField instances. + */ + void clear(); - /** - * Adds a template @a DataField instance to this map. - * @param field the @a DataField instance to add. - * @param name the name to use in the map, or the empty string to use the @a DataField name. - * @param replace whether replacing an already stored instance is allowed. - * @return @a RESULT_OK on success, or an error code. - * Note: the caller may not free the added instance on success. - */ - result_t add(DataField* field, string name = "", bool replace = false); + /** + * Adds a template @a DataField instance to this map. + * @param field the @a DataField instance to add. + * @param name the name to use in the map, or the empty string to use the @a DataField name. + * @param replace whether replacing an already stored instance is allowed. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller may not free the added instance on success. + */ + result_t add(DataField* field, string name = "", bool replace = false); - // @copydoc - virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, - vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - const string& filename, unsigned int lineNo); + // @copydoc + virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, + vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + const string& filename, unsigned int lineNo); - /** - * Gets the template @a DataField instance with the specified name. - * @param name the name of the template to get. - * @return the template @a DataField instance, or NULL. - * Note: the caller may not free the returned instance. - */ - DataField* get(string name); + /** + * Gets the template @a DataField instance with the specified name. + * @param name the name of the template to get. + * @return the template @a DataField instance, or NULL. + * Note: the caller may not free the returned instance. + */ + DataField* get(string name); - private: - /** the known template @a DataField instances by name. */ - map m_fieldsByName; + private: + /** the known template @a DataField instances by name. */ + map m_fieldsByName; }; } // namespace ebusd diff --git a/src/lib/ebus/datatype.cpp b/src/lib/ebus/datatype.cpp index 2828232e..6bbc9b47 100644 --- a/src/lib/ebus/datatype.cpp +++ b/src/lib/ebus/datatype.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "datatype.h" @@ -28,7 +28,7 @@ #include #include #ifdef HAVE_CONTRIB -# include "contrib/contrib.h" +# include "contrib/contrib.h" #endif namespace ebusd { @@ -41,922 +41,922 @@ using std::setprecision; using std::setw; unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length) { - char* strEnd = NULL; + char* strEnd = NULL; - unsigned long ret = strtoul(str, &strEnd, base); + unsigned long ret = strtoul(str, &strEnd, base); - if (strEnd == NULL || strEnd == str || *strEnd != 0) { - result = RESULT_ERR_INVALID_NUM; // invalid value - return 0; - } + if (strEnd == NULL || strEnd == str || *strEnd != 0) { + result = RESULT_ERR_INVALID_NUM; // invalid value + return 0; + } - if (minValue > ret || ret > maxValue) { - result = RESULT_ERR_OUT_OF_RANGE; // invalid value - return 0; - } - if (length != NULL) { - *length = (unsigned int)(strEnd - str); - } - result = RESULT_OK; - return (unsigned int)ret; + if (minValue > ret || ret > maxValue) { + result = RESULT_ERR_OUT_OF_RANGE; // invalid value + return 0; + } + if (length != NULL) { + *length = (unsigned int)(strEnd - str); + } + result = RESULT_OK; + return (unsigned int)ret; } int parseSignedInt(const char* str, int base, const int minValue, const int maxValue, result_t& result, unsigned int* length) { - char* strEnd = NULL; + char* strEnd = NULL; - long ret = strtol(str, &strEnd, base); + long ret = strtol(str, &strEnd, base); - if (strEnd == NULL || *strEnd != 0) { - result = RESULT_ERR_INVALID_NUM; // invalid value - return 0; - } + if (strEnd == NULL || *strEnd != 0) { + result = RESULT_ERR_INVALID_NUM; // invalid value + return 0; + } - if (minValue > ret || ret > maxValue) { - result = RESULT_ERR_OUT_OF_RANGE; // invalid value - return 0; - } - if (length != NULL) { - *length = (unsigned int)(strEnd - str); - } - result = RESULT_OK; - return static_cast(ret); + if (minValue > ret || ret > maxValue) { + result = RESULT_ERR_OUT_OF_RANGE; // invalid value + return 0; + } + if (length != NULL) { + *length = (unsigned int)(strEnd - str); + } + result = RESULT_OK; + return static_cast(ret); } void printErrorPos(ostream& out, vector::iterator begin, const vector::iterator end, vector::iterator pos, string filename, size_t lineNo, result_t result) { - if (pos > begin) { - pos--; - } - out << "Error reading \"" << filename << "\" line " << setw(0) << dec << static_cast(lineNo) << " field " << static_cast(1+pos.base()-begin.base()) << " value \"" << *pos << "\": " << getResultCode(result) << endl; - out << "Erroneous item is here:" << endl; - bool first = true; - int cnt = 0; - while (begin != end) { - if (first) { - first = false; - } else { - out << FIELD_SEPARATOR; - if (begin <= pos) { - cnt++; - } - } - string item = *begin; - size_t i = item.find(TEXT_SEPARATOR); - if (i != string::npos) { - do { - item.replace(i, 1, TEXT_SEPARATOR_STR TEXT_SEPARATOR_STR); - i = item.find(TEXT_SEPARATOR, i+sizeof(TEXT_SEPARATOR_STR)+sizeof(TEXT_SEPARATOR_STR)); - } while (i != string::npos); - i = 0; - } else { - i = item.find(FIELD_SEPARATOR); - } - if (i != string::npos) { - out << TEXT_SEPARATOR << item << TEXT_SEPARATOR; - if (begin < pos) { - cnt += 2; - } else if (begin == pos) { - cnt++; - } - } else { - out << item; - } - if (begin < pos) { - cnt += (unsigned int)(item).length(); - } - begin++; - } - out << endl; - out << setw(cnt) << " " << setw(0) << "^" << endl; + if (pos > begin) { + pos--; + } + out << "Error reading \"" << filename << "\" line " << setw(0) << dec << static_cast(lineNo) << " field " << static_cast(1+pos.base()-begin.base()) << " value \"" << *pos << "\": " << getResultCode(result) << endl; + out << "Erroneous item is here:" << endl; + bool first = true; + int cnt = 0; + while (begin != end) { + if (first) { + first = false; + } else { + out << FIELD_SEPARATOR; + if (begin <= pos) { + cnt++; + } + } + string item = *begin; + size_t i = item.find(TEXT_SEPARATOR); + if (i != string::npos) { + do { + item.replace(i, 1, TEXT_SEPARATOR_STR TEXT_SEPARATOR_STR); + i = item.find(TEXT_SEPARATOR, i+sizeof(TEXT_SEPARATOR_STR)+sizeof(TEXT_SEPARATOR_STR)); + } while (i != string::npos); + i = 0; + } else { + i = item.find(FIELD_SEPARATOR); + } + if (i != string::npos) { + out << TEXT_SEPARATOR << item << TEXT_SEPARATOR; + if (begin < pos) { + cnt += 2; + } else if (begin == pos) { + cnt++; + } + } else { + out << item; + } + if (begin < pos) { + cnt += (unsigned int)(item).length(); + } + begin++; + } + out << endl; + out << setw(cnt) << " " << setw(0) << "^" << endl; } bool DataType::dump(ostream& output, const unsigned char length) const { - output << m_id; - if (isAdjustableLength()) { - if (length == REMAIN_LEN) { - output << ":*"; - } else { - output << ":" << static_cast(length); - } - } - output << FIELD_SEPARATOR; - return false; + output << m_id; + if (isAdjustableLength()) { + if (length == REMAIN_LEN) { + output << ":*"; + } else { + output << ":" << static_cast(length); + } + } + output << FIELD_SEPARATOR; + return false; } result_t StringDataType::readRawValue(SymbolString& input, const unsigned char offset, - const unsigned char length, unsigned int& value) { - return RESULT_EMPTY; + const unsigned char length, unsigned int& value) { + return RESULT_EMPTY; } result_t StringDataType::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char baseOffset, const unsigned char length, - ostringstream& output, OutputFormat outputFormat) { - size_t start = 0, count = length; - int incr = 1; - unsigned char ch; - bool terminated = false; - if (count == REMAIN_LEN && input.size() > baseOffset) { - count = input.size()-baseOffset; - } else if (baseOffset + count > input.size()) { - return RESULT_ERR_INVALID_POS; - } - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } + const unsigned char baseOffset, const unsigned char length, + ostringstream& output, OutputFormat outputFormat) { + size_t start = 0, count = length; + int incr = 1; + unsigned char ch; + bool terminated = false; + if (count == REMAIN_LEN && input.size() > baseOffset) { + count = input.size()-baseOffset; + } else if (baseOffset + count > input.size()) { + return RESULT_ERR_INVALID_POS; + } + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } - if (outputFormat & OF_JSON) { - output << '"'; - } - output << setfill('0') << (m_isHex ? hex : dec); - for (size_t offset = start, i = 0; i < count; offset += incr, i++) { - ch = input[baseOffset + offset]; - if (m_isHex) { - if (i > 0) { - output << ' '; - } - output << setw(2) << static_cast(ch); - } else { - if (ch == 0x00) { - terminated = true; - } else if (!terminated) { - if (ch < 0x20) { - ch = (unsigned char)m_replacement; - } else if (!isprint(ch)) { - ch = '?'; - } else if (outputFormat & OF_JSON) { - if (ch == '"' || ch == '\\') { - output << '\\'; // escape - } - } - output << static_cast(ch); - } - } - } - if (outputFormat & OF_JSON) { - output << '"'; - } - return RESULT_OK; + if (outputFormat & OF_JSON) { + output << '"'; + } + output << setfill('0') << (m_isHex ? hex : dec); + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + ch = input[baseOffset + offset]; + if (m_isHex) { + if (i > 0) { + output << ' '; + } + output << setw(2) << static_cast(ch); + } else { + if (ch == 0x00) { + terminated = true; + } else if (!terminated) { + if (ch < 0x20) { + ch = (unsigned char)m_replacement; + } else if (!isprint(ch)) { + ch = '?'; + } else if (outputFormat & OF_JSON) { + if (ch == '"' || ch == '\\') { + output << '\\'; // escape + } + } + output << static_cast(ch); + } + } + } + if (outputFormat & OF_JSON) { + output << '"'; + } + return RESULT_OK; } result_t StringDataType::writeSymbols(istringstream& input, - unsigned char baseOffset, const unsigned char length, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - size_t start = 0, count = length; - bool remainder = count == REMAIN_LEN && hasFlag(ADJ); - int incr = 1; - unsigned int value = 0; - string token; + unsigned char baseOffset, const unsigned char length, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + size_t start = 0, count = length; + bool remainder = count == REMAIN_LEN && hasFlag(ADJ); + int incr = 1; + unsigned int value = 0; + string token; - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } - if (isIgnored() && !hasFlag(REQ)) { - if (remainder) { - count = 1; - } - for (size_t offset = start, i = 0; i < count; offset += incr, i++) { - output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement - } - if (usedLength != NULL) { - *usedLength = (unsigned char)count; - } - return RESULT_OK; - } - result_t result; - size_t i = 0, offset; - for (offset = start; i < count; offset += incr, i++) { - if (m_isHex) { - while (!input.eof() && input.peek() == ' ') { - input.get(); - } - if (input.eof()) { // no more digits - value = m_replacement; // fill up with replacement - } else { - token.clear(); - token.push_back((unsigned char)input.get()); - if (input.eof()) { - return RESULT_ERR_INVALID_NUM; // too short hex value - } - token.push_back((unsigned char)input.get()); - if (input.eof()) { - return RESULT_ERR_INVALID_NUM; // too short hex value - } - value = parseInt(token.c_str(), 16, 0, 0xff, result); - if (result != RESULT_OK) { - return result; // invalid hex value - } - } - } else { - if (input.eof()) { - value = m_replacement; - } else { - value = input.get(); - if (input.eof() || value < 0x20) { - value = m_replacement; - } - } - } - if (remainder && input.eof() && i > 0) { - if (value == 0x00 && !m_isHex) { - output[baseOffset + offset] = 0; - offset += incr; - } - break; - } - if (value > 0xff) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - output[baseOffset + offset] = (unsigned char)value; - } + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } + if (isIgnored() && !hasFlag(REQ)) { + if (remainder) { + count = 1; + } + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement + } + if (usedLength != NULL) { + *usedLength = (unsigned char)count; + } + return RESULT_OK; + } + result_t result; + size_t i = 0, offset; + for (offset = start; i < count; offset += incr, i++) { + if (m_isHex) { + while (!input.eof() && input.peek() == ' ') { + input.get(); + } + if (input.eof()) { // no more digits + value = m_replacement; // fill up with replacement + } else { + token.clear(); + token.push_back((unsigned char)input.get()); + if (input.eof()) { + return RESULT_ERR_INVALID_NUM; // too short hex value + } + token.push_back((unsigned char)input.get()); + if (input.eof()) { + return RESULT_ERR_INVALID_NUM; // too short hex value + } + value = parseInt(token.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; // invalid hex value + } + } + } else { + if (input.eof()) { + value = m_replacement; + } else { + value = input.get(); + if (input.eof() || value < 0x20) { + value = m_replacement; + } + } + } + if (remainder && input.eof() && i > 0) { + if (value == 0x00 && !m_isHex) { + output[baseOffset + offset] = 0; + offset += incr; + } + break; + } + if (value > 0xff) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + output[baseOffset + offset] = (unsigned char)value; + } - if (!remainder && i < count) { - return RESULT_ERR_EOF; // input too short - } - if (usedLength != NULL) { - *usedLength = (unsigned char)((offset-start)*incr); - } - return RESULT_OK; + if (!remainder && i < count) { + return RESULT_ERR_EOF; // input too short + } + if (usedLength != NULL) { + *usedLength = (unsigned char)((offset-start)*incr); + } + return RESULT_OK; } result_t DateTimeDataType::readRawValue(SymbolString& input, const unsigned char offset, - const unsigned char length, unsigned int& value) { - return RESULT_EMPTY; + const unsigned char length, unsigned int& value) { + return RESULT_EMPTY; } result_t DateTimeDataType::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char baseOffset, const unsigned char length, - ostringstream& output, OutputFormat outputFormat) { - size_t start = 0, count = length; - int incr = 1; - unsigned char ch, last = 0, hour = 0; - if (count == REMAIN_LEN && input.size() > baseOffset) { - count = input.size()-baseOffset; - } else if (baseOffset + count > input.size()) { - return RESULT_ERR_INVALID_POS; - } - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } + const unsigned char baseOffset, const unsigned char length, + ostringstream& output, OutputFormat outputFormat) { + size_t start = 0, count = length; + int incr = 1; + unsigned char ch, last = 0, hour = 0; + if (count == REMAIN_LEN && input.size() > baseOffset) { + count = input.size()-baseOffset; + } else if (baseOffset + count > input.size()) { + return RESULT_ERR_INVALID_POS; + } + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } - if (outputFormat & OF_JSON) { - output << '"'; - } - int type = (m_hasDate?2:0) | (m_hasTime?1:0); - for (size_t offset = start, i = 0; i < count; offset += incr, i++) { - if (length == 4 && i == 2 && m_hasDate) { - continue; // skip weekday in between - } - ch = input[baseOffset + offset]; - if (hasFlag(BCD) && (hasFlag(REQ) || ch != m_replacement)) { - if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) { - return RESULT_ERR_OUT_OF_RANGE; // invalid BCD - } - ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f)); - } - switch (type) { - case 2: // date only - if (!hasFlag(REQ) && ch == m_replacement) { - if (i + 1 != length) { - output << NULL_VALUE << "."; - break; - } else if (last == m_replacement) { - if (length == 2) { // number of days since 01.01.1900 - output << NULL_VALUE << "."; - } - output << NULL_VALUE; - break; - } - } - if (length == 2) { // number of days since 01.01.1900 - if (i == 0) { - break; - } - int mjd = last + ch*256 + 15020; // 01.01.1900 - int y = static_cast((mjd-15078.2)/365.25); - int m = static_cast((mjd-14956.1-static_cast(y*365.25))/30.6001); - int d = mjd-14956-static_cast(y*365.25)-static_cast(m*30.6001); - m--; - if (m >= 13) { - y++; - m -= 12; - } - output << dec << setfill('0') << setw(2) << static_cast(d) << "." - << setw(2) << static_cast(m) << "." << static_cast(y + 1900); - break; - } - if (i + 1 == length) { - output << (2000 + ch); - } else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) { - return RESULT_ERR_OUT_OF_RANGE; // invalid date - } else { - output << setw(2) << dec << setfill('0') << static_cast(ch) << "."; - } - break; + if (outputFormat & OF_JSON) { + output << '"'; + } + int type = (m_hasDate?2:0) | (m_hasTime?1:0); + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + if (length == 4 && i == 2 && m_hasDate) { + continue; // skip weekday in between + } + ch = input[baseOffset + offset]; + if (hasFlag(BCD) && (hasFlag(REQ) || ch != m_replacement)) { + if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) { + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD + } + ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f)); + } + switch (type) { + case 2: // date only + if (!hasFlag(REQ) && ch == m_replacement) { + if (i + 1 != length) { + output << NULL_VALUE << "."; + break; + } else if (last == m_replacement) { + if (length == 2) { // number of days since 01.01.1900 + output << NULL_VALUE << "."; + } + output << NULL_VALUE; + break; + } + } + if (length == 2) { // number of days since 01.01.1900 + if (i == 0) { + break; + } + int mjd = last + ch*256 + 15020; // 01.01.1900 + int y = static_cast((mjd-15078.2)/365.25); + int m = static_cast((mjd-14956.1-static_cast(y*365.25))/30.6001); + int d = mjd-14956-static_cast(y*365.25)-static_cast(m*30.6001); + m--; + if (m >= 13) { + y++; + m -= 12; + } + output << dec << setfill('0') << setw(2) << static_cast(d) << "." + << setw(2) << static_cast(m) << "." << static_cast(y + 1900); + break; + } + if (i + 1 == length) { + output << (2000 + ch); + } else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) { + return RESULT_ERR_OUT_OF_RANGE; // invalid date + } else { + output << setw(2) << dec << setfill('0') << static_cast(ch) << "."; + } + break; - case 1: // time only - if (!hasFlag(REQ) && ch == m_replacement) { - if (length == 1) { // truncated time - output << NULL_VALUE << ":" << NULL_VALUE; - break; - } - if (i > 0) { - output << ":"; - } - output << NULL_VALUE; - break; - } - if (hasFlag(SPE)) { // minutes since midnight - if (i == 0) { - last = ch; - continue; - } - int minutes = ch*256 + last; - if (minutes > 24*60) { - return RESULT_ERR_OUT_OF_RANGE; // invalid value - } - int hour = minutes / 60; - if (hour > 24) { - return RESULT_ERR_OUT_OF_RANGE; // invalid hour - } - output << setw(2) << dec << setfill('0') << static_cast(hour); - ch = (unsigned char)(minutes % 60); - } else if (length == 1) { // truncated time - if (i == 0) { - ch = (unsigned char)(ch/(60/m_resolution)); // convert to hours - offset -= incr; // repeat for minutes - count++; - } else { - ch = (unsigned char)((ch % (60/m_resolution)) * m_resolution); // convert to minutes - } - } - if (i == 0) { - if (ch > 24) { - return RESULT_ERR_OUT_OF_RANGE; // invalid hour - } - hour = ch; - } else if (ch > 59 || (hour == 24 && ch > 0)) { - return RESULT_ERR_OUT_OF_RANGE; // invalid time - } - if (i > 0) { - output << ":"; - } - output << setw(2) << dec << setfill('0') << static_cast(ch); - break; - } - last = ch; - } - if (outputFormat & OF_JSON) { - output << '"'; - } - return RESULT_OK; + case 1: // time only + if (!hasFlag(REQ) && ch == m_replacement) { + if (length == 1) { // truncated time + output << NULL_VALUE << ":" << NULL_VALUE; + break; + } + if (i > 0) { + output << ":"; + } + output << NULL_VALUE; + break; + } + if (hasFlag(SPE)) { // minutes since midnight + if (i == 0) { + last = ch; + continue; + } + int minutes = ch*256 + last; + if (minutes > 24*60) { + return RESULT_ERR_OUT_OF_RANGE; // invalid value + } + int hour = minutes / 60; + if (hour > 24) { + return RESULT_ERR_OUT_OF_RANGE; // invalid hour + } + output << setw(2) << dec << setfill('0') << static_cast(hour); + ch = (unsigned char)(minutes % 60); + } else if (length == 1) { // truncated time + if (i == 0) { + ch = (unsigned char)(ch/(60/m_resolution)); // convert to hours + offset -= incr; // repeat for minutes + count++; + } else { + ch = (unsigned char)((ch % (60/m_resolution)) * m_resolution); // convert to minutes + } + } + if (i == 0) { + if (ch > 24) { + return RESULT_ERR_OUT_OF_RANGE; // invalid hour + } + hour = ch; + } else if (ch > 59 || (hour == 24 && ch > 0)) { + return RESULT_ERR_OUT_OF_RANGE; // invalid time + } + if (i > 0) { + output << ":"; + } + output << setw(2) << dec << setfill('0') << static_cast(ch); + break; + } + last = ch; + } + if (outputFormat & OF_JSON) { + output << '"'; + } + return RESULT_OK; } result_t DateTimeDataType::writeSymbols(istringstream& input, - unsigned char baseOffset, const unsigned char length, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - size_t start = 0, count = length; - bool remainder = count == REMAIN_LEN && hasFlag(ADJ); - int incr = 1; - unsigned int value = 0, last = 0, lastLast = 0; - string token; + unsigned char baseOffset, const unsigned char length, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + size_t start = 0, count = length; + bool remainder = count == REMAIN_LEN && hasFlag(ADJ); + int incr = 1; + unsigned int value = 0, last = 0, lastLast = 0; + string token; - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } - if (isIgnored() && !hasFlag(REQ)) { - if (remainder) { - count = 1; - } - for (size_t offset = start, i = 0; i < count; offset += incr, i++) { - output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement - } - if (usedLength != NULL) { - *usedLength = (unsigned char)count; - } - return RESULT_OK; - } - result_t result; - size_t i = 0, offset; - int type = (m_hasDate?2:0) | (m_hasTime?1:0); - bool skip = false; - for (offset = start; i < count; offset += skip ? 0 : incr, i++) { - skip = false; - switch (type) { - case 2: // date only - if (length == 4 && i == 2) { - continue; // skip weekday in between - } - if (input.eof() || !getline(input, token, '.')) { - return RESULT_ERR_EOF; // incomplete - } - if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) { - value = m_replacement; - break; - } - value = parseInt(token.c_str(), 10, 0, 2099, result); - if (result != RESULT_OK) { - return result; // invalid date part - } - if (length == 2) { // number of days since 01.01.1900 - skip = true; - if (i == 0) { - count++; - } else if (i + 1 == count) { - int y = (value < 100 ? value + 2000 : value) - 1900; - int l = last <= 2 ? 1 : 0; - int mjd = 14956 + lastLast + static_cast((y-l)*365.25) + static_cast((last+1+l*12)*30.6001); - value = mjd - 15020; // 01.01.1900 - output[baseOffset + offset] = (unsigned char)(value&0xff); - value >>= 8; - offset += incr; - skip = false; - break; - } - } - if (i + 1 == count) { - if (length == 4) { - // calculate local week day - int y = (value < 100 ? value + 2000 : value) - 1900; - int l = last <= 2 ? 1 : 0; - int mjd = 14956 + lastLast + static_cast((y-l)*365.25) + static_cast((last+1+l*12)*30.6001); - int daysSinceSunday = (mjd+3) % 7; // Sun=0 - if (hasFlag(BCD)) { - output[baseOffset + offset - incr] = (unsigned char)((6+daysSinceSunday) % 7); // Sun=0x06 - } else { - output[baseOffset + offset - incr] = (unsigned char)(daysSinceSunday == 0 ? 7 : daysSinceSunday); // Sun=0x07 - } - } - if (value >= 2000) { - value -= 2000; - } - if (value > 99) { - return RESULT_ERR_OUT_OF_RANGE; // invalid year - } - } else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) { - return RESULT_ERR_OUT_OF_RANGE; // invalid date part - } - break; + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } + if (isIgnored() && !hasFlag(REQ)) { + if (remainder) { + count = 1; + } + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + output[baseOffset + offset] = (unsigned char)m_replacement; // fill up with replacement + } + if (usedLength != NULL) { + *usedLength = (unsigned char)count; + } + return RESULT_OK; + } + result_t result; + size_t i = 0, offset; + int type = (m_hasDate?2:0) | (m_hasTime?1:0); + bool skip = false; + for (offset = start; i < count; offset += skip ? 0 : incr, i++) { + skip = false; + switch (type) { + case 2: // date only + if (length == 4 && i == 2) { + continue; // skip weekday in between + } + if (input.eof() || !getline(input, token, '.')) { + return RESULT_ERR_EOF; // incomplete + } + if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) { + value = m_replacement; + break; + } + value = parseInt(token.c_str(), 10, 0, 2099, result); + if (result != RESULT_OK) { + return result; // invalid date part + } + if (length == 2) { // number of days since 01.01.1900 + skip = true; + if (i == 0) { + count++; + } else if (i + 1 == count) { + int y = (value < 100 ? value + 2000 : value) - 1900; + int l = last <= 2 ? 1 : 0; + int mjd = 14956 + lastLast + static_cast((y-l)*365.25) + static_cast((last+1+l*12)*30.6001); + value = mjd - 15020; // 01.01.1900 + output[baseOffset + offset] = (unsigned char)(value&0xff); + value >>= 8; + offset += incr; + skip = false; + break; + } + } + if (i + 1 == count) { + if (length == 4) { + // calculate local week day + int y = (value < 100 ? value + 2000 : value) - 1900; + int l = last <= 2 ? 1 : 0; + int mjd = 14956 + lastLast + static_cast((y-l)*365.25) + static_cast((last+1+l*12)*30.6001); + int daysSinceSunday = (mjd+3) % 7; // Sun=0 + if (hasFlag(BCD)) { + output[baseOffset + offset - incr] = (unsigned char)((6+daysSinceSunday) % 7); // Sun=0x06 + } else { + output[baseOffset + offset - incr] = (unsigned char)(daysSinceSunday == 0 ? 7 : daysSinceSunday); // Sun=0x07 + } + } + if (value >= 2000) { + value -= 2000; + } + if (value > 99) { + return RESULT_ERR_OUT_OF_RANGE; // invalid year + } + } else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) { + return RESULT_ERR_OUT_OF_RANGE; // invalid date part + } + break; - case 1: // time only - if (input.eof() || !getline(input, token, LENGTH_SEPARATOR)) { - return RESULT_ERR_EOF; // incomplete - } - if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) { - value = m_replacement; - if (length == 1) { // truncated time - if (i == 0) { - skip = true; // repeat for minutes - count++; - break; - } - if (last != m_replacement) { - return RESULT_ERR_INVALID_NUM; // invalid truncated time minutes - } - } - break; - } - value = parseInt(token.c_str(), 10, 0, 59, result); - if (result != RESULT_OK) { - return result; // invalid time part - } - if ((i == 0 && value > 24) || (i > 0 && (last == 24 && value > 0) )) { - return RESULT_ERR_OUT_OF_RANGE; // invalid time part - } - if (hasFlag(SPE)) { // minutes since midnight - if (i == 0) { - skip = true; // repeat for minutes - break; - } - value += last*60; - output[baseOffset + offset] = (unsigned char)(value&0xff); - value >>= 8; - offset += incr; - } else if (length == 1) { // truncated time - if (i == 0) { - skip = true; // repeat for minutes - count++; - break; - } - value = (last * 60 + value + m_resolution/2)/m_resolution; - if (value > 24 * 6) { - return RESULT_ERR_OUT_OF_RANGE; // invalid time - } - } - break; - } - lastLast = last; - last = value; - if (!skip) { - if (hasFlag(BCD) && (hasFlag(REQ) || value != m_replacement)) { - if (value > 99) { - return RESULT_ERR_OUT_OF_RANGE; // invalid BCD - } - value = ((value / 10) << 4) | (value % 10); - } - if (value > 0xff) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - output[baseOffset + offset] = (unsigned char)value; - } - } + case 1: // time only + if (input.eof() || !getline(input, token, LENGTH_SEPARATOR)) { + return RESULT_ERR_EOF; // incomplete + } + if (!hasFlag(REQ) && strcmp(token.c_str(), NULL_VALUE) == 0) { + value = m_replacement; + if (length == 1) { // truncated time + if (i == 0) { + skip = true; // repeat for minutes + count++; + break; + } + if (last != m_replacement) { + return RESULT_ERR_INVALID_NUM; // invalid truncated time minutes + } + } + break; + } + value = parseInt(token.c_str(), 10, 0, 59, result); + if (result != RESULT_OK) { + return result; // invalid time part + } + if ((i == 0 && value > 24) || (i > 0 && (last == 24 && value > 0) )) { + return RESULT_ERR_OUT_OF_RANGE; // invalid time part + } + if (hasFlag(SPE)) { // minutes since midnight + if (i == 0) { + skip = true; // repeat for minutes + break; + } + value += last*60; + output[baseOffset + offset] = (unsigned char)(value&0xff); + value >>= 8; + offset += incr; + } else if (length == 1) { // truncated time + if (i == 0) { + skip = true; // repeat for minutes + count++; + break; + } + value = (last * 60 + value + m_resolution/2)/m_resolution; + if (value > 24 * 6) { + return RESULT_ERR_OUT_OF_RANGE; // invalid time + } + } + break; + } + lastLast = last; + last = value; + if (!skip) { + if (hasFlag(BCD) && (hasFlag(REQ) || value != m_replacement)) { + if (value > 99) { + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD + } + value = ((value / 10) << 4) | (value % 10); + } + if (value > 0xff) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + output[baseOffset + offset] = (unsigned char)value; + } + } - if (!remainder && i < count) { - return RESULT_ERR_EOF; // input too short - } - if (usedLength != NULL) { - *usedLength = (unsigned char)((offset-start)*incr); - } - return RESULT_OK; + if (!remainder && i < count) { + return RESULT_ERR_EOF; // input too short + } + if (usedLength != NULL) { + *usedLength = (unsigned char)((offset-start)*incr); + } + return RESULT_OK; } unsigned char NumberDataType::calcPrecision(const int divisor) { - unsigned char precision = 0; - if (divisor > 1) { - for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, precision++) { - if (exp >= (unsigned int)divisor) { - break; - } - } - } - return precision; + unsigned char precision = 0; + if (divisor > 1) { + for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, precision++) { + if (exp >= (unsigned int)divisor) { + break; + } + } + } + return precision; } bool NumberDataType::dump(ostream& output, unsigned char length) const { - if (m_bitCount < 8) { - DataType::dump(output, m_bitCount); - } else { - DataType::dump(output, length); - } - if (m_baseType) { - if (m_baseType->m_divisor != m_divisor) { - output << static_cast(m_divisor / m_baseType->m_divisor); - return true; - } - } else if (m_divisor != 1) { - output << static_cast(m_divisor); - return true; - } - return false; + if (m_bitCount < 8) { + DataType::dump(output, m_bitCount); + } else { + DataType::dump(output, length); + } + if (m_baseType) { + if (m_baseType->m_divisor != m_divisor) { + output << static_cast(m_divisor / m_baseType->m_divisor); + return true; + } + } else if (m_divisor != 1) { + output << static_cast(m_divisor); + return true; + } + return false; } result_t NumberDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived) { - if (divisor == 0) { - divisor = 1; - } - if (m_divisor != 1) { - if (divisor == 1) { - divisor = m_divisor; - } else if (divisor < 0) { - if (m_divisor > 1) { - return RESULT_ERR_INVALID_ARG; - } - divisor *= -m_divisor; - } else if (m_divisor < 0) { - if (divisor > 1) { - return RESULT_ERR_INVALID_ARG; - } - divisor *= -m_divisor; - } else { - divisor *= m_divisor; - } - } - if (divisor == m_divisor && bitCount == m_bitCount) { - derived = this; - return RESULT_OK; - } - if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) { - return RESULT_ERR_OUT_OF_RANGE; - } - if (bitCount <= 0 || bitCount == m_bitCount) { - bitCount = m_bitCount; - } else if (isAdjustableLength()) { - if (m_bitCount < 8) { - if (bitCount+m_firstBit > 8) { - return RESULT_ERR_OUT_OF_RANGE; - } - } else if ((bitCount%8) == 0) { - return RESULT_ERR_INVALID_ARG; - } - } else { - return RESULT_ERR_INVALID_ARG; - } - if (m_bitCount < 8) { - derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, - m_firstBit, divisor); - } else { - derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, - m_minValue, m_maxValue, divisor); - } - derived->m_baseType = m_baseType ? m_baseType : this; - DataTypeList::getInstance()->addCleanup(derived); - return RESULT_OK; + if (divisor == 0) { + divisor = 1; + } + if (m_divisor != 1) { + if (divisor == 1) { + divisor = m_divisor; + } else if (divisor < 0) { + if (m_divisor > 1) { + return RESULT_ERR_INVALID_ARG; + } + divisor *= -m_divisor; + } else if (m_divisor < 0) { + if (divisor > 1) { + return RESULT_ERR_INVALID_ARG; + } + divisor *= -m_divisor; + } else { + divisor *= m_divisor; + } + } + if (divisor == m_divisor && bitCount == m_bitCount) { + derived = this; + return RESULT_OK; + } + if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) { + return RESULT_ERR_OUT_OF_RANGE; + } + if (bitCount <= 0 || bitCount == m_bitCount) { + bitCount = m_bitCount; + } else if (isAdjustableLength()) { + if (m_bitCount < 8) { + if (bitCount+m_firstBit > 8) { + return RESULT_ERR_OUT_OF_RANGE; + } + } else if ((bitCount%8) == 0) { + return RESULT_ERR_INVALID_ARG; + } + } else { + return RESULT_ERR_INVALID_ARG; + } + if (m_bitCount < 8) { + derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, + m_firstBit, divisor); + } else { + derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement, + m_minValue, m_maxValue, divisor); + } + derived->m_baseType = m_baseType ? m_baseType : this; + DataTypeList::getInstance()->addCleanup(derived); + return RESULT_OK; } result_t NumberDataType::readRawValue(SymbolString& input, - unsigned char baseOffset, const unsigned char length, - unsigned int& value) { - size_t start = 0, count = length; - int incr = 1; - unsigned char ch; + unsigned char baseOffset, const unsigned char length, + unsigned int& value) { + size_t start = 0, count = length; + int incr = 1; + unsigned char ch; - if (baseOffset + length > input.size()) { - return RESULT_ERR_INVALID_POS; // not enough data available - } - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } + if (baseOffset + length > input.size()) { + return RESULT_ERR_INVALID_POS; // not enough data available + } + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } - value = 0; - unsigned int exp = 1; - for (size_t offset = start, i = 0; i < count; offset += incr, i++) { - ch = input[baseOffset + offset]; - if (hasFlag(BCD)) { - if (!hasFlag(REQ) && ch == (m_replacement & 0xff)) { - value = m_replacement; - return RESULT_OK; - } - if (!hasFlag(HCD)) { - if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) { - return RESULT_ERR_OUT_OF_RANGE; // invalid BCD - } - ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f)); - } else if (ch > 0x63) { - return RESULT_ERR_OUT_OF_RANGE; // invalid HCD - } - value += ch * exp; - exp *= 100; - } else { - value |= ch * exp; - exp <<= 8; - } - } - if (m_firstBit > 0) { - value >>= m_firstBit; - } - if (m_bitCount < 8) { - value &= (1 << m_bitCount) - 1; - } - return RESULT_OK; + value = 0; + unsigned int exp = 1; + for (size_t offset = start, i = 0; i < count; offset += incr, i++) { + ch = input[baseOffset + offset]; + if (hasFlag(BCD)) { + if (!hasFlag(REQ) && ch == (m_replacement & 0xff)) { + value = m_replacement; + return RESULT_OK; + } + if (!hasFlag(HCD)) { + if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) { + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD + } + ch = (unsigned char)((ch >> 4) * 10 + (ch & 0x0f)); + } else if (ch > 0x63) { + return RESULT_ERR_OUT_OF_RANGE; // invalid HCD + } + value += ch * exp; + exp *= 100; + } else { + value |= ch * exp; + exp <<= 8; + } + } + if (m_firstBit > 0) { + value >>= m_firstBit; + } + if (m_bitCount < 8) { + value &= (1 << m_bitCount) - 1; + } + return RESULT_OK; } result_t NumberDataType::readSymbols(SymbolString& input, const bool isMaster, - const unsigned char baseOffset, const unsigned char length, - ostringstream& output, OutputFormat outputFormat) { - unsigned int value = 0; - int signedValue; + const unsigned char baseOffset, const unsigned char length, + ostringstream& output, OutputFormat outputFormat) { + unsigned int value = 0; + int signedValue; - result_t result = readRawValue(input, baseOffset, length, value); - if (result != RESULT_OK) { - return result; - } - output << setw(0) << dec; // initialize output + result_t result = readRawValue(input, baseOffset, length, value); + if (result != RESULT_OK) { + return result; + } + output << setw(0) << dec; // initialize output - if (!hasFlag(REQ) && value == m_replacement) { - if (outputFormat & OF_JSON) { - output << "null"; - } else { - output << NULL_VALUE; - } - return RESULT_OK; - } + if (!hasFlag(REQ) && value == m_replacement) { + if (outputFormat & OF_JSON) { + output << "null"; + } else { + output << NULL_VALUE; + } + return RESULT_OK; + } - bool negative = hasFlag(SIG) && (value & (1 << (m_bitCount - 1))) != 0; - if (m_bitCount == 32) { - if (hasFlag(EXP)) { // IEEE 754 binary32 - float val; + bool negative = hasFlag(SIG) && (value & (1 << (m_bitCount - 1))) != 0; + if (m_bitCount == 32) { + if (hasFlag(EXP)) { // IEEE 754 binary32 + float val; #ifdef HAVE_DIRECT_FLOAT_FORMAT -# if HAVE_DIRECT_FLOAT_FORMAT == 2 - value = __builtin_bswap32(value); -# endif - unsigned char* pval = (unsigned char*)&value; - val = *reinterpret_cast(pval); +# if HAVE_DIRECT_FLOAT_FORMAT == 2 + value = __builtin_bswap32(value); +# endif + unsigned char* pval = (unsigned char*)&value; + val = *reinterpret_cast(pval); #else - int exp = (value >> 23) & 0xff; // 8 bits, signed - if (exp == 0) { - val = 0.0; - } else { - exp -= 127; - unsigned int sig = value & ((1 << 23) - 1); - val = (1.0f + static_cast(sig / exp2(23))) * static_cast(exp2(exp)); - if (negative) { - val = -val; - } - } + int exp = (value >> 23) & 0xff; // 8 bits, signed + if (exp == 0) { + val = 0.0; + } else { + exp -= 127; + unsigned int sig = value & ((1 << 23) - 1); + val = (1.0f + static_cast(sig / exp2(23))) * static_cast(exp2(exp)); + if (negative) { + val = -val; + } + } #endif - if (val != 0.0) { - if (m_divisor < 0) { - val *= static_cast(-m_divisor); - } else if (m_divisor > 1) { - val /= static_cast(m_divisor); - } - } - if (m_precision != 0) { - output << fixed << setprecision(m_precision+6); - } else if (val == 0) { - output << fixed << setprecision(1); - } - output << static_cast(val); - return RESULT_OK; - } - if (!negative) { - if (m_divisor < 0) { - output << (static_cast(value) * static_cast(-m_divisor)); - } else if (m_divisor <= 1) { - output << static_cast(value); - } else { - output << setprecision(m_precision) - << fixed << (static_cast(value) / static_cast(m_divisor)); - } - return RESULT_OK; - } - signedValue = static_cast(value); // negative signed value - } else if (negative) { // negative signed value - signedValue = static_cast(value) - (1 << m_bitCount); - } else { - signedValue = static_cast(value); - } - if (m_divisor < 0) { - output << fixed << setprecision(0) - << (static_cast(signedValue) * static_cast(-m_divisor)); - } else if (m_divisor <= 1) { - if (hasFlag(FIX) && hasFlag(BCD)) { - if (outputFormat & OF_JSON) { - output << '"'; - output << setw(length * 2) << setfill('0'); - output << static_cast(signedValue) << setw(0); - output << '"'; - return RESULT_OK; - } - output << setw(length * 2) << setfill('0'); - } - output << static_cast(signedValue) << setw(0); - } else { - output << setprecision(m_precision) - << fixed << (static_cast(signedValue) / static_cast(m_divisor)); - } - return RESULT_OK; + if (val != 0.0) { + if (m_divisor < 0) { + val *= static_cast(-m_divisor); + } else if (m_divisor > 1) { + val /= static_cast(m_divisor); + } + } + if (m_precision != 0) { + output << fixed << setprecision(m_precision+6); + } else if (val == 0) { + output << fixed << setprecision(1); + } + output << static_cast(val); + return RESULT_OK; + } + if (!negative) { + if (m_divisor < 0) { + output << (static_cast(value) * static_cast(-m_divisor)); + } else if (m_divisor <= 1) { + output << static_cast(value); + } else { + output << setprecision(m_precision) + << fixed << (static_cast(value) / static_cast(m_divisor)); + } + return RESULT_OK; + } + signedValue = static_cast(value); // negative signed value + } else if (negative) { // negative signed value + signedValue = static_cast(value) - (1 << m_bitCount); + } else { + signedValue = static_cast(value); + } + if (m_divisor < 0) { + output << fixed << setprecision(0) + << (static_cast(signedValue) * static_cast(-m_divisor)); + } else if (m_divisor <= 1) { + if (hasFlag(FIX) && hasFlag(BCD)) { + if (outputFormat & OF_JSON) { + output << '"'; + output << setw(length * 2) << setfill('0'); + output << static_cast(signedValue) << setw(0); + output << '"'; + return RESULT_OK; + } + output << setw(length * 2) << setfill('0'); + } + output << static_cast(signedValue) << setw(0); + } else { + output << setprecision(m_precision) + << fixed << (static_cast(signedValue) / static_cast(m_divisor)); + } + return RESULT_OK; } result_t NumberDataType::writeRawValue(unsigned int value, - const unsigned char baseOffset, const unsigned char length, - SymbolString& output, unsigned char* usedLength) { - size_t start = 0, count = length; - int incr = 1; - unsigned char ch; + const unsigned char baseOffset, const unsigned char length, + SymbolString& output, unsigned char* usedLength) { + size_t start = 0, count = length; + int incr = 1; + unsigned char ch; - if (m_bitCount < 8 && (value & ~((1 << m_bitCount) - 1)) != 0) { - return RESULT_ERR_OUT_OF_RANGE; - } - if (m_firstBit > 0) { - value <<= m_firstBit; - } + if (m_bitCount < 8 && (value & ~((1 << m_bitCount) - 1)) != 0) { + return RESULT_ERR_OUT_OF_RANGE; + } + if (m_firstBit > 0) { + value <<= m_firstBit; + } - if (hasFlag(REV)) { // reverted binary representation (most significant byte first) - start = length - 1; - incr = -1; - } + if (hasFlag(REV)) { // reverted binary representation (most significant byte first) + start = length - 1; + incr = -1; + } - for (size_t offset = start, i = 0, exp = 1; i < count; offset += incr, i++) { - if (hasFlag(BCD)) { - if (!hasFlag(REQ) && value == m_replacement) { - ch = m_replacement & 0xff; - } else { - ch = (unsigned char)((value / exp) % 100); - if (!hasFlag(HCD)) { - ch = (unsigned char)(((ch / 10) << 4) | (ch % 10)); - } - } - exp *= 100; - } else { - ch = (value / exp) & 0xff; - exp <<= 8; - } - if (offset == start && (m_bitCount % 8) != 0 && baseOffset + offset < output.size()) { - output[baseOffset + offset] |= ch; - } else { - output[baseOffset + offset] = ch; - } - } - if (usedLength != NULL) { - *usedLength = length; - } - return RESULT_OK; + for (size_t offset = start, i = 0, exp = 1; i < count; offset += incr, i++) { + if (hasFlag(BCD)) { + if (!hasFlag(REQ) && value == m_replacement) { + ch = m_replacement & 0xff; + } else { + ch = (unsigned char)((value / exp) % 100); + if (!hasFlag(HCD)) { + ch = (unsigned char)(((ch / 10) << 4) | (ch % 10)); + } + } + exp *= 100; + } else { + ch = (value / exp) & 0xff; + exp <<= 8; + } + if (offset == start && (m_bitCount % 8) != 0 && baseOffset + offset < output.size()) { + output[baseOffset + offset] |= ch; + } else { + output[baseOffset + offset] = ch; + } + } + if (usedLength != NULL) { + *usedLength = length; + } + return RESULT_OK; } result_t NumberDataType::writeSymbols(istringstream& input, - const unsigned char baseOffset, const unsigned char length, - SymbolString& output, const bool isMaster, unsigned char* usedLength) { - unsigned int value; + const unsigned char baseOffset, const unsigned char length, + SymbolString& output, const bool isMaster, unsigned char* usedLength) { + unsigned int value; - const char* str = input.str().c_str(); - if (!hasFlag(REQ) && (isIgnored() || strcasecmp(str, NULL_VALUE) == 0)) { - value = m_replacement; // replacement value - } else if (str == NULL || *str == 0) { - return RESULT_ERR_EOF; // input too short - } else if (hasFlag(EXP)) { // IEEE 754 binary32 - char* strEnd = NULL; - double dvalue = strtod(str, &strEnd); - if (strEnd == NULL || strEnd == str || *strEnd != 0) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - if (m_divisor < 0) { - dvalue /= -m_divisor; - } else if (m_divisor > 1) { - dvalue *= m_divisor; - } + const char* str = input.str().c_str(); + if (!hasFlag(REQ) && (isIgnored() || strcasecmp(str, NULL_VALUE) == 0)) { + value = m_replacement; // replacement value + } else if (str == NULL || *str == 0) { + return RESULT_ERR_EOF; // input too short + } else if (hasFlag(EXP)) { // IEEE 754 binary32 + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + if (strEnd == NULL || strEnd == str || *strEnd != 0) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + if (m_divisor < 0) { + dvalue /= -m_divisor; + } else if (m_divisor > 1) { + dvalue *= m_divisor; + } #ifdef HAVE_DIRECT_FLOAT_FORMAT - float val = static_cast(dvalue); - unsigned char* pval = (unsigned char*)&val; - value = *reinterpret_cast(pval); -# if HAVE_DIRECT_FLOAT_FORMAT == 2 - value = __builtin_bswap32(value); -# endif + float val = static_cast(dvalue); + unsigned char* pval = (unsigned char*)&val; + value = *reinterpret_cast(pval); +# if HAVE_DIRECT_FLOAT_FORMAT == 2 + value = __builtin_bswap32(value); +# endif #else - value = 0; - if (dvalue != 0) { - bool negative = dvalue < 0; - if (negative) { - dvalue = -dvalue; - } - int exp = ilogb(dvalue); - if (exp < -126 || exp > 127) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - dvalue = scalbln(dvalue, -exp) - 1.0; - unsigned int sig = (unsigned int)(dvalue * exp2(23)); - exp += 127; - value = (exp << 23) | sig; - if (negative) { - value |= 0x80000000; - } - } + value = 0; + if (dvalue != 0) { + bool negative = dvalue < 0; + if (negative) { + dvalue = -dvalue; + } + int exp = ilogb(dvalue); + if (exp < -126 || exp > 127) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + dvalue = scalbln(dvalue, -exp) - 1.0; + unsigned int sig = (unsigned int)(dvalue * exp2(23)); + exp += 127; + value = (exp << 23) | sig; + if (negative) { + value |= 0x80000000; + } + } #endif - } else { - char* strEnd = NULL; - if (m_divisor == 1) { - if (hasFlag(SIG)) { - long signedValue = strtol(str, &strEnd, 10); - if (signedValue < 0 && m_bitCount != 32) { - value = (unsigned int)(signedValue + (1 << m_bitCount)); - } else { - value = (unsigned int)signedValue; - } - } else { - value = (unsigned int)strtoul(str, &strEnd, 10); - } - if (strEnd == NULL || strEnd == str || (*strEnd != 0 && *strEnd != '.')) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - } else { - char* strEnd = NULL; - double dvalue = strtod(str, &strEnd); - if (strEnd == NULL || strEnd == str || *strEnd != 0) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - if (m_divisor < 0) { - dvalue = round(dvalue / -m_divisor); - } else { - dvalue = round(dvalue * m_divisor); - } - if (hasFlag(SIG)) { - if (dvalue < -(1LL << (8 * length)) || dvalue >= (1LL << (8 * length))) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - if (dvalue < 0 && m_bitCount != 32) { - value = static_cast(dvalue + (1 << m_bitCount)); - } else { - value = static_cast(dvalue); - } - } else { - if (dvalue < 0.0 || dvalue >= (1LL << (8 * length))) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - value = (unsigned int)dvalue; - } - } + } else { + char* strEnd = NULL; + if (m_divisor == 1) { + if (hasFlag(SIG)) { + long signedValue = strtol(str, &strEnd, 10); + if (signedValue < 0 && m_bitCount != 32) { + value = (unsigned int)(signedValue + (1 << m_bitCount)); + } else { + value = (unsigned int)signedValue; + } + } else { + value = (unsigned int)strtoul(str, &strEnd, 10); + } + if (strEnd == NULL || strEnd == str || (*strEnd != 0 && *strEnd != '.')) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + } else { + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + if (strEnd == NULL || strEnd == str || *strEnd != 0) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + if (m_divisor < 0) { + dvalue = round(dvalue / -m_divisor); + } else { + dvalue = round(dvalue * m_divisor); + } + if (hasFlag(SIG)) { + if (dvalue < -(1LL << (8 * length)) || dvalue >= (1LL << (8 * length))) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + if (dvalue < 0 && m_bitCount != 32) { + value = static_cast(dvalue + (1 << m_bitCount)); + } else { + value = static_cast(dvalue); + } + } else { + if (dvalue < 0.0 || dvalue >= (1LL << (8 * length))) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + value = (unsigned int)dvalue; + } + } - if (hasFlag(SIG)) { // signed value - if ((value & (1 << (m_bitCount - 1))) != 0) { // negative signed value - if (value < m_minValue) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - } else if (value > m_maxValue) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - } else if (value < m_minValue || value > m_maxValue) { - return RESULT_ERR_OUT_OF_RANGE; // value out of range - } - } + if (hasFlag(SIG)) { // signed value + if ((value & (1 << (m_bitCount - 1))) != 0) { // negative signed value + if (value < m_minValue) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + } else if (value > m_maxValue) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + } else if (value < m_minValue || value > m_maxValue) { + return RESULT_ERR_OUT_OF_RANGE; // value out of range + } + } - return writeRawValue(value, baseOffset, length, output, usedLength); + return writeRawValue(value, baseOffset, length, output, usedLength); } @@ -968,117 +968,117 @@ bool DataTypeList::s_contrib_initialized = libebus_contrib_register(); DataTypeList::DataTypeList() { - add(new StringDataType("STR", MAX_LEN*8, ADJ, ' ')); // >= 1 byte character string filled up with space - add(new NumberDataType("PIN", 16, FIX|BCD|REV, 0xffff, 0, 0x9999, 1)); // unsigned decimal in BCD, 0000 - 9999 (fixed length) - add(new NumberDataType("UCH", 8, 0, 0xff, 0, 0xfe, 1)); // unsigned integer, 0 - 254 - add(new StringDataType("IGN", MAX_LEN*8, IGN|ADJ, 0)); // >= 1 byte ignored data - add(new StringDataType("NTS", MAX_LEN*8, ADJ, 0)); // >= 1 byte character string filled up with 0x00 (null terminated string) - add(new StringDataType("HEX", MAX_LEN*8, ADJ, 0, true)); // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d - add(new DateTimeDataType("BDA", 32, BCD, 0xff, true, false, 0)); // date with weekday in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is weekday Mon=0x00 - Sun=0x06, replacement 0xff) - add(new DateTimeDataType("BDA", 24, BCD, 0xff, true, false, 0)); // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99, replacement 0xff) - add(new DateTimeDataType("HDA", 32, 0, 0xff, true, false, 0)); // date with weekday, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x1f,0x0c,WW,0x63, WW is weekday Mon=0x01 - Sun=0x07, replacement 0xff) - add(new DateTimeDataType("HDA", 24, 0, 0xff, true, false, 0)); // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x1f,0x0c,0x63, replacement 0xff) - add(new DateTimeDataType("DAY", 16, 0, 0xff, true, false, 0)); // date, days since 01.01.1900, 01.01.1900 - 06.06.2079 (0x00,0x00 - 0xff,0xff) - add(new DateTimeDataType("BTI", 24, BCD|REV, 0xff, false, true, 0)); // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) - add(new DateTimeDataType("HTI", 24, 0, 0xff, false, true, 0)); // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x17,0x3b,0x3b) - add(new DateTimeDataType("VTI", 24, REV, 0x63, false, true, 0)); // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x3b,0x3b,0x17, replacement 0x63) [Vaillant type] - add(new DateTimeDataType("BTM", 16, BCD|REV, 0xff, false, true, 0)); // time as hh:mm in BCD, 00:00 - 23:59 (0x00,0x00 - 0x59,0x23, replacement 0xff) - add(new DateTimeDataType("HTM", 16, 0, 0xff, false, true, 0)); // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x17,0x3b) - add(new DateTimeDataType("VTM", 16, REV, 0xff, false, true, 0)); // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x3b,0x17, replacement 0xff) [Vaillant type] - add(new DateTimeDataType("MIN", 16, SPE, 0xff, false, true, 0)); // time, minutes since last midnight, 00:00 - 24:00 (minutes + hour * 60 as integer) - add(new DateTimeDataType("TTM", 8, 0, 0x90, false, true, 10)); // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) - add(new DateTimeDataType("TTH", 8, 0, 0, false, true, 30)); // truncated time (only multiple of 30 minutes), 00:00 - 24:00 (minutes div 30 + hour * 2 as integer) - add(new DateTimeDataType("TTQ", 8, 0, 0, false, true, 15)); // truncated time (only multiple of 15 minutes), 00:00 - 24:00 (minutes div 15 + hour * 4 as integer) - add(new NumberDataType("BDY", 8, DAY, 0x07, 0, 6, 1)); // weekday, "Mon" - "Sun" (0x00 - 0x06) [eBUS type] - add(new NumberDataType("HDY", 8, DAY, 0x00, 1, 7, 1)); // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type] - add(new NumberDataType("BCD", 8, BCD, 0xff, 0, 99, 1)); // unsigned decimal in BCD, 0 - 99 - add(new NumberDataType("BCD", 16, BCD, 0xffff, 0, 9999, 1)); // unsigned decimal in BCD, 0 - 9999 - add(new NumberDataType("BCD", 24, BCD, 0xffffff, 0, 999999, 1)); // unsigned decimal in BCD, 0 - 999999 - add(new NumberDataType("BCD", 32, BCD, 0xffffffff, 0, 99999999, 1)); // unsigned decimal in BCD, 0 - 99999999 - add(new NumberDataType("HCD", 32, HCD|BCD|REQ, 0, 0, 99999999, 1)); // unsigned decimal in HCD, 0 - 99999999 - add(new NumberDataType("HCD", 8, HCD|BCD|REQ, 0, 0, 99, 1)); // unsigned decimal in HCD, 0 - 99 - add(new NumberDataType("HCD", 16, HCD|BCD|REQ, 0, 0, 9999, 1)); // unsigned decimal in HCD, 0 - 9999 - add(new NumberDataType("HCD", 24, HCD|BCD|REQ, 0, 0, 999999, 1)); // unsigned decimal in HCD, 0 - 999999 - add(new NumberDataType("SCH", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 - add(new NumberDataType("D1B", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 - add(new NumberDataType("D1C", 8, 0, 0xff, 0x00, 0xc8, 2)); // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) - add(new NumberDataType("D2B", 16, SIG, 0x8000, 0x8001, 0x7fff, 256)); // signed number (fraction 1/256), -127.99 - +127.99 - add(new NumberDataType("D2C", 16, SIG, 0x8000, 0x8001, 0x7fff, 16)); // signed number (fraction 1/16), -2047.9 - +2047.9 - add(new NumberDataType("FLT", 16, SIG, 0x8000, 0x8001, 0x7fff, 1000)); // signed number (fraction 1/1000), -32.767 - +32.767, little endian - add(new NumberDataType("FLR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1000)); // signed number (fraction 1/1000), -32.767 - +32.767, big endian - add(new NumberDataType("EXP", 32, SIG|EXP, 0x7f800000, 0x00000000, 0xffffffff, 1)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), little endian - add(new NumberDataType("EXR", 32, SIG|EXP|REV, 0x7f800000, 0x00000000, 0xffffffff, 1)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), big endian - add(new NumberDataType("UIN", 16, 0, 0xffff, 0, 0xfffe, 1)); // unsigned integer, 0 - 65534, little endian - add(new NumberDataType("UIR", 16, REV, 0xffff, 0, 0xfffe, 1)); // unsigned integer, 0 - 65534, big endian - add(new NumberDataType("SIN", 16, SIG, 0x8000, 0x8001, 0x7fff, 1)); // signed integer, -32767 - +32767, little endian - add(new NumberDataType("SIR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1)); // signed integer, -32767 - +32767, big endian - add(new NumberDataType("U3N", 24, 0, 0xffffff, 0, 0xfffffe, 1)); // unsigned 3 bytes int, 0 - 16777214, little endian - add(new NumberDataType("U3R", 24, REV, 0xffffff, 0, 0xfffffe, 1)); // unsigned 3 bytes int, 0 - 16777214, big endian - add(new NumberDataType("S3N", 24, SIG, 0x800000, 0x800001, 0xffffff, 1)); // signed 3 bytes int, -8388607 - +8388607, little endian - add(new NumberDataType("S3R", 24, SIG|REV, 0x800000, 0x800001, 0xffffff, 1)); // signed 3 bytes int, -8388607 - +8388607, big endian - add(new NumberDataType("ULG", 32, 0, 0xffffffff, 0, 0xfffffffe, 1)); // unsigned integer, 0 - 4294967294, little endian - add(new NumberDataType("ULR", 32, REV, 0xffffffff, 0, 0xfffffffe, 1)); // unsigned integer, 0 - 4294967294, big endian - add(new NumberDataType("SLG", 32, SIG, 0x80000000, 0x80000001, 0xffffffff, 1)); // signed integer, -2147483647 - +2147483647, little endian - add(new NumberDataType("SLR", 32, SIG|REV, 0x80000000, 0x80000001, 0xffffffff, 1)); // signed integer, -2147483647 - +2147483647, big endian - add(new NumberDataType("BI0", 7, ADJ|REQ, 0, 0, 1)); // bit 0 (up to 7 bits until bit 6) - add(new NumberDataType("BI1", 7, ADJ|REQ, 0, 1, 1)); // bit 1 (up to 7 bits until bit 7) - add(new NumberDataType("BI2", 6, ADJ|REQ, 0, 2, 1)); // bit 2 (up to 6 bits until bit 7) - add(new NumberDataType("BI3", 5, ADJ|REQ, 0, 3, 1)); // bit 3 (up to 5 bits until bit 7) - add(new NumberDataType("BI4", 4, ADJ|REQ, 0, 4, 1)); // bit 4 (up to 4 bits until bit 7) - add(new NumberDataType("BI5", 3, ADJ|REQ, 0, 5, 1)); // bit 5 (up to 3 bits until bit 7) - add(new NumberDataType("BI6", 2, ADJ|REQ, 0, 6, 1)); // bit 6 (up to 2 bits until bit 7) - add(new NumberDataType("BI7", 1, ADJ|REQ, 0, 7, 1)); // bit 7 + add(new StringDataType("STR", MAX_LEN*8, ADJ, ' ')); // >= 1 byte character string filled up with space + add(new NumberDataType("PIN", 16, FIX|BCD|REV, 0xffff, 0, 0x9999, 1)); // unsigned decimal in BCD, 0000 - 9999 (fixed length) + add(new NumberDataType("UCH", 8, 0, 0xff, 0, 0xfe, 1)); // unsigned integer, 0 - 254 + add(new StringDataType("IGN", MAX_LEN*8, IGN|ADJ, 0)); // >= 1 byte ignored data + add(new StringDataType("NTS", MAX_LEN*8, ADJ, 0)); // >= 1 byte character string filled up with 0x00 (null terminated string) + add(new StringDataType("HEX", MAX_LEN*8, ADJ, 0, true)); // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d + add(new DateTimeDataType("BDA", 32, BCD, 0xff, true, false, 0)); // date with weekday in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is weekday Mon=0x00 - Sun=0x06, replacement 0xff) + add(new DateTimeDataType("BDA", 24, BCD, 0xff, true, false, 0)); // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99, replacement 0xff) + add(new DateTimeDataType("HDA", 32, 0, 0xff, true, false, 0)); // date with weekday, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x1f,0x0c,WW,0x63, WW is weekday Mon=0x01 - Sun=0x07, replacement 0xff) + add(new DateTimeDataType("HDA", 24, 0, 0xff, true, false, 0)); // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x1f,0x0c,0x63, replacement 0xff) + add(new DateTimeDataType("DAY", 16, 0, 0xff, true, false, 0)); // date, days since 01.01.1900, 01.01.1900 - 06.06.2079 (0x00,0x00 - 0xff,0xff) + add(new DateTimeDataType("BTI", 24, BCD|REV, 0xff, false, true, 0)); // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) + add(new DateTimeDataType("HTI", 24, 0, 0xff, false, true, 0)); // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x17,0x3b,0x3b) + add(new DateTimeDataType("VTI", 24, REV, 0x63, false, true, 0)); // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x3b,0x3b,0x17, replacement 0x63) [Vaillant type] + add(new DateTimeDataType("BTM", 16, BCD|REV, 0xff, false, true, 0)); // time as hh:mm in BCD, 00:00 - 23:59 (0x00,0x00 - 0x59,0x23, replacement 0xff) + add(new DateTimeDataType("HTM", 16, 0, 0xff, false, true, 0)); // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x17,0x3b) + add(new DateTimeDataType("VTM", 16, REV, 0xff, false, true, 0)); // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x3b,0x17, replacement 0xff) [Vaillant type] + add(new DateTimeDataType("MIN", 16, SPE, 0xff, false, true, 0)); // time, minutes since last midnight, 00:00 - 24:00 (minutes + hour * 60 as integer) + add(new DateTimeDataType("TTM", 8, 0, 0x90, false, true, 10)); // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) + add(new DateTimeDataType("TTH", 8, 0, 0, false, true, 30)); // truncated time (only multiple of 30 minutes), 00:00 - 24:00 (minutes div 30 + hour * 2 as integer) + add(new DateTimeDataType("TTQ", 8, 0, 0, false, true, 15)); // truncated time (only multiple of 15 minutes), 00:00 - 24:00 (minutes div 15 + hour * 4 as integer) + add(new NumberDataType("BDY", 8, DAY, 0x07, 0, 6, 1)); // weekday, "Mon" - "Sun" (0x00 - 0x06) [eBUS type] + add(new NumberDataType("HDY", 8, DAY, 0x00, 1, 7, 1)); // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type] + add(new NumberDataType("BCD", 8, BCD, 0xff, 0, 99, 1)); // unsigned decimal in BCD, 0 - 99 + add(new NumberDataType("BCD", 16, BCD, 0xffff, 0, 9999, 1)); // unsigned decimal in BCD, 0 - 9999 + add(new NumberDataType("BCD", 24, BCD, 0xffffff, 0, 999999, 1)); // unsigned decimal in BCD, 0 - 999999 + add(new NumberDataType("BCD", 32, BCD, 0xffffffff, 0, 99999999, 1)); // unsigned decimal in BCD, 0 - 99999999 + add(new NumberDataType("HCD", 32, HCD|BCD|REQ, 0, 0, 99999999, 1)); // unsigned decimal in HCD, 0 - 99999999 + add(new NumberDataType("HCD", 8, HCD|BCD|REQ, 0, 0, 99, 1)); // unsigned decimal in HCD, 0 - 99 + add(new NumberDataType("HCD", 16, HCD|BCD|REQ, 0, 0, 9999, 1)); // unsigned decimal in HCD, 0 - 9999 + add(new NumberDataType("HCD", 24, HCD|BCD|REQ, 0, 0, 999999, 1)); // unsigned decimal in HCD, 0 - 999999 + add(new NumberDataType("SCH", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 + add(new NumberDataType("D1B", 8, SIG, 0x80, 0x81, 0x7f, 1)); // signed integer, -127 - +127 + add(new NumberDataType("D1C", 8, 0, 0xff, 0x00, 0xc8, 2)); // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) + add(new NumberDataType("D2B", 16, SIG, 0x8000, 0x8001, 0x7fff, 256)); // signed number (fraction 1/256), -127.99 - +127.99 + add(new NumberDataType("D2C", 16, SIG, 0x8000, 0x8001, 0x7fff, 16)); // signed number (fraction 1/16), -2047.9 - +2047.9 + add(new NumberDataType("FLT", 16, SIG, 0x8000, 0x8001, 0x7fff, 1000)); // signed number (fraction 1/1000), -32.767 - +32.767, little endian + add(new NumberDataType("FLR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1000)); // signed number (fraction 1/1000), -32.767 - +32.767, big endian + add(new NumberDataType("EXP", 32, SIG|EXP, 0x7f800000, 0x00000000, 0xffffffff, 1)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), little endian + add(new NumberDataType("EXR", 32, SIG|EXP|REV, 0x7f800000, 0x00000000, 0xffffffff, 1)); // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), big endian + add(new NumberDataType("UIN", 16, 0, 0xffff, 0, 0xfffe, 1)); // unsigned integer, 0 - 65534, little endian + add(new NumberDataType("UIR", 16, REV, 0xffff, 0, 0xfffe, 1)); // unsigned integer, 0 - 65534, big endian + add(new NumberDataType("SIN", 16, SIG, 0x8000, 0x8001, 0x7fff, 1)); // signed integer, -32767 - +32767, little endian + add(new NumberDataType("SIR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1)); // signed integer, -32767 - +32767, big endian + add(new NumberDataType("U3N", 24, 0, 0xffffff, 0, 0xfffffe, 1)); // unsigned 3 bytes int, 0 - 16777214, little endian + add(new NumberDataType("U3R", 24, REV, 0xffffff, 0, 0xfffffe, 1)); // unsigned 3 bytes int, 0 - 16777214, big endian + add(new NumberDataType("S3N", 24, SIG, 0x800000, 0x800001, 0xffffff, 1)); // signed 3 bytes int, -8388607 - +8388607, little endian + add(new NumberDataType("S3R", 24, SIG|REV, 0x800000, 0x800001, 0xffffff, 1)); // signed 3 bytes int, -8388607 - +8388607, big endian + add(new NumberDataType("ULG", 32, 0, 0xffffffff, 0, 0xfffffffe, 1)); // unsigned integer, 0 - 4294967294, little endian + add(new NumberDataType("ULR", 32, REV, 0xffffffff, 0, 0xfffffffe, 1)); // unsigned integer, 0 - 4294967294, big endian + add(new NumberDataType("SLG", 32, SIG, 0x80000000, 0x80000001, 0xffffffff, 1)); // signed integer, -2147483647 - +2147483647, little endian + add(new NumberDataType("SLR", 32, SIG|REV, 0x80000000, 0x80000001, 0xffffffff, 1)); // signed integer, -2147483647 - +2147483647, big endian + add(new NumberDataType("BI0", 7, ADJ|REQ, 0, 0, 1)); // bit 0 (up to 7 bits until bit 6) + add(new NumberDataType("BI1", 7, ADJ|REQ, 0, 1, 1)); // bit 1 (up to 7 bits until bit 7) + add(new NumberDataType("BI2", 6, ADJ|REQ, 0, 2, 1)); // bit 2 (up to 6 bits until bit 7) + add(new NumberDataType("BI3", 5, ADJ|REQ, 0, 3, 1)); // bit 3 (up to 5 bits until bit 7) + add(new NumberDataType("BI4", 4, ADJ|REQ, 0, 4, 1)); // bit 4 (up to 4 bits until bit 7) + add(new NumberDataType("BI5", 3, ADJ|REQ, 0, 5, 1)); // bit 5 (up to 3 bits until bit 7) + add(new NumberDataType("BI6", 2, ADJ|REQ, 0, 6, 1)); // bit 6 (up to 2 bits until bit 7) + add(new NumberDataType("BI7", 1, ADJ|REQ, 0, 7, 1)); // bit 7 } DataTypeList* DataTypeList::getInstance() { - return &s_instance; + return &s_instance; } void DataTypeList::clear() { - for (list::iterator it = m_cleanupTypes.begin(); it != m_cleanupTypes.end(); it++) { - delete *it; - } - m_cleanupTypes.clear(); - m_typesByIdLength.clear(); - m_typesById.clear(); + for (list::iterator it = m_cleanupTypes.begin(); it != m_cleanupTypes.end(); it++) { + delete *it; + } + m_cleanupTypes.clear(); + m_typesByIdLength.clear(); + m_typesById.clear(); } result_t DataTypeList::add(DataType* dataType) { - if (!dataType->isAdjustableLength()) { - ostringstream str; - unsigned char bitCount = dataType->getBitCount(); - str << dataType->getId() << LENGTH_SEPARATOR << static_cast(bitCount >= 8?bitCount/8:bitCount); - map::iterator it = m_typesByIdLength.find(str.str()); - if (it != m_typesByIdLength.end()) { - return RESULT_ERR_DUPLICATE_NAME; // duplicate key - } - m_typesByIdLength[str.str()] = dataType; - if (m_typesById.find(dataType->getId()) != m_typesById.end()) { - m_cleanupTypes.push_back(dataType); - return RESULT_OK; // only store first one as default - } - } else if (m_typesById.find(dataType->getId()) != m_typesById.end()) { - return RESULT_ERR_DUPLICATE_NAME; // duplicate key - } - m_typesById[dataType->getId()] = dataType; - m_cleanupTypes.push_back(dataType); - return RESULT_OK; + if (!dataType->isAdjustableLength()) { + ostringstream str; + unsigned char bitCount = dataType->getBitCount(); + str << dataType->getId() << LENGTH_SEPARATOR << static_cast(bitCount >= 8?bitCount/8:bitCount); + map::iterator it = m_typesByIdLength.find(str.str()); + if (it != m_typesByIdLength.end()) { + return RESULT_ERR_DUPLICATE_NAME; // duplicate key + } + m_typesByIdLength[str.str()] = dataType; + if (m_typesById.find(dataType->getId()) != m_typesById.end()) { + m_cleanupTypes.push_back(dataType); + return RESULT_OK; // only store first one as default + } + } else if (m_typesById.find(dataType->getId()) != m_typesById.end()) { + return RESULT_ERR_DUPLICATE_NAME; // duplicate key + } + m_typesById[dataType->getId()] = dataType; + m_cleanupTypes.push_back(dataType); + return RESULT_OK; } DataType* DataTypeList::get(const string id, const unsigned char length) { - DataType* dataType = NULL; - if (length > 0) { - ostringstream str; - str << id << LENGTH_SEPARATOR << static_cast(length); - dataType = m_typesByIdLength[str.str()]; - } - if (!dataType) { - dataType = m_typesById[id]; - if (dataType && length > 0 && !dataType->isAdjustableLength()) { - return NULL; - } - } - return dataType; + DataType* dataType = NULL; + if (length > 0) { + ostringstream str; + str << id << LENGTH_SEPARATOR << static_cast(length); + dataType = m_typesByIdLength[str.str()]; + } + if (!dataType) { + dataType = m_typesById[id]; + if (dataType && length > 0 && !dataType->isAdjustableLength()) { + return NULL; + } + } + return dataType; } } // namespace ebusd diff --git a/src/lib/ebus/datatype.h b/src/lib/ebus/datatype.h index 229b6ae3..0ccb50d4 100644 --- a/src/lib/ebus/datatype.h +++ b/src/lib/ebus/datatype.h @@ -89,9 +89,9 @@ static const unsigned int OF_JSON = 0x10; //!< JSON format. /** the message part in which a data field is stored. */ enum PartType { - pt_any, //!< stored in any data (master or slave) - pt_masterData, //!< stored in master data - pt_slaveData, //!< stored in slave data + pt_any, //!< stored in any data (master or slave) + pt_masterData, //!< stored in master data + pt_slaveData, //!< stored in slave data }; /* flags for @a DataType. */ @@ -151,121 +151,121 @@ void printErrorPos(ostream& out, vector::iterator begin, const vector m_typesById; + private: + /** the known @a DataType instances by ID only. */ + map m_typesById; - /** the known @a DataType instances by ID and length (i.e. "ID:BITS"). - * Note: adjustable length types are stored by ID only. */ - map m_typesByIdLength; + /** the known @a DataType instances by ID and length (i.e. "ID:BITS"). + * Note: adjustable length types are stored by ID only. */ + map m_typesByIdLength; - /** the @a DataType instances to cleanup. */ - list m_cleanupTypes; + /** the @a DataType instances to cleanup. */ + list m_cleanupTypes; - /** the singleton instance. */ - static DataTypeList s_instance; + /** the singleton instance. */ + static DataTypeList s_instance; #ifdef HAVE_CONTRIB - /** true when contributed datatypes were successfully initialized. */ - static bool s_contrib_initialized; + /** true when contributed datatypes were successfully initialized. */ + static bool s_contrib_initialized; #endif }; diff --git a/src/lib/ebus/device.cpp b/src/lib/ebus/device.cpp index 2e66fa2f..41007fda 100644 --- a/src/lib/ebus/device.cpp +++ b/src/lib/ebus/device.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "device.h" @@ -29,7 +29,7 @@ #include #include #ifdef HAVE_PPOLL -# include +# include #endif #include #include @@ -39,289 +39,289 @@ namespace ebusd { Device::~Device() { - close(); + close(); } Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) { - if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) { - char* in = strdup(name); - bool udp = false; - char* addrpos = in; - char* portpos = strchr(addrpos, ':'); - if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { - addrpos += 4; - portpos = strchr(addrpos, ':'); - } - if (portpos == NULL) { - free(in); - return NULL; // invalid protocol or missing port - } - result_t result = RESULT_OK; - unsigned int port = parseInt(portpos+1, 10, 1, 65535, result); - if (result != RESULT_OK) { - free(in); - return NULL; // invalid port - } - struct sockaddr_in address; - memset(reinterpret_cast(&address), 0, sizeof(address)); - *portpos = 0; - if (inet_aton(addrpos, &address.sin_addr) == 0) { - struct hostent* h = gethostbyname(addrpos); - if (h == NULL) { - free(in); - return NULL; // invalid host - } - memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length); - } - free(in); - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons((uint16_t)port); - return new NetworkDevice(name, address, readOnly, initialSend, udp); - } - return new SerialDevice(name, checkDevice, readOnly, initialSend); + if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) { + char* in = strdup(name); + bool udp = false; + char* addrpos = in; + char* portpos = strchr(addrpos, ':'); + if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { + addrpos += 4; + portpos = strchr(addrpos, ':'); + } + if (portpos == NULL) { + free(in); + return NULL; // invalid protocol or missing port + } + result_t result = RESULT_OK; + unsigned int port = parseInt(portpos+1, 10, 1, 65535, result); + if (result != RESULT_OK) { + free(in); + return NULL; // invalid port + } + struct sockaddr_in address; + memset(reinterpret_cast(&address), 0, sizeof(address)); + *portpos = 0; + if (inet_aton(addrpos, &address.sin_addr) == 0) { + struct hostent* h = gethostbyname(addrpos); + if (h == NULL) { + free(in); + return NULL; // invalid host + } + memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length); + } + free(in); + address.sin_family = AF_INET; + address.sin_port = (in_port_t)htons((uint16_t)port); + return new NetworkDevice(name, address, readOnly, initialSend, udp); + } + return new SerialDevice(name, checkDevice, readOnly, initialSend); } void Device::close() { - if (m_fd != -1) { - ::close(m_fd); - m_fd = -1; - } + if (m_fd != -1) { + ::close(m_fd); + m_fd = -1; + } } bool Device::isValid() { - if (m_fd == -1) { - return false; - } - if (m_checkDevice) { - checkDevice(); - } - return m_fd != -1; + if (m_fd == -1) { + return false; + } + if (m_checkDevice) { + checkDevice(); + } + return m_fd != -1; } result_t Device::send(const unsigned char value) { - if (!isValid()) { - return RESULT_ERR_DEVICE; - } - if (m_readOnly || write(value) != 1) { - return RESULT_ERR_SEND; - } - if (m_listener != NULL) { - m_listener->notifyDeviceData(value, false); - } - return RESULT_OK; + if (!isValid()) { + return RESULT_ERR_DEVICE; + } + if (m_readOnly || write(value) != 1) { + return RESULT_ERR_SEND; + } + if (m_listener != NULL) { + m_listener->notifyDeviceData(value, false); + } + return RESULT_OK; } result_t Device::recv(const unsigned int timeout, unsigned char& value) { - if (!isValid()) { - return RESULT_ERR_DEVICE; - } - if (!available() && timeout > 0) { - int ret; - struct timespec tdiff; + if (!isValid()) { + return RESULT_ERR_DEVICE; + } + if (!available() && timeout > 0) { + int ret; + struct timespec tdiff; - // set select timeout - tdiff.tv_sec = timeout/1000000; - tdiff.tv_nsec = (timeout%1000000)*1000; + // set select timeout + tdiff.tv_sec = timeout/1000000; + tdiff.tv_nsec = (timeout%1000000)*1000; #ifdef HAVE_PPOLL - int nfds = 1; - struct pollfd fds[nfds]; + int nfds = 1; + struct pollfd fds[nfds]; - memset(fds, 0, sizeof(fds)); + memset(fds, 0, sizeof(fds)); - fds[0].fd = m_fd; - fds[0].events = POLLIN; + fds[0].fd = m_fd; + fds[0].events = POLLIN; - ret = ppoll(fds, nfds, &tdiff, NULL); + ret = ppoll(fds, nfds, &tdiff, NULL); #else #ifdef HAVE_PSELECT - fd_set readfds; + fd_set readfds; - FD_ZERO(&readfds); - FD_SET(m_fd, &readfds); + FD_ZERO(&readfds); + FD_SET(m_fd, &readfds); - ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL); + ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL); #else - ret = 1; // ignore timeout if neither ppoll nor pselect are available + ret = 1; // ignore timeout if neither ppoll nor pselect are available #endif #endif - if (ret == -1) { - return RESULT_ERR_DEVICE; - } - if (ret == 0) { - return RESULT_ERR_TIMEOUT; - } - } + if (ret == -1) { + return RESULT_ERR_DEVICE; + } + if (ret == 0) { + return RESULT_ERR_TIMEOUT; + } + } - // directly read byte from device - ssize_t nbytes = read(value); - if (nbytes == 0) { - return RESULT_ERR_EOF; - } - if (nbytes < 0) { - return RESULT_ERR_DEVICE; - } - if (m_listener != NULL) { - m_listener->notifyDeviceData(value, true); - } - return RESULT_OK; + // directly read byte from device + ssize_t nbytes = read(value); + if (nbytes == 0) { + return RESULT_ERR_EOF; + } + if (nbytes < 0) { + return RESULT_ERR_DEVICE; + } + if (m_listener != NULL) { + m_listener->notifyDeviceData(value, true); + } + return RESULT_OK; } result_t SerialDevice::open() { - if (m_fd != -1) { - close(); - } - struct termios newSettings; + if (m_fd != -1) { + close(); + } + struct termios newSettings; - // open file descriptor - m_fd = ::open(m_name, O_RDWR | O_NOCTTY); + // open file descriptor + m_fd = ::open(m_name, O_RDWR | O_NOCTTY); - if (m_fd < 0) { - return RESULT_ERR_NOTFOUND; - } - if (isatty(m_fd) == 0) { - close(); - return RESULT_ERR_NOTFOUND; - } + if (m_fd < 0) { + return RESULT_ERR_NOTFOUND; + } + if (isatty(m_fd) == 0) { + close(); + return RESULT_ERR_NOTFOUND; + } - if (flock(m_fd, LOCK_EX|LOCK_NB)) { - close(); - return RESULT_ERR_DEVICE; - } + if (flock(m_fd, LOCK_EX|LOCK_NB)) { + close(); + return RESULT_ERR_DEVICE; + } - // save current settings - tcgetattr(m_fd, &m_oldSettings); + // save current settings + tcgetattr(m_fd, &m_oldSettings); - // create new settings - memset(&newSettings, '\0', sizeof(newSettings)); + // create new settings + memset(&newSettings, '\0', sizeof(newSettings)); - newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); - newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode - newSettings.c_iflag |= IGNPAR; // ignore parity errors - newSettings.c_oflag &= ~OPOST; + newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); + newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode + newSettings.c_iflag |= IGNPAR; // ignore parity errors + newSettings.c_oflag &= ~OPOST; - // non-canonical mode: read() blocks until at least one byte is available - newSettings.c_cc[VMIN] = 1; - newSettings.c_cc[VTIME] = 0; + // non-canonical mode: read() blocks until at least one byte is available + newSettings.c_cc[VMIN] = 1; + newSettings.c_cc[VTIME] = 0; - // empty device buffer - tcflush(m_fd, TCIFLUSH); + // empty device buffer + tcflush(m_fd, TCIFLUSH); - // activate new settings of serial device - tcsetattr(m_fd, TCSAFLUSH, &newSettings); + // activate new settings of serial device + tcsetattr(m_fd, TCSAFLUSH, &newSettings); - // set serial device into blocking mode - fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); + // set serial device into blocking mode + fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); - if (m_initialSend && write(ESC) != 1) { - return RESULT_ERR_SEND; - } - return RESULT_OK; + if (m_initialSend && write(ESC) != 1) { + return RESULT_ERR_SEND; + } + return RESULT_OK; } void SerialDevice::close() { - if (m_fd != -1) { - // empty device buffer - tcflush(m_fd, TCIOFLUSH); + if (m_fd != -1) { + // empty device buffer + tcflush(m_fd, TCIOFLUSH); - // restore previous settings of the device - tcsetattr(m_fd, TCSANOW, &m_oldSettings); - } - Device::close(); + // restore previous settings of the device + tcsetattr(m_fd, TCSANOW, &m_oldSettings); + } + Device::close(); } void SerialDevice::checkDevice() { - int port; - if (ioctl(m_fd, TIOCMGET, &port) == -1) { - close(); - } + int port; + if (ioctl(m_fd, TIOCMGET, &port) == -1) { + close(); + } } result_t NetworkDevice::open() { - if (m_fd != -1) { - close(); - } - m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); - if (m_fd < 0) { - return RESULT_ERR_GENERIC_IO; - } - int ret; - if (m_udp) { - struct sockaddr_in address = m_address; - address.sin_addr.s_addr = INADDR_ANY; - ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address)); - } else { - int value = 1; - ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&value), sizeof(value)); - value = 1; - setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&value), sizeof(value)); - } - if (ret == 0) { - ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address)); - } - if (ret < 0) { - close(); - return RESULT_ERR_GENERIC_IO; - } - int cnt; - if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) { - // skip buffered input - unsigned char buf[256]; - while (::read(m_fd, &buf, 256) > 0) { - } - } - if (m_bufSize == 0) { - m_bufSize = MAX_LEN+1; - m_buffer = (unsigned char*)malloc(m_bufSize); - if (!m_buffer) { - m_bufSize = 0; - } - } - m_bufLen = 0; - if (m_initialSend && write(ESC) != 1) { - return RESULT_ERR_SEND; - } - return RESULT_OK; + if (m_fd != -1) { + close(); + } + m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); + if (m_fd < 0) { + return RESULT_ERR_GENERIC_IO; + } + int ret; + if (m_udp) { + struct sockaddr_in address = m_address; + address.sin_addr.s_addr = INADDR_ANY; + ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address)); + } else { + int value = 1; + ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&value), sizeof(value)); + value = 1; + setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&value), sizeof(value)); + } + if (ret == 0) { + ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address)); + } + if (ret < 0) { + close(); + return RESULT_ERR_GENERIC_IO; + } + int cnt; + if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) { + // skip buffered input + unsigned char buf[256]; + while (::read(m_fd, &buf, 256) > 0) { + } + } + if (m_bufSize == 0) { + m_bufSize = MAX_LEN+1; + m_buffer = (unsigned char*)malloc(m_bufSize); + if (!m_buffer) { + m_bufSize = 0; + } + } + m_bufLen = 0; + if (m_initialSend && write(ESC) != 1) { + return RESULT_ERR_SEND; + } + return RESULT_OK; } void NetworkDevice::checkDevice() { - unsigned char value; - ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT); - if (c == 0 || (c < 0 && errno != EAGAIN)) { - m_bufLen = 0; // flush read buffer - close(); - } + unsigned char value; + ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT); + if (c == 0 || (c < 0 && errno != EAGAIN)) { + m_bufLen = 0; // flush read buffer + close(); + } } bool NetworkDevice::available() { - return m_buffer && m_bufLen > 0; + return m_buffer && m_bufLen > 0; } ssize_t NetworkDevice::write(const unsigned char value) { - m_bufLen = 0; // flush read buffer - return Device::write(value); + m_bufLen = 0; // flush read buffer + return Device::write(value); } ssize_t NetworkDevice::read(unsigned char& value) { - if (available()) { - value = m_buffer[m_bufPos]; - m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize); - m_bufLen--; - return 1; - } - if (m_bufSize > 0) { - ssize_t size = ::read(m_fd, m_buffer, m_bufSize); - if (size <= 0) { - return size; - } - value = m_buffer[0]; - m_bufPos = 1; - m_bufLen = (unsigned char)(size-1); - return size; - } - return Device::read(value); + if (available()) { + value = m_buffer[m_bufPos]; + m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize); + m_bufLen--; + return 1; + } + if (m_bufSize > 0) { + ssize_t size = ::read(m_fd, m_buffer, m_bufSize); + if (size <= 0) { + return size; + } + value = m_buffer[0]; + m_bufPos = 1; + m_bufLen = (unsigned char)(size-1); + return size; + } + return Device::read(value); } } // namespace ebusd diff --git a/src/lib/ebus/device.h b/src/lib/ebus/device.h index cb17d7d8..df384009 100644 --- a/src/lib/ebus/device.h +++ b/src/lib/ebus/device.h @@ -42,18 +42,18 @@ namespace ebusd { * Interface for listening to data received on/sent to a device. */ class DeviceListener { - public: - /** - * Destructor. - */ - virtual ~DeviceListener() {} + public: + /** + * Destructor. + */ + virtual ~DeviceListener() {} - /** - * Listener method that is called when a data byte was received/sent. - * @param byte the data byte received/sent. - * @param received @a true on reception, @a false on sending. - */ - virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract + /** + * Listener method that is called when a data byte was received/sent. + * @param byte the data byte received/sent. + * @param received @a true on reception, @a false on sending. + */ + virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract }; @@ -61,227 +61,227 @@ class DeviceListener { * The base class for accessing an eBUS. */ class Device { - public: - /** - * Construct a new instance. - * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param checkDevice whether to regularly check the device availability (only for serial devices). - * @param readOnly whether to allow read access to the device only. - * @param initialSend whether to send an initial @a ESC symbol in @a open(). - */ - Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) - : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), - m_listener(NULL) {} + public: + /** + * Construct a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param checkDevice whether to regularly check the device availability (only for serial devices). + * @param readOnly whether to allow read access to the device only. + * @param initialSend whether to send an initial @a ESC symbol in @a open(). + */ + Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) + : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), + m_listener(NULL) {} - /** - * Destructor. - */ - virtual ~Device(); + /** + * Destructor. + */ + virtual ~Device(); - /** - * Factory method for creating a new instance. - * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param checkDevice whether to regularly check the device availability (only for serial devices). - * @param readOnly whether to allow read access to the device only. - * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @return the new @a Device, or NULL on error. - * Note: the caller needs to free the created instance. - */ - static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false); + /** + * Factory method for creating a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param checkDevice whether to regularly check the device availability (only for serial devices). + * @param readOnly whether to allow read access to the device only. + * @param initialSend whether to send an initial @a ESC symbol in @a open(). + * @return the new @a Device, or NULL on error. + * Note: the caller needs to free the created instance. + */ + static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false); - /** - * Get the transfer latency of this device. - * @return the transfer latency in microseconds. - */ - virtual unsigned int getLatency() const { return 0; } + /** + * Get the transfer latency of this device. + * @return the transfer latency in microseconds. + */ + virtual unsigned int getLatency() const { return 0; } - /** - * Open the file descriptor. - * @return the @a result_t code. - */ - virtual result_t open() = 0; // abstract + /** + * Open the file descriptor. + * @return the @a result_t code. + */ + virtual result_t open() = 0; // abstract - /** - * Close the file descriptor if opened. - */ - virtual void close(); + /** + * Close the file descriptor if opened. + */ + virtual void close(); - /** - * Write a single byte to the device. - * @param value the byte value to write. - * @return the @a result_t code. - */ - result_t send(const unsigned char value); + /** + * Write a single byte to the device. + * @param value the byte value to write. + * @return the @a result_t code. + */ + result_t send(const unsigned char value); - /** - * Read a single byte from the device. - * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite. - * @param value the reference in which the received byte value is stored. - * @return the result_t code. - */ - result_t recv(const unsigned int timeout, unsigned char& value); + /** + * Read a single byte from the device. + * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite. + * @param value the reference in which the received byte value is stored. + * @return the result_t code. + */ + result_t recv(const unsigned int timeout, unsigned char& value); - /** - * Return the device name. - * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - */ - const char* getName() { return m_name; } + /** + * Return the device name. + * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + */ + const char* getName() { return m_name; } - /** - * Return whether the device is opened and available. - * @return whether the device is opened and available. - */ - bool isValid(); + /** + * Return whether the device is opened and available. + * @return whether the device is opened and available. + */ + bool isValid(); - /** - * Return whether to allow read access to the device only. - * @return whether to allow read access to the device only. - */ - bool isReadOnly() const { return m_readOnly; } + /** + * Return whether to allow read access to the device only. + * @return whether to allow read access to the device only. + */ + bool isReadOnly() const { return m_readOnly; } - /** - * Set the @a DeviceListener. - * @param listener the @a DeviceListener. - */ - void setListener(DeviceListener* listener) { m_listener = listener; } + /** + * Set the @a DeviceListener. + * @param listener the @a DeviceListener. + */ + void setListener(DeviceListener* listener) { m_listener = listener; } - protected: - /** - * Check if the device is still available and close it if not. - */ - virtual void checkDevice() = 0; // abstract + protected: + /** + * Check if the device is still available and close it if not. + */ + virtual void checkDevice() = 0; // abstract - /** - * Check whether a byte is available immediately (without waiting). - * @return true when a a byte is available immediately. - */ - virtual bool available() { return false; } + /** + * Check whether a byte is available immediately (without waiting). + * @return true when a a byte is available immediately. + */ + virtual bool available() { return false; } - /** - * Write a single byte. - * @param value the byte value to write. - * @return the number of bytes written, or -1 on error. - */ - virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); } + /** + * Write a single byte. + * @param value the byte value to write. + * @return the number of bytes written, or -1 on error. + */ + virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); } - /** - * Read a single byte. - * @param value the reference in which the read byte value is stored. - * @return the number of bytes read, or -1 on error. - */ - virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); } + /** + * Read a single byte. + * @param value the reference in which the read byte value is stored. + * @return the number of bytes read, or -1 on error. + */ + virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); } - /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ - const char* m_name; + /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ + const char* m_name; - /** whether to regularly check the device availability (only for serial devices). */ - const bool m_checkDevice; + /** whether to regularly check the device availability (only for serial devices). */ + const bool m_checkDevice; - /** whether to allow read access to the device only. */ - const bool m_readOnly; + /** whether to allow read access to the device only. */ + const bool m_readOnly; - /** whether to send an initial @a ESC symbol in @a open(). */ - const bool m_initialSend; + /** whether to send an initial @a ESC symbol in @a open(). */ + const bool m_initialSend; - /** the opened file descriptor, or -1. */ - int m_fd; + /** the opened file descriptor, or -1. */ + int m_fd; - private: - /** the @a DeviceListener, or NULL. */ - DeviceListener* m_listener; + private: + /** the @a DeviceListener, or NULL. */ + DeviceListener* m_listener; }; /** * The @a Device for directly connected serial interfaces (tty). */ class SerialDevice : public Device { - public: - /** - * Construct a new instance. - * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param checkDevice whether to regularly check the device availability (only for serial devices). - * @param readOnly whether to allow read access to the device only. - * @param initialSend whether to send an initial @a ESC symbol in @a open(). - */ - SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) - : Device(name, checkDevice, readOnly, initialSend) {} + public: + /** + * Construct a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param checkDevice whether to regularly check the device availability (only for serial devices). + * @param readOnly whether to allow read access to the device only. + * @param initialSend whether to send an initial @a ESC symbol in @a open(). + */ + SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) + : Device(name, checkDevice, readOnly, initialSend) {} - // @copydoc - virtual result_t open(); + // @copydoc + virtual result_t open(); - // @copydoc - virtual void close(); + // @copydoc + virtual void close(); - protected: - // @copydoc - virtual void checkDevice(); + protected: + // @copydoc + virtual void checkDevice(); - private: - /** the previous settings of the device for restoring. */ - termios m_oldSettings; + private: + /** the previous settings of the device for restoring. */ + termios m_oldSettings; }; /** * The @a Device for remote network interfaces. */ class NetworkDevice : public Device { - public: - /** - * Construct a new instance. - * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). - * @param address the socket address of the device. - * @param readOnly whether to allow read access to the device only. - * @param initialSend whether to send an initial @a ESC symbol in @a open(). - * @param udp true for UDP, false to TCP. - */ - NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend, - const bool udp) - : Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp), - m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} + public: + /** + * Construct a new instance. + * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). + * @param address the socket address of the device. + * @param readOnly whether to allow read access to the device only. + * @param initialSend whether to send an initial @a ESC symbol in @a open(). + * @param udp true for UDP, false to TCP. + */ + NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend, + const bool udp) + : Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp), + m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} - // @copydoc - virtual unsigned int getLatency() const { return 10000; } + // @copydoc + virtual unsigned int getLatency() const { return 10000; } - // @copydoc - virtual result_t open(); + // @copydoc + virtual result_t open(); - protected: - // @copydoc - virtual void checkDevice(); + protected: + // @copydoc + virtual void checkDevice(); - // @copydoc - virtual bool available(); + // @copydoc + virtual bool available(); - // @copydoc - virtual ssize_t write(const unsigned char value); + // @copydoc + virtual ssize_t write(const unsigned char value); - // @copydoc - virtual ssize_t read(unsigned char& value); + // @copydoc + virtual ssize_t read(unsigned char& value); - private: - /** the socket address of the device. */ - const struct sockaddr_in m_address; + private: + /** the socket address of the device. */ + const struct sockaddr_in m_address; - /** true for UDP, false to TCP. */ - const bool m_udp; + /** true for UDP, false to TCP. */ + const bool m_udp; - /** the buffer memory, or NULL. */ - unsigned char* m_buffer; + /** the buffer memory, or NULL. */ + unsigned char* m_buffer; - /** the buffer size. */ - unsigned char m_bufSize; + /** the buffer size. */ + unsigned char m_bufSize; - /** the buffer fill length. */ - unsigned char m_bufLen; + /** the buffer fill length. */ + unsigned char m_bufLen; - /** the buffer read position. */ - unsigned char m_bufPos; + /** the buffer read position. */ + unsigned char m_bufPos; }; } // namespace ebusd diff --git a/src/lib/ebus/filereader.h b/src/lib/ebus/filereader.h index eea8751b..cc04313d 100644 --- a/src/lib/ebus/filereader.h +++ b/src/lib/ebus/filereader.h @@ -70,319 +70,319 @@ extern unsigned int parseInt(const char* str, int base, const unsigned int minVa * An abstract class that support reading definitions from a file. */ class FileReader { - public: - /** - * Construct a new instance. - */ - explicit FileReader(bool supportsDefaults) - : m_supportsDefaults(supportsDefaults) {} + public: + /** + * Construct a new instance. + */ + explicit FileReader(bool supportsDefaults) + : m_supportsDefaults(supportsDefaults) {} - /** - * Destructor. - */ - virtual ~FileReader() {} + /** + * Destructor. + */ + virtual ~FileReader() {} - /** - * Read the definitions from a file. - * @param filename the name of the file being read. - * @param verbose whether to verbosely log problems. - * @param defaultDest the default destination address (may be overwritten by file name), or empty. - * @param defaultCircuit the default circuit name (may be overwritten by file name), or empty. - * @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t readFromFile(const string filename, bool verbose = false, - string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") { - ifstream ifs; - ifs.open(filename.c_str(), ifstream::in); - if (!ifs.is_open()) { - m_lastError = filename; - return RESULT_ERR_NOTFOUND; - } - size_t lastSep = filename.find_last_of('/'); - if (lastSep != string::npos) { // potential destination address, matches "^ZZ." - // extract defaultDest, defaultCircuit, defaultSuffix from filename: - // ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv - unsigned char checkDest; - string checkIdent, useCircuit, useSuffix; - unsigned int checkSw, checkHw; - if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { - defaultDest = filename.substr(lastSep+1, 2); - if (!useCircuit.empty()) { - defaultCircuit = useCircuit; - } - if (!useSuffix.empty()) { - defaultSuffix = useSuffix; - } - } - } - unsigned int lineNo = 0; - vector row; - vector< vector > defaults; - while (splitFields(ifs, row, lineNo)) { - if (row.empty()) { - continue; - } - result_t result; - vector::iterator it = row.begin(); - const vector::iterator end = row.end(); - if (m_supportsDefaults) { - if (row[0][0] == '*') { - row[0] = row[0].substr(1); - result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); - if (result == RESULT_OK) { - continue; - } - } else { - result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); - } - } else { - result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); - } - if (result != RESULT_OK) { - if (!verbose) { - ifs.close(); - ostringstream error; - error << filename << ":" << static_cast(lineNo); - if (m_lastError.length() > 0) { - error << ": " << m_lastError; - } - m_lastError = error.str(); - return result; - } - if (m_lastError.length() > 0) { - cout << m_lastError << endl; - } - printErrorPos(cout, row.begin(), end, it, filename, lineNo, result); - } else if (!verbose) { - m_lastError = ""; - } - } + /** + * Read the definitions from a file. + * @param filename the name of the file being read. + * @param verbose whether to verbosely log problems. + * @param defaultDest the default destination address (may be overwritten by file name), or empty. + * @param defaultCircuit the default circuit name (may be overwritten by file name), or empty. + * @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t readFromFile(const string filename, bool verbose = false, + string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") { + ifstream ifs; + ifs.open(filename.c_str(), ifstream::in); + if (!ifs.is_open()) { + m_lastError = filename; + return RESULT_ERR_NOTFOUND; + } + size_t lastSep = filename.find_last_of('/'); + if (lastSep != string::npos) { // potential destination address, matches "^ZZ." + // extract defaultDest, defaultCircuit, defaultSuffix from filename: + // ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv + unsigned char checkDest; + string checkIdent, useCircuit, useSuffix; + unsigned int checkSw, checkHw; + if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { + defaultDest = filename.substr(lastSep+1, 2); + if (!useCircuit.empty()) { + defaultCircuit = useCircuit; + } + if (!useSuffix.empty()) { + defaultSuffix = useSuffix; + } + } + } + unsigned int lineNo = 0; + vector row; + vector< vector > defaults; + while (splitFields(ifs, row, lineNo)) { + if (row.empty()) { + continue; + } + result_t result; + vector::iterator it = row.begin(); + const vector::iterator end = row.end(); + if (m_supportsDefaults) { + if (row[0][0] == '*') { + row[0] = row[0].substr(1); + result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); + if (result == RESULT_OK) { + continue; + } + } else { + result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); + } + } else { + result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); + } + if (result != RESULT_OK) { + if (!verbose) { + ifs.close(); + ostringstream error; + error << filename << ":" << static_cast(lineNo); + if (m_lastError.length() > 0) { + error << ": " << m_lastError; + } + m_lastError = error.str(); + return result; + } + if (m_lastError.length() > 0) { + cout << m_lastError << endl; + } + printErrorPos(cout, row.begin(), end, it, filename, lineNo, result); + } else if (!verbose) { + m_lastError = ""; + } + } - ifs.close(); - return RESULT_OK; - } + ifs.close(); + return RESULT_OK; + } - /** - * Return a @a string describing the last error position. - * @return a @a string describing the last error position. - */ - virtual string getLastError() { return m_lastError; } + /** + * Return a @a string describing the last error position. + * @return a @a string describing the last error position. + */ + virtual string getLastError() { return m_lastError; } - /** - * Add a default row that was read from a file. - * @param defaults the list to add the default row to. - * @param row the default row (initial star char removed). - * @param begin an iterator to the first column of the default row to read (for error reporting). - * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. - * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. - * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. - * @param filename the name of the file being read. - * @param lineNo the current line number in the file being read. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t addDefaultFromFile(vector< vector >& defaults, vector& row, - vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, - const string& filename, unsigned int lineNo) { - defaults.push_back(row); - begin = row.end(); - return RESULT_OK; - } + /** + * Add a default row that was read from a file. + * @param defaults the list to add the default row to. + * @param row the default row (initial star char removed). + * @param begin an iterator to the first column of the default row to read (for error reporting). + * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. + * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. + * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. + * @param filename the name of the file being read. + * @param lineNo the current line number in the file being read. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t addDefaultFromFile(vector< vector >& defaults, vector& row, + vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, + const string& filename, unsigned int lineNo) { + defaults.push_back(row); + begin = row.end(); + return RESULT_OK; + } - /** - * Add a definition that was read from a file. - * @param begin an iterator to the first column of the definition row to read. - * @param end the end iterator of the definition row to read. - * @param defaults all previously read default rows (initial star char removed), or NULL if not supported. - * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. - * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. - * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. - * @param filename the name of the file being read. - * @param lineNo the current line number in the file being read. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, - vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - const string& filename, unsigned int lineNo) = 0; + /** + * Add a definition that was read from a file. + * @param begin an iterator to the first column of the definition row to read. + * @param end the end iterator of the definition row to read. + * @param defaults all previously read default rows (initial star char removed), or NULL if not supported. + * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. + * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. + * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. + * @param filename the name of the file being read. + * @param lineNo the current line number in the file being read. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, + vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + const string& filename, unsigned int lineNo) = 0; - /** - * Left and right trim the string. - * @param str the @a string to trim. - */ - static void trim(string& str) { - size_t pos = str.find_first_not_of(" \t"); - if (pos != string::npos) { - str.erase(0, pos); - } - pos = str.find_last_not_of(" \t"); - if (pos != string::npos) { - str.erase(pos+1); - } - } + /** + * Left and right trim the string. + * @param str the @a string to trim. + */ + static void trim(string& str) { + size_t pos = str.find_first_not_of(" \t"); + if (pos != string::npos) { + str.erase(0, pos); + } + pos = str.find_last_not_of(" \t"); + if (pos != string::npos) { + str.erase(pos+1); + } + } - /** - * Convert all upper case characters in the string to lower case. - * @param str the @a string to convert. - */ - static void tolower(string& str) { - transform(str.begin(), str.end(), str.begin(), ::tolower); - } + /** + * Convert all upper case characters in the string to lower case. + * @param str the @a string to convert. + */ + static void tolower(string& str) { + transform(str.begin(), str.end(), str.begin(), ::tolower); + } - /** - * Split the next line(s) from the @a istring into fields. - * @param ifs the @a istream to read from. - * @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines. - * @param lineNo the current line number (incremented with each line read). - * @return true if there are more lines to read, false when there are no more lines left. - */ - static bool splitFields(istream& ifs, vector& row, unsigned int& lineNo) { - row.clear(); - string line; - bool quotedText = false, wasQuoted = false; - ostringstream field; - char prev = FIELD_SEPARATOR; - bool empty = true, read = false; - while (getline(ifs, line)) { - read = true; - lineNo++; - trim(line); + /** + * Split the next line(s) from the @a istring into fields. + * @param ifs the @a istream to read from. + * @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines. + * @param lineNo the current line number (incremented with each line read). + * @return true if there are more lines to read, false when there are no more lines left. + */ + static bool splitFields(istream& ifs, vector& row, unsigned int& lineNo) { + row.clear(); + string line; + bool quotedText = false, wasQuoted = false; + ostringstream field; + char prev = FIELD_SEPARATOR; + bool empty = true, read = false; + while (getline(ifs, line)) { + read = true; + lineNo++; + trim(line); - size_t length = line.length(); - if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) { - continue; // skip empty lines and comments - } - for (size_t pos = 0; pos < length; pos++) { - char ch = line[pos]; - switch (ch) { - case FIELD_SEPARATOR: - if (quotedText) { - field << ch; - } else { - string str = field.str(); - trim(str); - empty &= str.empty(); - row.push_back(str); - field.str(""); - wasQuoted = false; - } - break; - case TEXT_SEPARATOR: - if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote - field << ch; - quotedText = true; - } else if (quotedText) { - quotedText = false; - } else if (prev == FIELD_SEPARATOR) { - quotedText = wasQuoted = true; - } else { - field << ch; - } - break; - case '\r': - break; - default: - if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) { - field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text - quotedText = true; - } else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) { - field << VALUE_SEPARATOR; - } - field << ch; - break; - } - prev = ch; - } - if (!quotedText) { - break; - } - } - string str = field.str(); - trim(str); - if (empty && str.empty()) { - row.clear(); - return read; - } - row.push_back(str); - return true; - } + size_t length = line.length(); + if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) { + continue; // skip empty lines and comments + } + for (size_t pos = 0; pos < length; pos++) { + char ch = line[pos]; + switch (ch) { + case FIELD_SEPARATOR: + if (quotedText) { + field << ch; + } else { + string str = field.str(); + trim(str); + empty &= str.empty(); + row.push_back(str); + field.str(""); + wasQuoted = false; + } + break; + case TEXT_SEPARATOR: + if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote + field << ch; + quotedText = true; + } else if (quotedText) { + quotedText = false; + } else if (prev == FIELD_SEPARATOR) { + quotedText = wasQuoted = true; + } else { + field << ch; + } + break; + case '\r': + break; + default: + if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) { + field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text + quotedText = true; + } else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) { + field << VALUE_SEPARATOR; + } + field << ch; + break; + } + prev = ch; + } + if (!quotedText) { + break; + } + } + string str = field.str(); + trim(str); + if (empty && str.empty()) { + row.clear(); + return read; + } + row.push_back(str); + return true; + } - /** - * Extract default values from the file name. - * @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv". - * @param dest the output destination address ZZ (hex digits). - * @param ident the identification part IDENT (up to 5 characters, set to empty if not present). - * @param circuit the circuit part CIRCUIT (set to IDENT if not present). - * @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present). - * @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present). - * @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present). - * @return true if at least the address and the identification part were extracted, false otherwise. - */ - static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit, - string& suffix, unsigned int& software, unsigned int& hardware) { - ident = circuit = suffix = ""; - software = hardware = UINT_MAX; - if (name.length() > 4 && name.substr(name.length()-4) == ".csv") { - name = name.substr(0, name.length()-3); // including trailing "." - } - size_t pos = name.find('.'); - if (pos != 2) { - return false; // missing "ZZ." - } - result_t result = RESULT_OK; - dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL); - if (result != RESULT_OK || !isValidAddress(dest)) { - return false; // invalid "ZZ" - } - name.erase(0, pos); - if (name.length() > 1) { - pos = name.rfind(".SW"); // check for ".SWxxxx." - if (pos != string::npos && name.find(".", pos+1) == pos+7) { - software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); - if (result != RESULT_OK) { - return false; // invalid "SWxxxx" - } - name.erase(pos, 7); - } - } - if (name.length() > 1) { - pos = name.rfind(".HW"); // check for ".HWxxxx." - if (pos != string::npos && name.find(".", pos+1) == pos+7) { - hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); - if (result != RESULT_OK) { - return false; // invalid "HWxxxx" - } - name.erase(pos, 7); - } - } - if (name.length() > 1) { - pos = name.find('.', 1); // check for ".IDENT." - if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".." - ident = circuit = name.substr(1, pos-1); - name.erase(0, pos); - pos = name.find('.', 1); // check for ".CIRCUIT." - if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) { - circuit = name.substr(1, pos-1); - name.erase(0, pos); - pos = name.find('.', 1); // check for ".SUFFIX." - } - if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') { - suffix = name.substr(0, 2); - name.erase(0, pos); - } - } - } - return true; - } + /** + * Extract default values from the file name. + * @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv". + * @param dest the output destination address ZZ (hex digits). + * @param ident the identification part IDENT (up to 5 characters, set to empty if not present). + * @param circuit the circuit part CIRCUIT (set to IDENT if not present). + * @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present). + * @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present). + * @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present). + * @return true if at least the address and the identification part were extracted, false otherwise. + */ + static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit, + string& suffix, unsigned int& software, unsigned int& hardware) { + ident = circuit = suffix = ""; + software = hardware = UINT_MAX; + if (name.length() > 4 && name.substr(name.length()-4) == ".csv") { + name = name.substr(0, name.length()-3); // including trailing "." + } + size_t pos = name.find('.'); + if (pos != 2) { + return false; // missing "ZZ." + } + result_t result = RESULT_OK; + dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL); + if (result != RESULT_OK || !isValidAddress(dest)) { + return false; // invalid "ZZ" + } + name.erase(0, pos); + if (name.length() > 1) { + pos = name.rfind(".SW"); // check for ".SWxxxx." + if (pos != string::npos && name.find(".", pos+1) == pos+7) { + software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); + if (result != RESULT_OK) { + return false; // invalid "SWxxxx" + } + name.erase(pos, 7); + } + } + if (name.length() > 1) { + pos = name.rfind(".HW"); // check for ".HWxxxx." + if (pos != string::npos && name.find(".", pos+1) == pos+7) { + hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); + if (result != RESULT_OK) { + return false; // invalid "HWxxxx" + } + name.erase(pos, 7); + } + } + if (name.length() > 1) { + pos = name.find('.', 1); // check for ".IDENT." + if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".." + ident = circuit = name.substr(1, pos-1); + name.erase(0, pos); + pos = name.find('.', 1); // check for ".CIRCUIT." + if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) { + circuit = name.substr(1, pos-1); + name.erase(0, pos); + pos = name.find('.', 1); // check for ".SUFFIX." + } + if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') { + suffix = name.substr(0, 2); + name.erase(0, pos); + } + } + } + return true; + } - private: - /** whether this instance supports rows with defaults (starting with a star). */ - bool m_supportsDefaults; + private: + /** whether this instance supports rows with defaults (starting with a star). */ + bool m_supportsDefaults; - protected: - /** a @a string describing the last error position. */ - string m_lastError; + protected: + /** a @a string describing the last error position. */ + string m_lastError; }; } // namespace ebusd diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 0714210e..0c70c231 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -59,48 +59,48 @@ using std::setw; extern DataFieldTemplates* getTemplates(const string filename); Message::Message(const string circuit, const string name, - const bool isWrite, const bool isPassive, const string comment, - const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, - DataField* data, const bool deleteData, - const unsigned char pollPriority, - Condition* condition) - : m_circuit(circuit), m_name(name), m_isWrite(isWrite), - m_isPassive(isPassive), m_comment(comment), - m_srcAddress(srcAddress), m_dstAddress(dstAddress), - m_id(id), m_data(data), m_deleteData(deleteData), - m_pollPriority(pollPriority), - m_usedByCondition(false), m_isScanMessage(false), m_condition(condition), - m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { - m_key = createKey(id, isWrite, isPassive, srcAddress, dstAddress); - if (circuit == "scan") { - setScanMessage(); - m_pollPriority = 0; - } + const bool isWrite, const bool isPassive, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition) + : m_circuit(circuit), m_name(name), m_isWrite(isWrite), + m_isPassive(isPassive), m_comment(comment), + m_srcAddress(srcAddress), m_dstAddress(dstAddress), + m_id(id), m_data(data), m_deleteData(deleteData), + m_pollPriority(pollPriority), + m_usedByCondition(false), m_isScanMessage(false), m_condition(condition), + m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { + m_key = createKey(id, isWrite, isPassive, srcAddress, dstAddress); + if (circuit == "scan") { + setScanMessage(); + m_pollPriority = 0; + } } Message::Message(const string circuit, const string name, - const bool isWrite, const bool isPassive, - const unsigned char pb, const unsigned char sb, - DataField* data, const bool deleteData) - : m_circuit(circuit), m_name(name), m_isWrite(isWrite), - m_isPassive(isPassive), m_comment(), - m_srcAddress(SYN), m_dstAddress(SYN), - m_data(data), m_deleteData(true), - m_pollPriority(0), - m_usedByCondition(false), m_isScanMessage(false), m_condition(NULL), - m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { - m_id.push_back(pb); - m_id.push_back(sb); - uint64_t key = 0; - if (!isPassive) { - key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active - } - key |= (uint64_t)SYN << (8 * 6); - key |= (uint64_t)pb << (8 * 5); - key |= (uint64_t)sb << (8 * 4); - m_key = key; - setScanMessage(); + const bool isWrite, const bool isPassive, + const unsigned char pb, const unsigned char sb, + DataField* data, const bool deleteData) + : m_circuit(circuit), m_name(name), m_isWrite(isWrite), + m_isPassive(isPassive), m_comment(), + m_srcAddress(SYN), m_dstAddress(SYN), + m_data(data), m_deleteData(true), + m_pollPriority(0), + m_usedByCondition(false), m_isScanMessage(false), m_condition(NULL), + m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) { + m_id.push_back(pb); + m_id.push_back(sb); + uint64_t key = 0; + if (!isPassive) { + key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active + } + key |= (uint64_t)SYN << (8 * 6); + key |= (uint64_t)pb << (8 * 5); + key |= (uint64_t)sb << (8 * 4); + m_key = key; + setScanMessage(); } @@ -117,976 +117,976 @@ Message::Message(const string circuit, const string name, * @return the default if available and value is empty, or the value. */ string getDefault(const string value, vector* defaults, size_t pos, bool replaceStar = false, bool required = false) { - if (defaults == NULL || pos >= defaults->size()) { - return value; - } - if (value.length() == 0 && replaceStar && required) { - return value; - } - string defaultStr = defaults->at(pos); - if (!replaceStar || defaultStr.length() == 0) { - return value.length() > 0 ? value : defaultStr; - } - string::size_type insertPos = defaultStr.find('*'); - if (insertPos == string::npos) { - return value.length() == 0 ? defaultStr : value; - } - return defaultStr.substr(0, insertPos)+value+defaultStr.substr(insertPos+1); + if (defaults == NULL || pos >= defaults->size()) { + return value; + } + if (value.length() == 0 && replaceStar && required) { + return value; + } + string defaultStr = defaults->at(pos); + if (!replaceStar || defaultStr.length() == 0) { + return value.length() > 0 ? value : defaultStr; + } + string::size_type insertPos = defaultStr.find('*'); + if (insertPos == string::npos) { + return value.length() == 0 ? defaultStr : value; + } + return defaultStr.substr(0, insertPos)+value+defaultStr.substr(insertPos+1); } uint64_t Message::createKey(const vector id, - const bool isWrite, const bool isPassive, - const unsigned char srcAddress, const unsigned char dstAddress) { - uint64_t key = (uint64_t)(id.size()-2) << (8 * 7 + 5); - if (isPassive) { - key |= (uint64_t)getMasterNumber(srcAddress) << (8 * 7); // 0..25 - } else { - key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active - } - key |= (uint64_t)dstAddress << (8 * 6); - int exp = 5; - for (vector::const_iterator it = id.begin(); it < id.end(); it++) { - key ^= (uint64_t)*it << (8 * exp--); - if (exp == 0) { - exp = 3; - } - } - return key; + const bool isWrite, const bool isPassive, + const unsigned char srcAddress, const unsigned char dstAddress) { + uint64_t key = (uint64_t)(id.size()-2) << (8 * 7 + 5); + if (isPassive) { + key |= (uint64_t)getMasterNumber(srcAddress) << (8 * 7); // 0..25 + } else { + key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active + } + key |= (uint64_t)dstAddress << (8 * 6); + int exp = 5; + for (vector::const_iterator it = id.begin(); it < id.end(); it++) { + key ^= (uint64_t)*it << (8 * exp--); + if (exp == 0) { + exp = 3; + } + } + return key; } uint64_t Message::createKey(SymbolString& master, unsigned char maxIdLength, bool anyDestination) { - if (master.size() < 5) { - return INVALID_KEY; - } - unsigned char idLength = master[4]; - if (maxIdLength < idLength) { - idLength = maxIdLength; - } - if (master.size() < 5+idLength) { - return INVALID_KEY; - } - uint64_t key = (uint64_t)idLength << (8 * 7 + 5); - key |= (uint64_t)getMasterNumber(master[0]) << (8 * 7); // QQ address for passive message - key |= (uint64_t)(anyDestination ? SYN : master[1]) << (8 * 6); // ZZ address - key |= (uint64_t)master[2] << (8 * 5); // PB - key |= (uint64_t)master[3] << (8 * 4); // SB - int exp = 3; - for (unsigned char i = 0; i < idLength; i++) { - key ^= (uint64_t)master[5 + i] << (8 * exp--); - if (exp == 0) { - exp = 3; - } - } - return key; + if (master.size() < 5) { + return INVALID_KEY; + } + unsigned char idLength = master[4]; + if (maxIdLength < idLength) { + idLength = maxIdLength; + } + if (master.size() < 5+idLength) { + return INVALID_KEY; + } + uint64_t key = (uint64_t)idLength << (8 * 7 + 5); + key |= (uint64_t)getMasterNumber(master[0]) << (8 * 7); // QQ address for passive message + key |= (uint64_t)(anyDestination ? SYN : master[1]) << (8 * 6); // ZZ address + key |= (uint64_t)master[2] << (8 * 5); // PB + key |= (uint64_t)master[3] << (8 * 4); // SB + int exp = 3; + for (unsigned char i = 0; i < idLength; i++) { + key ^= (uint64_t)master[5 + i] << (8 * exp--); + if (exp == 0) { + exp = 3; + } + } + return key; } result_t Message::parseId(string input, vector& id) { - istringstream in(input); - while (!in.eof()) { - while (in.peek() == ' ') { - in.get(); - } - if (in.eof()) { // no more digits - break; - } - input.clear(); - input.push_back(static_cast(in.get())); - if (in.eof()) { - return RESULT_ERR_INVALID_ARG; // too short hex - } - input.push_back(static_cast(in.get())); + istringstream in(input); + while (!in.eof()) { + while (in.peek() == ' ') { + in.get(); + } + if (in.eof()) { // no more digits + break; + } + input.clear(); + input.push_back(static_cast(in.get())); + if (in.eof()) { + return RESULT_ERR_INVALID_ARG; // too short hex + } + input.push_back(static_cast(in.get())); - result_t result; - unsigned char value = (unsigned char)parseInt(input.c_str(), 16, 0, 0xff, result); - if (result != RESULT_OK) { - return result; // invalid hex value - } - id.push_back(value); - } - return RESULT_OK; + result_t result; + unsigned char value = (unsigned char)parseInt(input.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; // invalid hex value + } + id.push_back(value); + } + return RESULT_OK; } result_t Message::create(vector::iterator& it, const vector::iterator end, - vector< vector >* defaultsRows, Condition* condition, const string& filename, - DataFieldTemplates* templates, vector& messages) { - // [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... - result_t result; - bool isWrite = false, isPassive = false; - string defaultName; - unsigned char pollPriority = 0; - size_t defaultPos = 1; - if (it == end) { - return RESULT_ERR_EOF; - } - string typeStr = *it++; - const char* str = typeStr.c_str(); // [type] - if (it == end) { - return RESULT_ERR_EOF; - } - size_t len = strlen(str); - if (len == 0) { // default: active read - defaultName = "r"; - } else { - defaultName = str; - char type = str[0]; - if (type == 'r' || type == 'R') { // active read - char poll = str[1]; - if (poll >= '0' && poll <= '9') { // poll priority (=active read) - pollPriority = (unsigned char)(poll - '0'); - defaultName.erase(1, 1); // cut off priority digit - } - } else if (type == 'w' || type == 'W') { // active write - isWrite = true; - } else { // any other: passive read/write - isPassive = true; - type = str[1]; - isWrite = type == 'w' || type == 'W'; // if type continues with "w" it is treated as passive write - } - } + vector< vector >* defaultsRows, Condition* condition, const string& filename, + DataFieldTemplates* templates, vector& messages) { + // [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... + result_t result; + bool isWrite = false, isPassive = false; + string defaultName; + unsigned char pollPriority = 0; + size_t defaultPos = 1; + if (it == end) { + return RESULT_ERR_EOF; + } + string typeStr = *it++; + const char* str = typeStr.c_str(); // [type] + if (it == end) { + return RESULT_ERR_EOF; + } + size_t len = strlen(str); + if (len == 0) { // default: active read + defaultName = "r"; + } else { + defaultName = str; + char type = str[0]; + if (type == 'r' || type == 'R') { // active read + char poll = str[1]; + if (poll >= '0' && poll <= '9') { // poll priority (=active read) + pollPriority = (unsigned char)(poll - '0'); + defaultName.erase(1, 1); // cut off priority digit + } + } else if (type == 'w' || type == 'W') { // active write + isWrite = true; + } else { // any other: passive read/write + isPassive = true; + type = str[1]; + isWrite = type == 'w' || type == 'W'; // if type continues with "w" it is treated as passive write + } + } - vector* defaults = NULL; - if (defaultsRows != NULL && defaultsRows->size() > 0) { - for (vector< vector >::reverse_iterator it = defaultsRows->rbegin(); it != defaultsRows->rend(); it++) { - string check = (*it)[0]; - if (check == defaultName) { - defaults = &(*it); - break; - } - } - } + vector* defaults = NULL; + if (defaultsRows != NULL && defaultsRows->size() > 0) { + for (vector< vector >::reverse_iterator it = defaultsRows->rbegin(); it != defaultsRows->rend(); it++) { + string check = (*it)[0]; + if (check == defaultName) { + defaults = &(*it); + break; + } + } + } - string circuit = getDefault(*it++, defaults, defaultPos++, true); // [circuit] - if (it == end) { - return RESULT_ERR_EOF; - } - string name = getDefault(*it++, defaults, defaultPos++, true, true); // name - if (it == end) { - return RESULT_ERR_EOF; - } - if (name.length() == 0) { - return RESULT_ERR_INVALID_ARG; // empty name - } - string comment = getDefault(*it++, defaults, defaultPos++, true); // [comment] - if (it == end) { - return RESULT_ERR_EOF; - } - str = getDefault(*it++, defaults, defaultPos++).c_str(); // [QQ[;QQ]*] - if (it == end) { - return RESULT_ERR_EOF; - } - unsigned char srcAddress; - if (*str == 0) { - srcAddress = SYN; // no specific source - } else { - srcAddress = (unsigned char)parseInt(str, 16, 0, 0xff, result); - if (result != RESULT_OK) { - return result; - } - if (!isMaster(srcAddress)) { - return RESULT_ERR_INVALID_ADDR; - } - } + string circuit = getDefault(*it++, defaults, defaultPos++, true); // [circuit] + if (it == end) { + return RESULT_ERR_EOF; + } + string name = getDefault(*it++, defaults, defaultPos++, true, true); // name + if (it == end) { + return RESULT_ERR_EOF; + } + if (name.length() == 0) { + return RESULT_ERR_INVALID_ARG; // empty name + } + string comment = getDefault(*it++, defaults, defaultPos++, true); // [comment] + if (it == end) { + return RESULT_ERR_EOF; + } + str = getDefault(*it++, defaults, defaultPos++).c_str(); // [QQ[;QQ]*] + if (it == end) { + return RESULT_ERR_EOF; + } + unsigned char srcAddress; + if (*str == 0) { + srcAddress = SYN; // no specific source + } else { + srcAddress = (unsigned char)parseInt(str, 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; + } + if (!isMaster(srcAddress)) { + return RESULT_ERR_INVALID_ADDR; + } + } - str = getDefault(*it++, defaults, defaultPos++).c_str(); // [ZZ] - if (it == end) { - return RESULT_ERR_EOF; - } - vector dstAddresses; - bool isBroadcastOrMasterDestination = false; - if (*str == 0) { - dstAddresses.push_back(SYN); // no specific destination - } else { - istringstream stream(str); - string token; - bool first = true; - while (getline(stream, token, VALUE_SEPARATOR)) { - FileReader::trim(token); - unsigned char dstAddress = (unsigned char)parseInt(token.c_str(), 16, 0, 0xff, result); - if (result != RESULT_OK) { - return result; - } - if (!isValidAddress(dstAddress)) { - return RESULT_ERR_INVALID_ADDR; - } - bool broadcastOrMaster = (dstAddress == BROADCAST) || isMaster(dstAddress); - if (first) { - isBroadcastOrMasterDestination = broadcastOrMaster; - first = false; - } else if (isBroadcastOrMasterDestination != broadcastOrMaster) { - return RESULT_ERR_INVALID_ADDR; - } - dstAddresses.push_back(dstAddress); - } - } + str = getDefault(*it++, defaults, defaultPos++).c_str(); // [ZZ] + if (it == end) { + return RESULT_ERR_EOF; + } + vector dstAddresses; + bool isBroadcastOrMasterDestination = false; + if (*str == 0) { + dstAddresses.push_back(SYN); // no specific destination + } else { + istringstream stream(str); + string token; + bool first = true; + while (getline(stream, token, VALUE_SEPARATOR)) { + FileReader::trim(token); + unsigned char dstAddress = (unsigned char)parseInt(token.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; + } + if (!isValidAddress(dstAddress)) { + return RESULT_ERR_INVALID_ADDR; + } + bool broadcastOrMaster = (dstAddress == BROADCAST) || isMaster(dstAddress); + if (first) { + isBroadcastOrMasterDestination = broadcastOrMaster; + first = false; + } else if (isBroadcastOrMasterDestination != broadcastOrMaster) { + return RESULT_ERR_INVALID_ADDR; + } + dstAddresses.push_back(dstAddress); + } + } - vector id; - string token = *it++; // [PBSB] - bool useDefaults = token.empty(); - if (useDefaults) { - token = getDefault(token, defaults, defaultPos); - } - defaultPos++; - result = parseId(token, id); - if (result != RESULT_OK) { - return result; - } - if (id.size() != 2) { - return RESULT_ERR_INVALID_ARG; // missing/to short/to long PBSB - } - if (it == end) { - token = ""; - } else { - token = *it++;// [ID] (optional master data) - } - string defaultIdPrefix; - if (useDefaults) { - defaultIdPrefix = getDefault("", defaults, defaultPos); - } - defaultPos++; - vector< vector > chainIds; - vector chainLengths; - istringstream stream(token); - size_t maxLength = MAX_POS; - size_t chainLength = 16; - size_t chainPrefixLength = id.size(); - bool first = true, lastChainLengthSpecified = false; - while (getline(stream, token, VALUE_SEPARATOR) || first) { - FileReader::trim(token); - token = defaultIdPrefix+token; - size_t lengthPos = token.find(LENGTH_SEPARATOR); - lastChainLengthSpecified = lengthPos != string::npos; - if (lastChainLengthSpecified) { - chainLength = parseInt(token.substr(lengthPos+1).c_str(), 10, 0, MAX_POS, result); - if (result != RESULT_OK) { - return result; - } - token.resize(lengthPos); - } - vector chainId = id; - result = parseId(token, chainId); - if (result != RESULT_OK) { - return result; - } - if (!chainIds.empty() && chainId.size() != chainIds.front().size()) { - return RESULT_ERR_INVALID_LIST; - } - chainIds.push_back(chainId); - chainLengths.push_back((unsigned char)chainLength); - if (first) { - chainPrefixLength = chainId.size(); - maxLength = 0; - } else if (chainPrefixLength > 2) { - vector& front = chainIds.front(); - for (size_t pos = 2; pos < chainPrefixLength; pos++) { - if (chainId[pos] != front[pos]) { - chainPrefixLength = pos; - break; - } - } - } - if (maxLength+chainLength > 255) { - return RESULT_ERR_INVALID_POS; - } - maxLength += chainLength; - first = false; - } - id = chainIds.front(); - if (chainIds.size() > 1) { - if (isPassive) { - return RESULT_ERR_INVALID_LIST; - } - if (id.size() > chainPrefixLength) { - id.resize(chainPrefixLength); - } - if (!lastChainLengthSpecified && chainLength < MAX_POS) { - maxLength += MAX_POS-chainLength; - } - } else if (!lastChainLengthSpecified) { - maxLength = MAX_POS; - } - vector::iterator realEnd = end; - vector newTypes; - if (defaults != NULL && defaults->size() > defaultPos + 2) { // need at least "[name];[part];type" (optional: "[divisor|values][;[unit][;[comment]]]]") - while (defaults->size() > defaultPos + 2 && defaults->at(defaultPos + 2).size() > 0) { - for (size_t i = 0; i < 6; i++) { - if (defaults->size() > defaultPos) { - newTypes.push_back(defaults->at(defaultPos)); - } else { - newTypes.push_back(""); - } - defaultPos++; - } - } - if (newTypes.size() > 0) { - while (it != end) { - newTypes.push_back(*it++); - } - it = newTypes.begin(); - realEnd = newTypes.end(); - } - } - DataField* data = NULL; - if (it == realEnd) { - vector fields; - data = new DataFieldSet("", "", fields); - } else { - result = DataField::create(it, realEnd, templates, data, isWrite, false, isBroadcastOrMasterDestination, (unsigned char)maxLength); - if (result != RESULT_OK) { - return result; - } - } - if (id.size() + data->getLength(pt_masterData, (unsigned char)maxLength) > 2 + maxLength || data->getLength(pt_slaveData, (unsigned char)maxLength) > maxLength) { - // max NN exceeded - delete data; - return RESULT_ERR_INVALID_POS; - } - unsigned int index = 0; - bool multiple = dstAddresses.size() > 1; - char num[10]; - for (vector::iterator it = dstAddresses.begin(); it != dstAddresses.end(); it++, index++) { - unsigned char dstAddress = *it; - string useCircuit = circuit; - if (multiple) { - snprintf(num, sizeof(num), ".%d", index); - useCircuit = useCircuit + num; - } - Message* message; - if (chainIds.size() > 1) { - message = new ChainedMessage(useCircuit, name, isWrite, comment, srcAddress, dstAddress, id, chainIds, chainLengths, data, index == 0, pollPriority, condition); - } else { - message = new Message(useCircuit, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, index == 0, pollPriority, condition); - } - messages.push_back(message); - } - return RESULT_OK; + vector id; + string token = *it++; // [PBSB] + bool useDefaults = token.empty(); + if (useDefaults) { + token = getDefault(token, defaults, defaultPos); + } + defaultPos++; + result = parseId(token, id); + if (result != RESULT_OK) { + return result; + } + if (id.size() != 2) { + return RESULT_ERR_INVALID_ARG; // missing/to short/to long PBSB + } + if (it == end) { + token = ""; + } else { + token = *it++;// [ID] (optional master data) + } + string defaultIdPrefix; + if (useDefaults) { + defaultIdPrefix = getDefault("", defaults, defaultPos); + } + defaultPos++; + vector< vector > chainIds; + vector chainLengths; + istringstream stream(token); + size_t maxLength = MAX_POS; + size_t chainLength = 16; + size_t chainPrefixLength = id.size(); + bool first = true, lastChainLengthSpecified = false; + while (getline(stream, token, VALUE_SEPARATOR) || first) { + FileReader::trim(token); + token = defaultIdPrefix+token; + size_t lengthPos = token.find(LENGTH_SEPARATOR); + lastChainLengthSpecified = lengthPos != string::npos; + if (lastChainLengthSpecified) { + chainLength = parseInt(token.substr(lengthPos+1).c_str(), 10, 0, MAX_POS, result); + if (result != RESULT_OK) { + return result; + } + token.resize(lengthPos); + } + vector chainId = id; + result = parseId(token, chainId); + if (result != RESULT_OK) { + return result; + } + if (!chainIds.empty() && chainId.size() != chainIds.front().size()) { + return RESULT_ERR_INVALID_LIST; + } + chainIds.push_back(chainId); + chainLengths.push_back((unsigned char)chainLength); + if (first) { + chainPrefixLength = chainId.size(); + maxLength = 0; + } else if (chainPrefixLength > 2) { + vector& front = chainIds.front(); + for (size_t pos = 2; pos < chainPrefixLength; pos++) { + if (chainId[pos] != front[pos]) { + chainPrefixLength = pos; + break; + } + } + } + if (maxLength+chainLength > 255) { + return RESULT_ERR_INVALID_POS; + } + maxLength += chainLength; + first = false; + } + id = chainIds.front(); + if (chainIds.size() > 1) { + if (isPassive) { + return RESULT_ERR_INVALID_LIST; + } + if (id.size() > chainPrefixLength) { + id.resize(chainPrefixLength); + } + if (!lastChainLengthSpecified && chainLength < MAX_POS) { + maxLength += MAX_POS-chainLength; + } + } else if (!lastChainLengthSpecified) { + maxLength = MAX_POS; + } + vector::iterator realEnd = end; + vector newTypes; + if (defaults != NULL && defaults->size() > defaultPos + 2) { // need at least "[name];[part];type" (optional: "[divisor|values][;[unit][;[comment]]]]") + while (defaults->size() > defaultPos + 2 && defaults->at(defaultPos + 2).size() > 0) { + for (size_t i = 0; i < 6; i++) { + if (defaults->size() > defaultPos) { + newTypes.push_back(defaults->at(defaultPos)); + } else { + newTypes.push_back(""); + } + defaultPos++; + } + } + if (newTypes.size() > 0) { + while (it != end) { + newTypes.push_back(*it++); + } + it = newTypes.begin(); + realEnd = newTypes.end(); + } + } + DataField* data = NULL; + if (it == realEnd) { + vector fields; + data = new DataFieldSet("", "", fields); + } else { + result = DataField::create(it, realEnd, templates, data, isWrite, false, isBroadcastOrMasterDestination, (unsigned char)maxLength); + if (result != RESULT_OK) { + return result; + } + } + if (id.size() + data->getLength(pt_masterData, (unsigned char)maxLength) > 2 + maxLength || data->getLength(pt_slaveData, (unsigned char)maxLength) > maxLength) { + // max NN exceeded + delete data; + return RESULT_ERR_INVALID_POS; + } + unsigned int index = 0; + bool multiple = dstAddresses.size() > 1; + char num[10]; + for (vector::iterator it = dstAddresses.begin(); it != dstAddresses.end(); it++, index++) { + unsigned char dstAddress = *it; + string useCircuit = circuit; + if (multiple) { + snprintf(num, sizeof(num), ".%d", index); + useCircuit = useCircuit + num; + } + Message* message; + if (chainIds.size() > 1) { + message = new ChainedMessage(useCircuit, name, isWrite, comment, srcAddress, dstAddress, id, chainIds, chainLengths, data, index == 0, pollPriority, condition); + } else { + message = new Message(useCircuit, name, isWrite, isPassive, comment, srcAddress, dstAddress, id, data, index == 0, pollPriority, condition); + } + messages.push_back(message); + } + return RESULT_OK; } Message* Message::createScanMessage() { - return new Message("scan", "", false, false, 0x07, 0x04, DataFieldSet::getIdentFields(), true); + return new Message("scan", "", false, false, 0x07, 0x04, DataFieldSet::getIdentFields(), true); } Message* Message::derive(const unsigned char dstAddress, const unsigned char srcAddress, const string circuit) { - Message* result = new Message(circuit.length() == 0 ? m_circuit : circuit, m_name, - m_isWrite, m_isPassive, m_comment, - srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, - m_id, m_data, false, - m_pollPriority, m_condition); - if (m_isScanMessage) { - result->setScanMessage(); - } - return result; + Message* result = new Message(circuit.length() == 0 ? m_circuit : circuit, m_name, + m_isWrite, m_isPassive, m_comment, + srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, + m_id, m_data, false, + m_pollPriority, m_condition); + if (m_isScanMessage) { + result->setScanMessage(); + } + return result; } Message* Message::derive(const unsigned char dstAddress, const bool extendCircuit) { - if (extendCircuit) { - ostringstream out; - out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast(dstAddress); - return derive(dstAddress, SYN, out.str()); - } - return derive(dstAddress, SYN, m_circuit); + if (extendCircuit) { + ostringstream out; + out << m_circuit << '.' << hex << setw(2) << setfill('0') << static_cast(dstAddress); + return derive(dstAddress, SYN, out.str()); + } + return derive(dstAddress, SYN, m_circuit); } bool Message::checkIdPrefix(vector& id) { - if (id.size() > m_id.size()) { - return false; - } - for (size_t pos = 0; pos < id.size(); pos++) { - if (id[pos] != m_id[pos]) { - return false; - } - } - return true; + if (id.size() > m_id.size()) { + return false; + } + for (size_t pos = 0; pos < id.size(); pos++) { + if (id[pos] != m_id[pos]) { + return false; + } + } + return true; } bool Message::checkId(SymbolString& master, unsigned char* index) { - unsigned char idLen = getIdLength(); - if (master.size() < 5+idLen) { // QQ, ZZ, PB, SB, NN - return false; - } - for (unsigned char pos = 0; pos < idLen; pos++) { - if (m_id[2+pos] != master[5+pos]) { - return false; - } - } - if (index) { - *index = 0; - } - return true; + unsigned char idLen = getIdLength(); + if (master.size() < 5+idLen) { // QQ, ZZ, PB, SB, NN + return false; + } + for (unsigned char pos = 0; pos < idLen; pos++) { + if (m_id[2+pos] != master[5+pos]) { + return false; + } + } + if (index) { + *index = 0; + } + return true; } bool Message::checkId(Message& other) { - unsigned char idLen = getIdLength(); - if (idLen != other.getIdLength() || getCount() > 1) { // only equal for non-chained messages - return false; - } - return other.checkIdPrefix(m_id); + unsigned char idLen = getIdLength(); + if (idLen != other.getIdLength() || getCount() > 1) { // only equal for non-chained messages + return false; + } + return other.checkIdPrefix(m_id); } uint64_t Message::getDerivedKey(const unsigned char dstAddress) { - return (m_key & ~(0xffLL << (8*6))) | (uint64_t)dstAddress << (8*6); + return (m_key & ~(0xffLL << (8*6))) | (uint64_t)dstAddress << (8*6); } bool Message::setPollPriority(unsigned char priority) { - if (priority == m_pollPriority || m_isPassive || isScanMessage() || m_dstAddress == SYN) { - return false; - } - if (m_usedByCondition && (priority == 0 || priority > POLL_PRIORITY_CONDITION)) { - priority = POLL_PRIORITY_CONDITION; - } - bool ret = m_pollPriority == 0 && priority > 0; - m_pollPriority = priority; - return ret; + if (priority == m_pollPriority || m_isPassive || isScanMessage() || m_dstAddress == SYN) { + return false; + } + if (m_usedByCondition && (priority == 0 || priority > POLL_PRIORITY_CONDITION)) { + priority = POLL_PRIORITY_CONDITION; + } + bool ret = m_pollPriority == 0 && priority > 0; + m_pollPriority = priority; + return ret; } void Message::setUsedByCondition() { - if (m_usedByCondition) { - return; - } - m_usedByCondition = true; - if (m_pollPriority == 0 || m_pollPriority > POLL_PRIORITY_CONDITION) { - setPollPriority(POLL_PRIORITY_CONDITION); - } + if (m_usedByCondition) { + return; + } + m_usedByCondition = true; + if (m_pollPriority == 0 || m_pollPriority > POLL_PRIORITY_CONDITION) { + setPollPriority(POLL_PRIORITY_CONDITION); + } } bool Message::isAvailable() { - return (m_condition == NULL) || m_condition->isTrue(); + return (m_condition == NULL) || m_condition->isTrue(); } bool Message::hasField(const char* fieldName, bool numeric) { - return m_data->hasField(fieldName, numeric); + return m_data->hasField(fieldName, numeric); } result_t Message::prepareMaster(const unsigned char srcAddress, SymbolString& masterData, - istringstream& input, char separator, - const unsigned char dstAddress, unsigned char index) { - if (m_isPassive) { - return RESULT_ERR_INVALID_ARG; // prepare not possible - } - SymbolString master(false); - result_t result = master.push_back(srcAddress, false, false); - if (result != RESULT_OK) { - return result; - } - if (dstAddress == SYN) { - if (m_dstAddress == SYN) { - return RESULT_ERR_INVALID_ADDR; - } - result = master.push_back(m_dstAddress, false, false); - } else { - result = master.push_back(dstAddress, false, false); - } - if (result != RESULT_OK) { - return result; - } - result = master.push_back(m_id[0], false, false); - if (result != RESULT_OK) { - return result; - } - result = master.push_back(m_id[1], false, false); - if (result != RESULT_OK) { - return result; - } - result = prepareMasterPart(master, input, separator, index); - if (result != RESULT_OK) { - return result; - } - masterData.clear(); - masterData.addAll(master); - result = storeLastData(pt_masterData, masterData, index); - if (result < RESULT_OK) { - return result; - } - return RESULT_OK; + istringstream& input, char separator, + const unsigned char dstAddress, unsigned char index) { + if (m_isPassive) { + return RESULT_ERR_INVALID_ARG; // prepare not possible + } + SymbolString master(false); + result_t result = master.push_back(srcAddress, false, false); + if (result != RESULT_OK) { + return result; + } + if (dstAddress == SYN) { + if (m_dstAddress == SYN) { + return RESULT_ERR_INVALID_ADDR; + } + result = master.push_back(m_dstAddress, false, false); + } else { + result = master.push_back(dstAddress, false, false); + } + if (result != RESULT_OK) { + return result; + } + result = master.push_back(m_id[0], false, false); + if (result != RESULT_OK) { + return result; + } + result = master.push_back(m_id[1], false, false); + if (result != RESULT_OK) { + return result; + } + result = prepareMasterPart(master, input, separator, index); + if (result != RESULT_OK) { + return result; + } + masterData.clear(); + masterData.addAll(master); + result = storeLastData(pt_masterData, masterData, index); + if (result < RESULT_OK) { + return result; + } + return RESULT_OK; } result_t Message::prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index) { - if (index != 0) { - return RESULT_ERR_NOTFOUND; - } - unsigned char pos = master.size(); - result_t result = master.push_back(0, false, false); // length, will be set later - if (result != RESULT_OK) { - return result; - } - for (size_t i = 2; i < m_id.size(); i++) { - result = master.push_back(m_id[i], false, false); - if (result != RESULT_OK) { - return result; - } - } - result = m_data->write(input, pt_masterData, master, getIdLength(), separator); - if (result != RESULT_OK) { - return result; - } - master[pos] = (unsigned char)(master.size()-pos-1); - return result; + if (index != 0) { + return RESULT_ERR_NOTFOUND; + } + unsigned char pos = master.size(); + result_t result = master.push_back(0, false, false); // length, will be set later + if (result != RESULT_OK) { + return result; + } + for (size_t i = 2; i < m_id.size(); i++) { + result = master.push_back(m_id[i], false, false); + if (result != RESULT_OK) { + return result; + } + } + result = m_data->write(input, pt_masterData, master, getIdLength(), separator); + if (result != RESULT_OK) { + return result; + } + master[pos] = (unsigned char)(master.size()-pos-1); + return result; } result_t Message::prepareSlave(istringstream& input, SymbolString& slaveData) { - if (m_isWrite) { - return RESULT_ERR_INVALID_ARG; // prepare not possible - } - SymbolString slave(false); - result_t result = slave.push_back(0, false, false); // length, will be set later - if (result != RESULT_OK) { - return result; - } - result = m_data->write(input, pt_slaveData, slave, 0); - if (result != RESULT_OK) { - return result; - } - slave[0] = (unsigned char)(slave.size()-1); - time(&m_lastUpdateTime); - if (slave != m_lastSlaveData) { - m_lastChangeTime = m_lastUpdateTime; - m_lastSlaveData = slave; - } - slaveData.clear(); - slaveData.addAll(slave); - return result; + if (m_isWrite) { + return RESULT_ERR_INVALID_ARG; // prepare not possible + } + SymbolString slave(false); + result_t result = slave.push_back(0, false, false); // length, will be set later + if (result != RESULT_OK) { + return result; + } + result = m_data->write(input, pt_slaveData, slave, 0); + if (result != RESULT_OK) { + return result; + } + slave[0] = (unsigned char)(slave.size()-1); + time(&m_lastUpdateTime); + if (slave != m_lastSlaveData) { + m_lastChangeTime = m_lastUpdateTime; + m_lastSlaveData = slave; + } + slaveData.clear(); + slaveData.addAll(slave); + return result; } result_t Message::storeLastData(SymbolString& master, SymbolString& slave) { - result_t result = storeLastData(pt_masterData, master, 0); - if (result >= RESULT_OK) { - result = storeLastData(pt_slaveData, slave, 0); - } - return result; + result_t result = storeLastData(pt_masterData, master, 0); + if (result >= RESULT_OK) { + result = storeLastData(pt_slaveData, slave, 0); + } + return result; } result_t Message::storeLastData(const PartType partType, SymbolString& data, unsigned char index) { - if (data.size() > 0 - && (m_isWrite || this->m_dstAddress == BROADCAST || partType == pt_slaveData)) { - time(&m_lastUpdateTime); - } - if (partType == pt_masterData) { - switch (data.compareMaster(m_lastMasterData)) { - case 1: // completely different - m_lastChangeTime = m_lastUpdateTime; - m_lastMasterData = data; - break; - case 2: // only master address is different - m_lastMasterData = data; - break; - } - } else if (partType == pt_slaveData) { - if (data != m_lastSlaveData) { - m_lastChangeTime = m_lastUpdateTime; - m_lastSlaveData = data; - } - } - return RESULT_OK; + if (data.size() > 0 + && (m_isWrite || this->m_dstAddress == BROADCAST || partType == pt_slaveData)) { + time(&m_lastUpdateTime); + } + if (partType == pt_masterData) { + switch (data.compareMaster(m_lastMasterData)) { + case 1: // completely different + m_lastChangeTime = m_lastUpdateTime; + m_lastMasterData = data; + break; + case 2: // only master address is different + m_lastMasterData = data; + break; + } + } else if (partType == pt_slaveData) { + if (data != m_lastSlaveData) { + m_lastChangeTime = m_lastUpdateTime; + m_lastSlaveData = data; + } + } + return RESULT_OK; } result_t Message::decodeLastData(const PartType partType, - ostringstream& output, OutputFormat outputFormat, - bool leadingSeparator, const char* fieldName, signed char fieldIndex) { - unsigned char offset; - if (partType == pt_masterData) { - offset = (unsigned char)(m_id.size() - 2); - } else { - offset = 0; - } - result_t result = m_data->read(partType, partType == pt_masterData ? m_lastMasterData : m_lastSlaveData, offset, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - if (result == RESULT_EMPTY && fieldName != NULL) { - return RESULT_ERR_NOTFOUND; - } - return result; + ostringstream& output, OutputFormat outputFormat, + bool leadingSeparator, const char* fieldName, signed char fieldIndex) { + unsigned char offset; + if (partType == pt_masterData) { + offset = (unsigned char)(m_id.size() - 2); + } else { + offset = 0; + } + result_t result = m_data->read(partType, partType == pt_masterData ? m_lastMasterData : m_lastSlaveData, offset, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + if (result == RESULT_EMPTY && fieldName != NULL) { + return RESULT_ERR_NOTFOUND; + } + return result; } result_t Message::decodeLastData(ostringstream& output, OutputFormat outputFormat, - bool leadingSeparator, const char* fieldName, signed char fieldIndex) { - size_t startPos = output.str().length(); - result_t result = m_data->read(pt_masterData, m_lastMasterData, getIdLength(), output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - bool empty = result == RESULT_EMPTY; - leadingSeparator |= output.str().length() > startPos; - result = m_data->read(pt_slaveData, m_lastSlaveData, 0, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - if (result == RESULT_EMPTY && !empty) { - result = RESULT_OK; // OK if at least one part was non-empty - } else if (result == RESULT_EMPTY && fieldName != NULL) { - return RESULT_ERR_NOTFOUND; - } - return result; + bool leadingSeparator, const char* fieldName, signed char fieldIndex) { + size_t startPos = output.str().length(); + result_t result = m_data->read(pt_masterData, m_lastMasterData, getIdLength(), output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + bool empty = result == RESULT_EMPTY; + leadingSeparator |= output.str().length() > startPos; + result = m_data->read(pt_slaveData, m_lastSlaveData, 0, output, outputFormat, -1, leadingSeparator, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + if (result == RESULT_EMPTY && !empty) { + result = RESULT_OK; // OK if at least one part was non-empty + } else if (result == RESULT_EMPTY && fieldName != NULL) { + return RESULT_ERR_NOTFOUND; + } + return result; } result_t Message::decodeLastDataNumField(unsigned int& output, const char* fieldName, signed char fieldIndex) { - result_t result = m_data->read(pt_masterData, m_lastMasterData, getIdLength(), output, fieldName, fieldIndex); - if (result < RESULT_OK) { - return result; - } - if (result == RESULT_EMPTY) { - result = m_data->read(pt_slaveData, m_lastSlaveData, 0, output, fieldName, fieldIndex); - } - if (result < RESULT_OK) { - return result; - } - if (result == RESULT_EMPTY) { - return RESULT_ERR_NOTFOUND; - } - return result; + result_t result = m_data->read(pt_masterData, m_lastMasterData, getIdLength(), output, fieldName, fieldIndex); + if (result < RESULT_OK) { + return result; + } + if (result == RESULT_EMPTY) { + result = m_data->read(pt_slaveData, m_lastSlaveData, 0, output, fieldName, fieldIndex); + } + if (result < RESULT_OK) { + return result; + } + if (result == RESULT_EMPTY) { + return RESULT_ERR_NOTFOUND; + } + return result; } bool Message::isLessPollWeight(const Message* other) { - unsigned char tprio = m_pollPriority; - unsigned char oprio = other->m_pollPriority; - unsigned int tw = tprio * m_pollCount; - unsigned int ow = oprio * other->m_pollCount; - if (tw > ow) { - return true; - } - if (tw < ow) { - return false; - } - if (tprio > oprio) { - return true; - } - if (tprio < oprio) { - return false; - } - if (m_lastPollTime > other->m_lastPollTime) { - return true; - } - return false; + unsigned char tprio = m_pollPriority; + unsigned char oprio = other->m_pollPriority; + unsigned int tw = tprio * m_pollCount; + unsigned int ow = oprio * other->m_pollCount; + if (tw > ow) { + return true; + } + if (tw < ow) { + return false; + } + if (tprio > oprio) { + return true; + } + if (tprio < oprio) { + return false; + } + if (m_lastPollTime > other->m_lastPollTime) { + return true; + } + return false; } void Message::dump(ostream& output, vector* columns, bool withConditions) { - bool first = true, all = columns == NULL; - size_t end = all ? 9 : columns->size(); - for (size_t i = 0; i < end; i++) { - if (first) { - first = false; - } else { - output << FIELD_SEPARATOR; - } - size_t column = all ? i : (*columns)[i]; - dumpColumn(output, column, withConditions); - } + bool first = true, all = columns == NULL; + size_t end = all ? 9 : columns->size(); + for (size_t i = 0; i < end; i++) { + if (first) { + first = false; + } else { + output << FIELD_SEPARATOR; + } + size_t column = all ? i : (*columns)[i]; + dumpColumn(output, column, withConditions); + } } void Message::dumpColumn(ostream& output, size_t column, bool withConditions) { - switch (column) { - case COLUMN_TYPE: // type - if (withConditions && m_condition != NULL) { - m_condition->dump(output); - } - if (m_isPassive) { - output << "u"; - if (m_isWrite) { - output << "w"; - } - } else if (m_isWrite) { - output << "w"; - } else { - output << "r"; - if (m_pollPriority > 0) { - output << static_cast(m_pollPriority); - } - } - break; - case COLUMN_CIRCUIT: // circuit - DataField::dumpString(output, m_circuit, false); - break; - case COLUMN_NAME: // name - DataField::dumpString(output, m_name, false); - break; - case COLUMN_COMMENT: // comment - DataField::dumpString(output, m_comment, false); - break; - case COLUMN_QQ: // QQ - if (m_srcAddress != SYN) { - output << hex << setw(2) << setfill('0') << static_cast(m_srcAddress); - } - break; - case COLUMN_ZZ: // ZZ - if (m_dstAddress != SYN) { - output << hex << setw(2) << setfill('0') << static_cast(m_dstAddress); - } - break; - case COLUMN_PBSB: // PBSB - for (vector::const_iterator it = m_id.begin(); it < m_id.begin()+2 && it < m_id.end(); it++) { - output << hex << setw(2) << setfill('0') << static_cast(*it); - } - break; - case COLUMN_ID: // ID - for (vector::const_iterator it = m_id.begin()+2; it < m_id.end(); it++) { - output << hex << setw(2) << setfill('0') << static_cast(*it); - } - break; - case COLUMN_FIELDS: // fields - m_data->dump(output); - break; - } + switch (column) { + case COLUMN_TYPE: // type + if (withConditions && m_condition != NULL) { + m_condition->dump(output); + } + if (m_isPassive) { + output << "u"; + if (m_isWrite) { + output << "w"; + } + } else if (m_isWrite) { + output << "w"; + } else { + output << "r"; + if (m_pollPriority > 0) { + output << static_cast(m_pollPriority); + } + } + break; + case COLUMN_CIRCUIT: // circuit + DataField::dumpString(output, m_circuit, false); + break; + case COLUMN_NAME: // name + DataField::dumpString(output, m_name, false); + break; + case COLUMN_COMMENT: // comment + DataField::dumpString(output, m_comment, false); + break; + case COLUMN_QQ: // QQ + if (m_srcAddress != SYN) { + output << hex << setw(2) << setfill('0') << static_cast(m_srcAddress); + } + break; + case COLUMN_ZZ: // ZZ + if (m_dstAddress != SYN) { + output << hex << setw(2) << setfill('0') << static_cast(m_dstAddress); + } + break; + case COLUMN_PBSB: // PBSB + for (vector::const_iterator it = m_id.begin(); it < m_id.begin()+2 && it < m_id.end(); it++) { + output << hex << setw(2) << setfill('0') << static_cast(*it); + } + break; + case COLUMN_ID: // ID + for (vector::const_iterator it = m_id.begin()+2; it < m_id.end(); it++) { + output << hex << setw(2) << setfill('0') << static_cast(*it); + } + break; + case COLUMN_FIELDS: // fields + m_data->dump(output); + break; + } } ChainedMessage::ChainedMessage(const string circuit, const string name, - const bool isWrite, const string comment, - const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, - vector< vector > ids, vector lengths, - DataField* data, const bool deleteData, - const unsigned char pollPriority, - Condition* condition) - : Message(circuit, name, isWrite, false, comment, - srcAddress, dstAddress, id, - data, deleteData, pollPriority, condition), - m_ids(ids), m_lengths(lengths), - m_maxTimeDiff(m_ids.size()*15) { // 15 seconds per message - size_t cnt = ids.size(); - m_lastMasterDatas = reinterpret_cast(calloc(cnt, sizeof(SymbolString*))); - m_lastSlaveDatas = reinterpret_cast(calloc(cnt, sizeof(SymbolString*))); - m_lastMasterUpdateTimes = reinterpret_cast(calloc(cnt, sizeof(time_t))); - m_lastSlaveUpdateTimes = reinterpret_cast(calloc(cnt, sizeof(time_t))); - for (size_t index = 0; index < cnt; index++) { - m_lastMasterDatas[index] = new SymbolString(); - m_lastSlaveDatas[index] = new SymbolString(); - } + const bool isWrite, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, + vector< vector > ids, vector lengths, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition) + : Message(circuit, name, isWrite, false, comment, + srcAddress, dstAddress, id, + data, deleteData, pollPriority, condition), + m_ids(ids), m_lengths(lengths), + m_maxTimeDiff(m_ids.size()*15) { // 15 seconds per message + size_t cnt = ids.size(); + m_lastMasterDatas = reinterpret_cast(calloc(cnt, sizeof(SymbolString*))); + m_lastSlaveDatas = reinterpret_cast(calloc(cnt, sizeof(SymbolString*))); + m_lastMasterUpdateTimes = reinterpret_cast(calloc(cnt, sizeof(time_t))); + m_lastSlaveUpdateTimes = reinterpret_cast(calloc(cnt, sizeof(time_t))); + for (size_t index = 0; index < cnt; index++) { + m_lastMasterDatas[index] = new SymbolString(); + m_lastSlaveDatas[index] = new SymbolString(); + } } ChainedMessage::~ChainedMessage() { - for (unsigned char index = 0; index < m_ids.size(); index++) { - delete m_lastMasterDatas[index]; - m_lastMasterDatas[index] = NULL; - delete m_lastSlaveDatas[index]; - m_lastSlaveDatas[index] = NULL; - } - free(m_lastMasterDatas); - free(m_lastSlaveDatas); - free(m_lastMasterUpdateTimes); - free(m_lastSlaveUpdateTimes); + for (unsigned char index = 0; index < m_ids.size(); index++) { + delete m_lastMasterDatas[index]; + m_lastMasterDatas[index] = NULL; + delete m_lastSlaveDatas[index]; + m_lastSlaveDatas[index] = NULL; + } + free(m_lastMasterDatas); + free(m_lastSlaveDatas); + free(m_lastMasterUpdateTimes); + free(m_lastSlaveUpdateTimes); } Message* ChainedMessage::derive(const unsigned char dstAddress, const unsigned char srcAddress, const string circuit) { - ChainedMessage* result = new ChainedMessage(circuit.length() == 0 ? m_circuit : circuit, m_name, - m_isWrite, m_comment, - srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, - m_id, m_ids, m_lengths, m_data, false, - m_pollPriority, m_condition); - if (m_isScanMessage) { - result->setScanMessage(); - } - return result; + ChainedMessage* result = new ChainedMessage(circuit.length() == 0 ? m_circuit : circuit, m_name, + m_isWrite, m_comment, + srcAddress == SYN ? m_srcAddress : srcAddress, dstAddress, + m_id, m_ids, m_lengths, m_data, false, + m_pollPriority, m_condition); + if (m_isScanMessage) { + result->setScanMessage(); + } + return result; } bool ChainedMessage::checkId(SymbolString& master, unsigned char* index) { - unsigned char idLen = getIdLength(); - if (master.size() < 5+idLen) { // QQ, ZZ, PB, SB, NN - return false; - } - unsigned char chainPrefixLength = Message::getIdLength(); - for (unsigned char pos = 0; pos < chainPrefixLength; pos++) { - if (m_id[2+pos] != master[5+pos]) { - return false; // chain prefix mismatch - } - } - for (unsigned char checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part - vector id = m_ids[checkIndex]; - bool found = false; - for (unsigned char pos = chainPrefixLength; pos < idLen; pos++) { - if (id[2+pos] != master[5+pos]) { - found = false; - break; - } - found = true; - } - if (found) { - if (index) { - *index = checkIndex; - } - return true; - } - } - return false; + unsigned char idLen = getIdLength(); + if (master.size() < 5+idLen) { // QQ, ZZ, PB, SB, NN + return false; + } + unsigned char chainPrefixLength = Message::getIdLength(); + for (unsigned char pos = 0; pos < chainPrefixLength; pos++) { + if (m_id[2+pos] != master[5+pos]) { + return false; // chain prefix mismatch + } + } + for (unsigned char checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part + vector id = m_ids[checkIndex]; + bool found = false; + for (unsigned char pos = chainPrefixLength; pos < idLen; pos++) { + if (id[2+pos] != master[5+pos]) { + found = false; + break; + } + found = true; + } + if (found) { + if (index) { + *index = checkIndex; + } + return true; + } + } + return false; } bool ChainedMessage::checkId(Message& other) { - unsigned char idLen = getIdLength(); - if (idLen != other.getIdLength() || other.getCount() == 1) { // only equal for chained messages - return false; - } - if (!other.checkIdPrefix(m_id)) { - return false; // chain prefix mismatch - } - vector< vector > otherIds = ((ChainedMessage&)other).m_ids; - unsigned char chainPrefixLength = Message::getIdLength(); - for (unsigned char checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part - vector id = m_ids[checkIndex]; - for (unsigned char otherIndex = 0; otherIndex < otherIds.size(); otherIndex++) { - vector otherId = otherIds[otherIndex]; - bool found = false; - for (unsigned char pos = chainPrefixLength; pos < idLen; pos++) { - if (id[2+pos] != otherId[2+pos]) { - found = false; - break; - } - found = true; - } - if (found) { - return true; - } - } - } - return false; + unsigned char idLen = getIdLength(); + if (idLen != other.getIdLength() || other.getCount() == 1) { // only equal for chained messages + return false; + } + if (!other.checkIdPrefix(m_id)) { + return false; // chain prefix mismatch + } + vector< vector > otherIds = ((ChainedMessage&)other).m_ids; + unsigned char chainPrefixLength = Message::getIdLength(); + for (unsigned char checkIndex = 0; checkIndex < m_ids.size(); checkIndex++) { // check suffix for each part + vector id = m_ids[checkIndex]; + for (unsigned char otherIndex = 0; otherIndex < otherIds.size(); otherIndex++) { + vector otherId = otherIds[otherIndex]; + bool found = false; + for (unsigned char pos = chainPrefixLength; pos < idLen; pos++) { + if (id[2+pos] != otherId[2+pos]) { + found = false; + break; + } + found = true; + } + if (found) { + return true; + } + } + } + return false; } result_t ChainedMessage::prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index) { - size_t cnt = getCount(); - if (index >= cnt) { - return RESULT_ERR_NOTFOUND; - } - SymbolString allData(false); - result_t result = m_data->write(input, pt_masterData, allData, 0, separator); - if (result != RESULT_OK) { - return result; - } - size_t pos = 0, addData = 0; - if (m_isWrite) { - addData = m_lengths[0]; - for (size_t i = 0; i < index; i++) { - pos += addData; - addData = m_lengths[i+1]; - } - } - if (pos+addData > allData.size()) { - return RESULT_ERR_INVALID_POS; - } - vector id = m_ids[index]; - result = master.push_back((unsigned char)(id.size()-2+addData), false, false); // NN - if (result != RESULT_OK) { - return result; - } - for (size_t i = 2; i < id.size(); i++) { - result = master.push_back(id[i], false, false); - if (result != RESULT_OK) { - return result; - } - } - for (size_t i = 0; i < addData; i++) { - result = master.push_back(allData[pos+i], false, false); - if (result != RESULT_OK) { - return result; - } - } - if (index == 0) { - for (size_t i = 0; i < cnt; i++) { - m_lastMasterUpdateTimes[index] = m_lastSlaveUpdateTimes[index] = 0; - } - } - return result; + size_t cnt = getCount(); + if (index >= cnt) { + return RESULT_ERR_NOTFOUND; + } + SymbolString allData(false); + result_t result = m_data->write(input, pt_masterData, allData, 0, separator); + if (result != RESULT_OK) { + return result; + } + size_t pos = 0, addData = 0; + if (m_isWrite) { + addData = m_lengths[0]; + for (size_t i = 0; i < index; i++) { + pos += addData; + addData = m_lengths[i+1]; + } + } + if (pos+addData > allData.size()) { + return RESULT_ERR_INVALID_POS; + } + vector id = m_ids[index]; + result = master.push_back((unsigned char)(id.size()-2+addData), false, false); // NN + if (result != RESULT_OK) { + return result; + } + for (size_t i = 2; i < id.size(); i++) { + result = master.push_back(id[i], false, false); + if (result != RESULT_OK) { + return result; + } + } + for (size_t i = 0; i < addData; i++) { + result = master.push_back(allData[pos+i], false, false); + if (result != RESULT_OK) { + return result; + } + } + if (index == 0) { + for (size_t i = 0; i < cnt; i++) { + m_lastMasterUpdateTimes[index] = m_lastSlaveUpdateTimes[index] = 0; + } + } + return result; } result_t ChainedMessage::storeLastData(SymbolString& master, SymbolString& slave) { - // determine index from master ID - unsigned char index = 0; - if (checkId(master, &index)) { - result_t result = storeLastData(pt_masterData, master, index); - if (result >= RESULT_OK) { - result = storeLastData(pt_slaveData, slave, index); - } - return result; - } - return RESULT_ERR_INVALID_ARG; + // determine index from master ID + unsigned char index = 0; + if (checkId(master, &index)) { + result_t result = storeLastData(pt_masterData, master, index); + if (result >= RESULT_OK) { + result = storeLastData(pt_slaveData, slave, index); + } + return result; + } + return RESULT_ERR_INVALID_ARG; } result_t ChainedMessage::storeLastData(const PartType partType, SymbolString& data, unsigned char index) { - if (index >= m_ids.size()) { - return RESULT_ERR_INVALID_ARG; - } - if (partType == pt_masterData) { - switch (data.compareMaster(*m_lastMasterDatas[index])) { - case 1: // completely different - *m_lastMasterDatas[index] = data; - break; - case 2: // only master address is different - *m_lastMasterDatas[index] = data; - break; - } - time(&m_lastMasterUpdateTimes[index]); - } else if (partType == pt_slaveData) { - if (data != *m_lastSlaveDatas[index]) { - *m_lastSlaveDatas[index] = data; - } - time(&m_lastSlaveUpdateTimes[index]); - } - // check arrival time of all parts - time_t minTime = 0, maxTime = 0; - for (index = 0; index < m_ids.size(); index++) { - if (index == 0) { - minTime = maxTime = m_lastMasterUpdateTimes[index]; - } else { - if (m_lastMasterUpdateTimes[index] < minTime) { - minTime = m_lastMasterUpdateTimes[index]; - } - if (m_lastMasterUpdateTimes[index] > maxTime) { - maxTime = m_lastMasterUpdateTimes[index]; - } - } - if (m_lastSlaveUpdateTimes[index] < minTime) { - minTime = m_lastSlaveUpdateTimes[index]; - } - if (m_lastSlaveUpdateTimes[index] > maxTime) { - maxTime = m_lastSlaveUpdateTimes[index]; - } - if (minTime == 0 || maxTime == 0 || maxTime-minTime > m_maxTimeDiff) { - return RESULT_CONTINUE; - } - } - // everything was completely retrieved in short time - SymbolString master(false); - SymbolString slave(false); - size_t offset = 5+(m_ids[0].size()-2); // skip QQ, ZZ, PB, SB, NN - for (index = 0; index < m_ids.size(); index++) { - SymbolString* add = m_lastMasterDatas[index]; - size_t end = 5+(*add)[4]; - for (size_t pos = index == 0 ? 0 : offset; pos < end; pos++) { - master.push_back((*add)[pos], false, false); - } - add = m_lastSlaveDatas[index]; - end = 1+(*add)[0]; - for (size_t pos = index == 0 ? 0 : 1; pos < end; pos++) { - slave.push_back((*add)[pos], false, false); - } - } - // adjust NN - if (master.size()-5 > 255 || slave.size()-1 > 255) { - return RESULT_ERR_INVALID_POS; - } - master[4] = (unsigned char)(master.size()-5); - slave[0] = (unsigned char)(slave.size()-1); - result_t result = Message::storeLastData(pt_masterData, master, 0); - if (result == RESULT_OK) { - result = Message::storeLastData(pt_slaveData, slave, 0); - } - return result; + if (index >= m_ids.size()) { + return RESULT_ERR_INVALID_ARG; + } + if (partType == pt_masterData) { + switch (data.compareMaster(*m_lastMasterDatas[index])) { + case 1: // completely different + *m_lastMasterDatas[index] = data; + break; + case 2: // only master address is different + *m_lastMasterDatas[index] = data; + break; + } + time(&m_lastMasterUpdateTimes[index]); + } else if (partType == pt_slaveData) { + if (data != *m_lastSlaveDatas[index]) { + *m_lastSlaveDatas[index] = data; + } + time(&m_lastSlaveUpdateTimes[index]); + } + // check arrival time of all parts + time_t minTime = 0, maxTime = 0; + for (index = 0; index < m_ids.size(); index++) { + if (index == 0) { + minTime = maxTime = m_lastMasterUpdateTimes[index]; + } else { + if (m_lastMasterUpdateTimes[index] < minTime) { + minTime = m_lastMasterUpdateTimes[index]; + } + if (m_lastMasterUpdateTimes[index] > maxTime) { + maxTime = m_lastMasterUpdateTimes[index]; + } + } + if (m_lastSlaveUpdateTimes[index] < minTime) { + minTime = m_lastSlaveUpdateTimes[index]; + } + if (m_lastSlaveUpdateTimes[index] > maxTime) { + maxTime = m_lastSlaveUpdateTimes[index]; + } + if (minTime == 0 || maxTime == 0 || maxTime-minTime > m_maxTimeDiff) { + return RESULT_CONTINUE; + } + } + // everything was completely retrieved in short time + SymbolString master(false); + SymbolString slave(false); + size_t offset = 5+(m_ids[0].size()-2); // skip QQ, ZZ, PB, SB, NN + for (index = 0; index < m_ids.size(); index++) { + SymbolString* add = m_lastMasterDatas[index]; + size_t end = 5+(*add)[4]; + for (size_t pos = index == 0 ? 0 : offset; pos < end; pos++) { + master.push_back((*add)[pos], false, false); + } + add = m_lastSlaveDatas[index]; + end = 1+(*add)[0]; + for (size_t pos = index == 0 ? 0 : 1; pos < end; pos++) { + slave.push_back((*add)[pos], false, false); + } + } + // adjust NN + if (master.size()-5 > 255 || slave.size()-1 > 255) { + return RESULT_ERR_INVALID_POS; + } + master[4] = (unsigned char)(master.size()-5); + slave[0] = (unsigned char)(slave.size()-1); + result_t result = Message::storeLastData(pt_masterData, master, 0); + if (result == RESULT_OK) { + result = Message::storeLastData(pt_slaveData, slave, 0); + } + return result; } void ChainedMessage::dumpColumn(ostream& output, size_t column, bool withConditions) { - if (column != COLUMN_ID) { - Message::dumpColumn(output, column, withConditions); - return; - } - bool first = true; - for (size_t index = 0; index < m_ids.size(); index++) { - vector id = m_ids[index]; - for (vector::const_iterator it = id.begin()+2; it < id.end(); it++) { - if (first) { - first = false; - } else { - output << VALUE_SEPARATOR; - } - output << hex << setw(2) << setfill('0') << static_cast(*it); - } - output << LENGTH_SEPARATOR << dec << setw(0) << static_cast(m_lengths[index]); - } + if (column != COLUMN_ID) { + Message::dumpColumn(output, column, withConditions); + return; + } + bool first = true; + for (size_t index = 0; index < m_ids.size(); index++) { + vector id = m_ids[index]; + for (vector::const_iterator it = id.begin()+2; it < id.end(); it++) { + if (first) { + first = false; + } else { + output << VALUE_SEPARATOR; + } + output << hex << setw(2) << setfill('0') << static_cast(*it); + } + output << LENGTH_SEPARATOR << dec << setw(0) << static_cast(m_lengths[index]); + } } @@ -1096,16 +1096,16 @@ void ChainedMessage::dumpColumn(ostream& output, size_t column, bool withConditi * @param sameIdExtAs the optional @a Message to check for having the same ID. */ Message* getFirstAvailable(vector &messages, SymbolString* sameIdExtAs) { - for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { - Message* message = *msgIt; - if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { - continue; - } - if (message->isAvailable()) { - return message; - } - } - return NULL; + for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { + Message* message = *msgIt; + if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { + continue; + } + if (message->isAvailable()) { + return message; + } + } + return NULL; } /** @@ -1114,16 +1114,16 @@ Message* getFirstAvailable(vector &messages, SymbolString* sameIdExtAs * @param sameIdExtAs the optional @a Message to check for having the same ID. */ Message* getFirstAvailable(vector &messages, Message* sameIdExtAs = NULL) { - for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { - Message* message = *msgIt; - if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { - continue; - } - if (message->isAvailable()) { - return message; - } - } - return NULL; + for (vector::iterator msgIt = messages.begin(); msgIt != messages.end(); msgIt++) { + Message* message = *msgIt; + if (sameIdExtAs && !message->checkId(*sameIdExtAs)) { + continue; + } + if (message->isAvailable()) { + return message; + } + } + return NULL; } /** @@ -1132,15 +1132,15 @@ Message* getFirstAvailable(vector &messages, Message* sameIdExtAs = NU * @param values the output value list to append to. */ result_t splitValues(string valueList, vector& values) { - istringstream stream(valueList); - string str; - while (getline(stream, str, VALUE_SEPARATOR)) { - if (str.length() > 0 && str[0] == '\'' && str[str.length()-1] == '\'') { - str = str.substr(1, str.length()-2); - } - values.push_back(str); - } - return RESULT_OK; + istringstream stream(valueList); + string str; + while (getline(stream, str, VALUE_SEPARATOR)) { + if (str.length() > 0 && str[0] == '\'' && str[str.length()-1] == '\'') { + str = str.substr(1, str.length()-2); + } + values.push_back(str); + } + return RESULT_OK; } /** @@ -1149,1059 +1149,1059 @@ result_t splitValues(string valueList, vector& values) { * @param valueRanges the output list of value ranges to append to (pairs of inclusive from-to values). */ result_t splitValues(string valueList, vector& valueRanges) { - istringstream stream(valueList); - string str; - result_t result; - while (getline(stream, str, VALUE_SEPARATOR)) { - FileReader::trim(str); - if (str.length() == 0) { - return RESULT_ERR_INVALID_ARG; - } - bool upto = str[0] == '<'; - if (upto || str[0] == '>') { - if (str.length() == 1) { - return RESULT_ERR_INVALID_ARG; - } - if (upto) { - valueRanges.push_back(0); - } - bool inclusive = str[1] == '='; - unsigned int val = parseInt(str.substr(inclusive?2:1).c_str(), 10, inclusive?0:1, inclusive?UINT_MAX:(UINT_MAX-1), result); - if (result != RESULT_OK) { - return result; - } - valueRanges.push_back(inclusive ? val : (val+(upto?-1:1))); - if (!upto) { - valueRanges.push_back(UINT_MAX); - } - } else { - size_t pos = str.find('-'); - if (pos != string::npos && pos > 0) { // range - unsigned int val = parseInt(str.substr(0, pos).c_str(), 10, 0, UINT_MAX, result); - if (result != RESULT_OK) { - return result; - } - valueRanges.push_back(val); - pos++; - } else { // single value - pos = 0; - } - unsigned int val = parseInt(str.substr(pos).c_str(), 10, 0, UINT_MAX, result); - if (result != RESULT_OK) { - return result; - } - valueRanges.push_back(val); - if (pos == 0) { - valueRanges.push_back(val); // single value - } - } - } - return RESULT_OK; + istringstream stream(valueList); + string str; + result_t result; + while (getline(stream, str, VALUE_SEPARATOR)) { + FileReader::trim(str); + if (str.length() == 0) { + return RESULT_ERR_INVALID_ARG; + } + bool upto = str[0] == '<'; + if (upto || str[0] == '>') { + if (str.length() == 1) { + return RESULT_ERR_INVALID_ARG; + } + if (upto) { + valueRanges.push_back(0); + } + bool inclusive = str[1] == '='; + unsigned int val = parseInt(str.substr(inclusive?2:1).c_str(), 10, inclusive?0:1, inclusive?UINT_MAX:(UINT_MAX-1), result); + if (result != RESULT_OK) { + return result; + } + valueRanges.push_back(inclusive ? val : (val+(upto?-1:1))); + if (!upto) { + valueRanges.push_back(UINT_MAX); + } + } else { + size_t pos = str.find('-'); + if (pos != string::npos && pos > 0) { // range + unsigned int val = parseInt(str.substr(0, pos).c_str(), 10, 0, UINT_MAX, result); + if (result != RESULT_OK) { + return result; + } + valueRanges.push_back(val); + pos++; + } else { // single value + pos = 0; + } + unsigned int val = parseInt(str.substr(pos).c_str(), 10, 0, UINT_MAX, result); + if (result != RESULT_OK) { + return result; + } + valueRanges.push_back(val); + if (pos == 0) { + valueRanges.push_back(val); // single value + } + } + } + return RESULT_OK; } result_t Condition::create(const string condName, vector::iterator& it, const vector::iterator end, string defaultDest, string defaultCircuit, SimpleCondition*& returnValue) { - // name,circuit,messagename,[comment],[fieldname],[ZZ],values (name already skipped by caller) - string circuit = it == end ? "" : *(it++); // circuit - string name = it == end ? "" : *(it++); // messagename - if (it < end) { - it++; // comment - } - string field = it == end ? "" : *(it++); // fieldname - string zz = it == end ? "" : *(it++); // ZZ - unsigned char dstAddress = SYN; - result_t result = RESULT_OK; - if (zz.length() == 0) { - zz = defaultDest; - } - if (zz.length() > 0) { - dstAddress = (unsigned char)parseInt(zz.c_str(), 16, 0, 0xff, result); - if (result != RESULT_OK) { - return result; - } - if (dstAddress != SYN && !isValidAddress(dstAddress, false)) { - return RESULT_ERR_INVALID_ADDR; - } - } - if (name.length() == 0) { - if (!isValidAddress(dstAddress, false) || isMaster(dstAddress)) { - return RESULT_ERR_INVALID_ADDR; - } - } else if (circuit.length() == 0) { - circuit = defaultCircuit; - } - string valueList = it == end ? "" : *(it++); - if (valueList.length() == 0) { - returnValue = new SimpleCondition(condName, condName, circuit, name, dstAddress, field); - return RESULT_OK; - } - if (valueList[0] == '\'') { - // strings - vector values; - result = splitValues(valueList, values); - if (result != RESULT_OK) { - return result; - } - returnValue = new SimpleStringCondition(condName, condName, circuit, name, dstAddress, field, values); - return RESULT_OK; - } - // numbers - vector valueRanges; - result = splitValues(valueList, valueRanges); - if (result != RESULT_OK) { - return result; - } - returnValue = new SimpleNumericCondition(condName, condName, circuit, name, dstAddress, field, valueRanges); - return RESULT_OK; + // name,circuit,messagename,[comment],[fieldname],[ZZ],values (name already skipped by caller) + string circuit = it == end ? "" : *(it++); // circuit + string name = it == end ? "" : *(it++); // messagename + if (it < end) { + it++; // comment + } + string field = it == end ? "" : *(it++); // fieldname + string zz = it == end ? "" : *(it++); // ZZ + unsigned char dstAddress = SYN; + result_t result = RESULT_OK; + if (zz.length() == 0) { + zz = defaultDest; + } + if (zz.length() > 0) { + dstAddress = (unsigned char)parseInt(zz.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; + } + if (dstAddress != SYN && !isValidAddress(dstAddress, false)) { + return RESULT_ERR_INVALID_ADDR; + } + } + if (name.length() == 0) { + if (!isValidAddress(dstAddress, false) || isMaster(dstAddress)) { + return RESULT_ERR_INVALID_ADDR; + } + } else if (circuit.length() == 0) { + circuit = defaultCircuit; + } + string valueList = it == end ? "" : *(it++); + if (valueList.length() == 0) { + returnValue = new SimpleCondition(condName, condName, circuit, name, dstAddress, field); + return RESULT_OK; + } + if (valueList[0] == '\'') { + // strings + vector values; + result = splitValues(valueList, values); + if (result != RESULT_OK) { + return result; + } + returnValue = new SimpleStringCondition(condName, condName, circuit, name, dstAddress, field, values); + return RESULT_OK; + } + // numbers + vector valueRanges; + result = splitValues(valueList, valueRanges); + if (result != RESULT_OK) { + return result; + } + returnValue = new SimpleNumericCondition(condName, condName, circuit, name, dstAddress, field, valueRanges); + return RESULT_OK; } SimpleCondition* SimpleCondition::derive(string valueList) { - if (valueList.empty()) { - return NULL; - } - string name = m_condName+valueList; - if (valueList[0] == '=') { - valueList.erase(0, 1); - } - result_t result; - if (valueList[0] == '\'') { - // strings - vector values; - result = splitValues(valueList, values); - if (result != RESULT_OK) { - return NULL; - } - return new SimpleStringCondition(name, m_refName, m_circuit, m_name, m_dstAddress, m_field, values); - } - // numbers - if (!isNumeric()) { - return NULL; - } - vector valueRanges; - result = splitValues(valueList, valueRanges); - if (result != RESULT_OK) { - return NULL; - } - return new SimpleNumericCondition(name, m_refName, m_circuit, m_name, m_dstAddress, m_field, valueRanges); + if (valueList.empty()) { + return NULL; + } + string name = m_condName+valueList; + if (valueList[0] == '=') { + valueList.erase(0, 1); + } + result_t result; + if (valueList[0] == '\'') { + // strings + vector values; + result = splitValues(valueList, values); + if (result != RESULT_OK) { + return NULL; + } + return new SimpleStringCondition(name, m_refName, m_circuit, m_name, m_dstAddress, m_field, values); + } + // numbers + if (!isNumeric()) { + return NULL; + } + vector valueRanges; + result = splitValues(valueList, valueRanges); + if (result != RESULT_OK) { + return NULL; + } + return new SimpleNumericCondition(name, m_refName, m_circuit, m_name, m_dstAddress, m_field, valueRanges); } void SimpleCondition::dump(ostream& output, bool matched) { - if (matched) { - if (!m_isTrue) { - return; - } - output << "[" << m_refName; - if (m_hasValues) { - output << "=" << m_matchedValue; - } - output << "]"; - } else { - output << "[" << m_condName << "]"; - } + if (matched) { + if (!m_isTrue) { + return; + } + output << "[" << m_refName; + if (m_hasValues) { + output << "=" << m_matchedValue; + } + output << "]"; + } else { + output << "[" << m_condName << "]"; + } } CombinedCondition* SimpleCondition::combineAnd(Condition* other) { - CombinedCondition* ret = new CombinedCondition(); - return ret->combineAnd(this)->combineAnd(other); + CombinedCondition* ret = new CombinedCondition(); + return ret->combineAnd(this)->combineAnd(other); } result_t SimpleCondition::resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)) { - if (m_message == NULL) { - Message* message; - if (m_name.length() == 0) { - message = messages->getScanMessage(m_dstAddress); - errorMessage << "scan condition " << nouppercase << setw(2) << hex << setfill('0') << static_cast(m_dstAddress); - } else { - message = messages->find(m_circuit, m_name, false); - if (!message) { - message = messages->find(m_circuit, m_name, false, true); - } - errorMessage << "condition " << m_circuit << " " << m_name; - } - if (!message) { - errorMessage << ": message not found"; - return RESULT_ERR_NOTFOUND; - } - if (message->getDstAddress() == SYN) { - if (message->isPassive()) { - errorMessage << ": invalid passive message"; - return RESULT_ERR_INVALID_ARG; - } - if (m_dstAddress == SYN) { - errorMessage << ": destination address missing"; - return RESULT_ERR_INVALID_ADDR; - } - // clone the message with dedicated dstAddress if necessary - uint64_t key = message->getDerivedKey(m_dstAddress); - vector* derived = messages->getByKey(key); - if (derived == NULL) { - message = message->derive(m_dstAddress, true); - messages->add(message); - } else { - Message* first = getFirstAvailable(*derived, message); - if (first == NULL) { - errorMessage << ": conditional derived message " << message->getCircuit() << "." << message->getName() << " for " << hex << setw(2) << setfill('0') << static_cast(m_dstAddress) << " not found"; - return RESULT_ERR_INVALID_ARG; - } - message = first; - } - } + if (m_message == NULL) { + Message* message; + if (m_name.length() == 0) { + message = messages->getScanMessage(m_dstAddress); + errorMessage << "scan condition " << nouppercase << setw(2) << hex << setfill('0') << static_cast(m_dstAddress); + } else { + message = messages->find(m_circuit, m_name, false); + if (!message) { + message = messages->find(m_circuit, m_name, false, true); + } + errorMessage << "condition " << m_circuit << " " << m_name; + } + if (!message) { + errorMessage << ": message not found"; + return RESULT_ERR_NOTFOUND; + } + if (message->getDstAddress() == SYN) { + if (message->isPassive()) { + errorMessage << ": invalid passive message"; + return RESULT_ERR_INVALID_ARG; + } + if (m_dstAddress == SYN) { + errorMessage << ": destination address missing"; + return RESULT_ERR_INVALID_ADDR; + } + // clone the message with dedicated dstAddress if necessary + uint64_t key = message->getDerivedKey(m_dstAddress); + vector* derived = messages->getByKey(key); + if (derived == NULL) { + message = message->derive(m_dstAddress, true); + messages->add(message); + } else { + Message* first = getFirstAvailable(*derived, message); + if (first == NULL) { + errorMessage << ": conditional derived message " << message->getCircuit() << "." << message->getName() << " for " << hex << setw(2) << setfill('0') << static_cast(m_dstAddress) << " not found"; + return RESULT_ERR_INVALID_ARG; + } + message = first; + } + } - if (m_hasValues) { - if (!message->hasField(m_field.length() > 0 ? m_field.c_str() : NULL, isNumeric())) { - errorMessage << (isNumeric() ? ": numeric field " : ": string field ") << m_field << " not found"; - return RESULT_ERR_NOTFOUND; - } - } - m_message = message; - message->setUsedByCondition(); - if (m_name.length() > 0 && !message->isScanMessage()) { - messages->addPollMessage(message, true); - } - } - if (m_message->getLastUpdateTime() == 0 && readMessageFunc != NULL) { - (*readMessageFunc)(m_message); - } - return RESULT_OK; + if (m_hasValues) { + if (!message->hasField(m_field.length() > 0 ? m_field.c_str() : NULL, isNumeric())) { + errorMessage << (isNumeric() ? ": numeric field " : ": string field ") << m_field << " not found"; + return RESULT_ERR_NOTFOUND; + } + } + m_message = message; + message->setUsedByCondition(); + if (m_name.length() > 0 && !message->isScanMessage()) { + messages->addPollMessage(message, true); + } + } + if (m_message->getLastUpdateTime() == 0 && readMessageFunc != NULL) { + (*readMessageFunc)(m_message); + } + return RESULT_OK; } bool SimpleCondition::isTrue() { - if (!m_message) { - return false; - } - if (m_message->getLastChangeTime() > m_lastCheckTime) { - bool isTrue = !m_hasValues; // for message seen check - if (!isTrue) { - isTrue = checkValue(m_message, m_field); - } - m_isTrue = isTrue; - m_lastCheckTime = m_message->getLastChangeTime(); - } - return m_isTrue; + if (!m_message) { + return false; + } + if (m_message->getLastChangeTime() > m_lastCheckTime) { + bool isTrue = !m_hasValues; // for message seen check + if (!isTrue) { + isTrue = checkValue(m_message, m_field); + } + m_isTrue = isTrue; + m_lastCheckTime = m_message->getLastChangeTime(); + } + return m_isTrue; } bool SimpleNumericCondition::checkValue(Message* message, string field) { - unsigned int value = 0; - result_t result = message->decodeLastDataNumField(value, field.length() == 0 ? NULL : field.c_str()); - if (result == RESULT_OK) { - for (size_t i = 0; i+1 < m_valueRanges.size(); i+=2) { - if (m_valueRanges[i] <= value && value <= m_valueRanges[i+1]) { - ostringstream out; - out << static_cast(value); - m_matchedValue = out.str(); - return true; - } - } - } - return false; + unsigned int value = 0; + result_t result = message->decodeLastDataNumField(value, field.length() == 0 ? NULL : field.c_str()); + if (result == RESULT_OK) { + for (size_t i = 0; i+1 < m_valueRanges.size(); i+=2) { + if (m_valueRanges[i] <= value && value <= m_valueRanges[i+1]) { + ostringstream out; + out << static_cast(value); + m_matchedValue = out.str(); + return true; + } + } + } + return false; } bool SimpleStringCondition::checkValue(Message* message, string field) { - ostringstream output; - result_t result = message->decodeLastData(output, 0, false, field.length() == 0 ? NULL : field.c_str()); - if (result == RESULT_OK) { - string value = output.str(); - for (size_t i = 0; i < m_values.size(); i++) { - if (m_values[i] == value) { - m_matchedValue = "'"+value+"'"; - return true; - } - } - } - return false; + ostringstream output; + result_t result = message->decodeLastData(output, 0, false, field.length() == 0 ? NULL : field.c_str()); + if (result == RESULT_OK) { + string value = output.str(); + for (size_t i = 0; i < m_values.size(); i++) { + if (m_values[i] == value) { + m_matchedValue = "'"+value+"'"; + return true; + } + } + } + return false; } void CombinedCondition::dump(ostream& output, bool matched) { - for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - Condition* condition = *it; - condition->dump(output, matched); - } + for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + Condition* condition = *it; + condition->dump(output, matched); + } } result_t CombinedCondition::resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message)) { - for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - Condition* condition = *it; - ostringstream dummy; - result_t ret = condition->resolve(messages, dummy, readMessageFunc); - if (ret != RESULT_OK) { - errorMessage << dummy.str(); - return ret; - } - } - return RESULT_OK; + for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + Condition* condition = *it; + ostringstream dummy; + result_t ret = condition->resolve(messages, dummy, readMessageFunc); + if (ret != RESULT_OK) { + errorMessage << dummy.str(); + return ret; + } + } + return RESULT_OK; } bool CombinedCondition::isTrue() { - for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - if (!(*it)->isTrue()) { - return false; - } - } - return true; + for (vector::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + if (!(*it)->isTrue()) { + return false; + } + } + return true; } result_t Instruction::create(const string contextPath, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - Condition* condition, const string type, vector::iterator& it, const vector::iterator end, Instruction*& returnValue) { - // type[,argument]* (type already skipped by caller) - bool singleton = false; - if ((singleton=(type == "load")) || type == "include") { - if (it == end || (*it).empty()) { - return RESULT_ERR_INVALID_ARG; - } - size_t pos = contextPath.find_last_of('/'); - string path; - if (pos == string::npos) { - path = contextPath; - } else { - path = contextPath.substr(0, pos+1); - } - returnValue = new LoadInstruction(condition, singleton, defaultDest, defaultCircuit, defaultSuffix, path+(*it)); - return RESULT_OK; - } - // unknown instruction - return RESULT_ERR_INVALID_ARG; + Condition* condition, const string type, vector::iterator& it, const vector::iterator end, Instruction*& returnValue) { + // type[,argument]* (type already skipped by caller) + bool singleton = false; + if ((singleton=(type == "load")) || type == "include") { + if (it == end || (*it).empty()) { + return RESULT_ERR_INVALID_ARG; + } + size_t pos = contextPath.find_last_of('/'); + string path; + if (pos == string::npos) { + path = contextPath; + } else { + path = contextPath.substr(0, pos+1); + } + returnValue = new LoadInstruction(condition, singleton, defaultDest, defaultCircuit, defaultSuffix, path+(*it)); + return RESULT_OK; + } + // unknown instruction + return RESULT_ERR_INVALID_ARG; } string Instruction::getDestination() { - // ZZ.circuit[.suffix] - string ret; - if (!m_defaultDest.empty()) { - ret = m_defaultDest; - } - if (!m_defaultCircuit.empty() || !m_defaultSuffix.empty()) { - if (!ret.empty()) { - ret += "."; - } - if (m_defaultCircuit.empty()) { - ret += "*"; - } else { - ret += m_defaultCircuit; - } - if (!m_defaultSuffix.empty()) { - ret += m_defaultSuffix; - } - } - return ret; + // ZZ.circuit[.suffix] + string ret; + if (!m_defaultDest.empty()) { + ret = m_defaultDest; + } + if (!m_defaultCircuit.empty() || !m_defaultSuffix.empty()) { + if (!ret.empty()) { + ret += "."; + } + if (m_defaultCircuit.empty()) { + ret += "*"; + } else { + ret += m_defaultCircuit; + } + if (!m_defaultSuffix.empty()) { + ret += m_defaultSuffix; + } + } + return ret; } result_t LoadInstruction::execute(MessageMap* messages, ostringstream& log, Condition* condition) { - result_t result = messages->readFromFile(m_filename, false, m_defaultDest, m_defaultCircuit, m_defaultSuffix); - if (log.tellp() > 0) { - log << ", "; - } - if (result != RESULT_OK) { - log << "error " << (isSingleton() ? "loading \"" : "including \"") << m_filename << "\" for \"" << getDestination() << "\": " << getResultCode(result); - return result; - } - log << (isSingleton() ? "loaded \"" : "included \"") << m_filename << "\" for \"" << getDestination() << "\""; - if (isSingleton() && !m_defaultDest.empty()) { - result_t temp; - unsigned char address = (unsigned char)parseInt(m_defaultDest.c_str(), 16, 0, 0xff, temp); - if (temp == RESULT_OK) { - size_t pos = m_filename.find_last_of('/'); - string filename; - if (pos == string::npos) { - filename = m_filename; - } else { - filename = m_filename.substr(pos+1); - } - string comment; - if (condition) { - ostringstream out; - condition->dump(out, true); - comment = out.str(); - log << " ("+comment+")"; - } - messages->addLoadedFile(address, filename, comment); - } - } - return result; + result_t result = messages->readFromFile(m_filename, false, m_defaultDest, m_defaultCircuit, m_defaultSuffix); + if (log.tellp() > 0) { + log << ", "; + } + if (result != RESULT_OK) { + log << "error " << (isSingleton() ? "loading \"" : "including \"") << m_filename << "\" for \"" << getDestination() << "\": " << getResultCode(result); + return result; + } + log << (isSingleton() ? "loaded \"" : "included \"") << m_filename << "\" for \"" << getDestination() << "\""; + if (isSingleton() && !m_defaultDest.empty()) { + result_t temp; + unsigned char address = (unsigned char)parseInt(m_defaultDest.c_str(), 16, 0, 0xff, temp); + if (temp == RESULT_OK) { + size_t pos = m_filename.find_last_of('/'); + string filename; + if (pos == string::npos) { + filename = m_filename; + } else { + filename = m_filename.substr(pos+1); + } + string comment; + if (condition) { + ostringstream out; + condition->dump(out, true); + comment = out.str(); + log << " ("+comment+")"; + } + messages->addLoadedFile(address, filename, comment); + } + } + return result; } result_t MessageMap::add(Message* message, bool storeByName) { - uint64_t key = message->getKey(); - bool conditional = message->isConditional(); - if (!m_addAll) { - map >::iterator keyIt = m_messagesByKey.find(key); - if (keyIt != m_messagesByKey.end()) { - Message* other = getFirstAvailable(keyIt->second, message); - if (other != NULL) { - if (!conditional) { - return RESULT_ERR_DUPLICATE; // duplicate key - } - if (!other->isConditional()) { - return RESULT_ERR_DUPLICATE; // duplicate key - } - } - } - } - bool isPassive = message->isPassive(); - if (storeByName) { - bool isWrite = message->isWrite(); - string circuit = message->getCircuit(); - FileReader::tolower(circuit); - string name = message->getName(); - FileReader::tolower(name); - string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name; - if (!m_addAll) { - map >::iterator nameIt = m_messagesByName.find(nameKey); - if (nameIt != m_messagesByName.end()) { - vector* messages = &nameIt->second; - if (!message->isConditional() || !messages->front()->isConditional()) { - return RESULT_ERR_DUPLICATE_NAME; // duplicate key - } - } - } - m_messagesByName[nameKey].push_back(message); + uint64_t key = message->getKey(); + bool conditional = message->isConditional(); + if (!m_addAll) { + map >::iterator keyIt = m_messagesByKey.find(key); + if (keyIt != m_messagesByKey.end()) { + Message* other = getFirstAvailable(keyIt->second, message); + if (other != NULL) { + if (!conditional) { + return RESULT_ERR_DUPLICATE; // duplicate key + } + if (!other->isConditional()) { + return RESULT_ERR_DUPLICATE; // duplicate key + } + } + } + } + bool isPassive = message->isPassive(); + if (storeByName) { + bool isWrite = message->isWrite(); + string circuit = message->getCircuit(); + FileReader::tolower(circuit); + string name = message->getName(); + FileReader::tolower(name); + string nameKey = string(isPassive ? "P" : (isWrite ? "W" : "R")) + circuit + FIELD_SEPARATOR + name; + if (!m_addAll) { + map >::iterator nameIt = m_messagesByName.find(nameKey); + if (nameIt != m_messagesByName.end()) { + vector* messages = &nameIt->second; + if (!message->isConditional() || !messages->front()->isConditional()) { + return RESULT_ERR_DUPLICATE_NAME; // duplicate key + } + } + } + m_messagesByName[nameKey].push_back(message); - nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit - map >::iterator nameIt = m_messagesByName.find(nameKey); - if (nameIt == m_messagesByName.end()) { - m_messagesByName[nameKey].push_back(message); // always store first message without circuit (in order of circuit name) - } else { - vector* messages = &nameIt->second; - Message* first = messages->front(); - if (circuit < first->getCircuit()) { - m_messagesByName[nameKey].at(0) = message; // always store first message without circuit (in order of circuit name) - } else if (m_addAll || (conditional && first->isConditional())) { - m_messagesByName[nameKey].push_back(message); // store further messages only if both are conditional or if storing everything - } - } - m_messageCount++; - if (conditional) { - m_conditionalMessageCount++; - } - if (isPassive) { - m_passiveMessageCount++; - } - addPollMessage(message); - } - unsigned char idLength = message->getIdLength(); - if (idLength > m_maxIdLength) { - m_maxIdLength = idLength; - } - m_messagesByKey[key].push_back(message); - return RESULT_OK; + nameKey = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + name; // also store without circuit + map >::iterator nameIt = m_messagesByName.find(nameKey); + if (nameIt == m_messagesByName.end()) { + m_messagesByName[nameKey].push_back(message); // always store first message without circuit (in order of circuit name) + } else { + vector* messages = &nameIt->second; + Message* first = messages->front(); + if (circuit < first->getCircuit()) { + m_messagesByName[nameKey].at(0) = message; // always store first message without circuit (in order of circuit name) + } else if (m_addAll || (conditional && first->isConditional())) { + m_messagesByName[nameKey].push_back(message); // store further messages only if both are conditional or if storing everything + } + } + m_messageCount++; + if (conditional) { + m_conditionalMessageCount++; + } + if (isPassive) { + m_passiveMessageCount++; + } + addPollMessage(message); + } + unsigned char idLength = message->getIdLength(); + if (idLength > m_maxIdLength) { + m_maxIdLength = idLength; + } + m_messagesByKey[key].push_back(message); + return RESULT_OK; } result_t MessageMap::addDefaultFromFile(vector< vector >& defaults, vector& row, - vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, - const string& filename, unsigned int lineNo) { - // check for condition in defaults - string type = row[0]; - if (type.length() > 0 && type[0] == '[' && type[type.length()-1] == ']') { - // condition - type = type.substr(1, type.length()-2); - if (type.find('[') != string::npos || type.find(']') != string::npos) { - m_lastError = "invalid condition name "+type; - return RESULT_ERR_INVALID_ARG; - } - string key = filename+":"+type; - map::iterator it = m_conditions.find(key); - if (it != m_conditions.end()) { - m_lastError = "condition "+type+" already defined"; - return RESULT_ERR_DUPLICATE_NAME; - } - SimpleCondition* condition = NULL; - result_t result = Condition::create(type, ++begin, row.end(), defaultDest, defaultCircuit+defaultSuffix, condition); - if (condition == NULL || result != RESULT_OK) { - m_lastError = "invalid condition"; - return result; - } - m_conditions[key] = condition; - return RESULT_OK; - } - if (row.size() > 1 && defaultCircuit.length() > 0) { - if (row[1].length() == 0) { - row[1] = defaultCircuit+defaultSuffix; // set default circuit and suffix: "circuit[.suffix]" - } else if (row[1][0] == '#') { - row[1] = defaultCircuit+defaultSuffix+row[1]; // move security suffix behind default circuit and suffix: "circuit[.suffix]#security" - } else if (defaultSuffix.length() > 0 && row[1].find_last_of('.') == string::npos) { // circuit suffix not yet present - size_t pos = row[1].find_first_of('#'); - if (pos == string::npos) { - row[1] += defaultSuffix; // append default suffix: "circuit.suffix" - } else { - row[1] = row[1].substr(0, pos)+defaultSuffix+row[1].substr(pos); // insert default suffix: "circuit.suffix#security" - } - } - } - if (row.size() > 5 && defaultDest.length() > 0 && row[5].length() == 0) { - row[5] = defaultDest; // set default destination - } - return FileReader::addDefaultFromFile(defaults, row, begin, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); + vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, + const string& filename, unsigned int lineNo) { + // check for condition in defaults + string type = row[0]; + if (type.length() > 0 && type[0] == '[' && type[type.length()-1] == ']') { + // condition + type = type.substr(1, type.length()-2); + if (type.find('[') != string::npos || type.find(']') != string::npos) { + m_lastError = "invalid condition name "+type; + return RESULT_ERR_INVALID_ARG; + } + string key = filename+":"+type; + map::iterator it = m_conditions.find(key); + if (it != m_conditions.end()) { + m_lastError = "condition "+type+" already defined"; + return RESULT_ERR_DUPLICATE_NAME; + } + SimpleCondition* condition = NULL; + result_t result = Condition::create(type, ++begin, row.end(), defaultDest, defaultCircuit+defaultSuffix, condition); + if (condition == NULL || result != RESULT_OK) { + m_lastError = "invalid condition"; + return result; + } + m_conditions[key] = condition; + return RESULT_OK; + } + if (row.size() > 1 && defaultCircuit.length() > 0) { + if (row[1].length() == 0) { + row[1] = defaultCircuit+defaultSuffix; // set default circuit and suffix: "circuit[.suffix]" + } else if (row[1][0] == '#') { + row[1] = defaultCircuit+defaultSuffix+row[1]; // move security suffix behind default circuit and suffix: "circuit[.suffix]#security" + } else if (defaultSuffix.length() > 0 && row[1].find_last_of('.') == string::npos) { // circuit suffix not yet present + size_t pos = row[1].find_first_of('#'); + if (pos == string::npos) { + row[1] += defaultSuffix; // append default suffix: "circuit.suffix" + } else { + row[1] = row[1].substr(0, pos)+defaultSuffix+row[1].substr(pos); // insert default suffix: "circuit.suffix#security" + } + } + } + if (row.size() > 5 && defaultDest.length() > 0 && row[5].length() == 0) { + row[5] = defaultDest; // set default destination + } + return FileReader::addDefaultFromFile(defaults, row, begin, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); } result_t MessageMap::readConditions(string& types, const string& filename, Condition*& condition) { - size_t pos; - if (types.length() > 0 && types[0] == '[' && (pos=types.find_last_of(']')) != string::npos) { - // check if combined or simple condition is already known - const string combinedkey = filename+":"+types.substr(1, pos-1); - map::iterator it = m_conditions.find(combinedkey); - if (it != m_conditions.end()) { - condition = it->second; - types = types.substr(pos+1); - } else { - bool store = false; - condition = NULL; - while ((pos=types.find(']')) != string::npos) { - // simple condition - string key = filename+":"+types.substr(1, pos-1); - map::iterator it = m_conditions.find(key); - Condition* add = NULL; - if (it == m_conditions.end()) { - // check for on-the-fly condition - size_t pos = key.find_first_of("=<>", filename.length()+1); - if (pos != string::npos) { - it = m_conditions.find(key.substr(0, pos)); - if (it != m_conditions.end()) { - // derive from another condition - add = it->second->derive(key.substr(pos)); - if (add == NULL) { - m_lastError = "derive condition with values "+key.substr(pos)+" failed"; - return RESULT_ERR_INVALID_ARG; - } - m_conditions[key] = add; // store derived condition - } - } - if (add == NULL) { - // shared condition not available - m_lastError = "condition "+types.substr(1, pos-1)+" not defined"; - return RESULT_ERR_NOTFOUND; - } - } else { - add = it->second; - } - if (condition) { - condition = condition->combineAnd(add); - store = true; - } else { - condition = add; - } - types = types.substr(pos+1); - if (types.length() == 0 || types[0] != '[') { - break; - } - } - if (store) { - m_conditions[combinedkey] = condition; // store combined condition - } - } - } - return RESULT_OK; + size_t pos; + if (types.length() > 0 && types[0] == '[' && (pos=types.find_last_of(']')) != string::npos) { + // check if combined or simple condition is already known + const string combinedkey = filename+":"+types.substr(1, pos-1); + map::iterator it = m_conditions.find(combinedkey); + if (it != m_conditions.end()) { + condition = it->second; + types = types.substr(pos+1); + } else { + bool store = false; + condition = NULL; + while ((pos=types.find(']')) != string::npos) { + // simple condition + string key = filename+":"+types.substr(1, pos-1); + map::iterator it = m_conditions.find(key); + Condition* add = NULL; + if (it == m_conditions.end()) { + // check for on-the-fly condition + size_t pos = key.find_first_of("=<>", filename.length()+1); + if (pos != string::npos) { + it = m_conditions.find(key.substr(0, pos)); + if (it != m_conditions.end()) { + // derive from another condition + add = it->second->derive(key.substr(pos)); + if (add == NULL) { + m_lastError = "derive condition with values "+key.substr(pos)+" failed"; + return RESULT_ERR_INVALID_ARG; + } + m_conditions[key] = add; // store derived condition + } + } + if (add == NULL) { + // shared condition not available + m_lastError = "condition "+types.substr(1, pos-1)+" not defined"; + return RESULT_ERR_NOTFOUND; + } + } else { + add = it->second; + } + if (condition) { + condition = condition->combineAnd(add); + store = true; + } else { + condition = add; + } + types = types.substr(pos+1); + if (types.length() == 0 || types[0] != '[') { + break; + } + } + if (store) { + m_conditions[combinedkey] = condition; // store combined condition + } + } + } + return RESULT_OK; } result_t MessageMap::addFromFile(vector::iterator& begin, const vector::iterator end, - vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - const string& filename, unsigned int lineNo) { - vector::iterator restart = begin; - string types = *restart; - Condition* condition = NULL; - result_t result = readConditions(types, filename, condition); - if (result != RESULT_OK) { - return result; - } - if (types.length() > 0 && types[0] == '!') { - // instruction - types = types.substr(1); - Instruction* instruction = NULL; - result_t result = Instruction::create(filename, defaultDest, defaultCircuit, defaultSuffix, condition, types, ++begin, end, instruction); - if (instruction == NULL || result != RESULT_OK) { - m_lastError = "invalid instruction"; - return result; - } - map >::iterator it = m_instructions.find(filename); - if (it == m_instructions.end()) { - vector instructions; - instructions.push_back(instruction); - m_instructions[filename] = instructions; - } else { - it->second.push_back(instruction); - } - return RESULT_OK; - } - if (types.length() == 0) { - types.append("r"); - } else if (types.find(']') != string::npos) { - return RESULT_ERR_INVALID_ARG; - } - result = RESULT_ERR_EOF; - DataFieldTemplates* templates = getTemplates(filename); - istringstream stream(types); - string type; - vector messages; - while (getline(stream, type, VALUE_SEPARATOR)) { - FileReader::trim(type); - *restart = type; - begin = restart; - messages.clear(); - result = Message::create(begin, end, defaults, condition, filename, templates, messages); - for (vector::iterator it = messages.begin(); it != messages.end(); it++) { - Message* message = *it; - if (result == RESULT_OK) { - result = add(message); - if (result == RESULT_ERR_DUPLICATE_NAME) { - begin = restart+3; // mark name as invalid - } else if (result == RESULT_ERR_DUPLICATE) { - begin = restart+8; // mark ID as invalid - } - } - if (result != RESULT_OK) { - delete message; // delete all remaining messages on error - } - } - if (result != RESULT_OK) { - return result; - } - } - return result; + vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + const string& filename, unsigned int lineNo) { + vector::iterator restart = begin; + string types = *restart; + Condition* condition = NULL; + result_t result = readConditions(types, filename, condition); + if (result != RESULT_OK) { + return result; + } + if (types.length() > 0 && types[0] == '!') { + // instruction + types = types.substr(1); + Instruction* instruction = NULL; + result_t result = Instruction::create(filename, defaultDest, defaultCircuit, defaultSuffix, condition, types, ++begin, end, instruction); + if (instruction == NULL || result != RESULT_OK) { + m_lastError = "invalid instruction"; + return result; + } + map >::iterator it = m_instructions.find(filename); + if (it == m_instructions.end()) { + vector instructions; + instructions.push_back(instruction); + m_instructions[filename] = instructions; + } else { + it->second.push_back(instruction); + } + return RESULT_OK; + } + if (types.length() == 0) { + types.append("r"); + } else if (types.find(']') != string::npos) { + return RESULT_ERR_INVALID_ARG; + } + result = RESULT_ERR_EOF; + DataFieldTemplates* templates = getTemplates(filename); + istringstream stream(types); + string type; + vector messages; + while (getline(stream, type, VALUE_SEPARATOR)) { + FileReader::trim(type); + *restart = type; + begin = restart; + messages.clear(); + result = Message::create(begin, end, defaults, condition, filename, templates, messages); + for (vector::iterator it = messages.begin(); it != messages.end(); it++) { + Message* message = *it; + if (result == RESULT_OK) { + result = add(message); + if (result == RESULT_ERR_DUPLICATE_NAME) { + begin = restart+3; // mark name as invalid + } else if (result == RESULT_ERR_DUPLICATE) { + begin = restart+8; // mark ID as invalid + } + } + if (result != RESULT_OK) { + delete message; // delete all remaining messages on error + } + } + if (result != RESULT_OK) { + return result; + } + } + return result; } Message* MessageMap::getScanMessage(const unsigned char dstAddress) { - if (dstAddress == SYN) { - return m_scanMessage; - } - if (!isValidAddress(dstAddress, false) || isMaster(dstAddress)) { - return NULL; - } - uint64_t key = m_scanMessage->getDerivedKey(dstAddress); - vector* msgs = getByKey(key); - if (msgs != NULL) { - return msgs->front(); - } - Message* message = m_scanMessage->derive(dstAddress, true); - add(message); - return message; + if (dstAddress == SYN) { + return m_scanMessage; + } + if (!isValidAddress(dstAddress, false) || isMaster(dstAddress)) { + return NULL; + } + uint64_t key = m_scanMessage->getDerivedKey(dstAddress); + vector* msgs = getByKey(key); + if (msgs != NULL) { + return msgs->front(); + } + Message* message = m_scanMessage->derive(dstAddress, true); + add(message); + return message; } result_t MessageMap::resolveConditions(bool verbose) { - m_lastError = ""; - result_t overallResult = RESULT_OK; - for (map::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - Condition* condition = it->second; - result_t result = resolveCondition(condition); - if (result != RESULT_OK) { - overallResult = result; - } - } - return overallResult; + m_lastError = ""; + result_t overallResult = RESULT_OK; + for (map::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + Condition* condition = it->second; + result_t result = resolveCondition(condition); + if (result != RESULT_OK) { + overallResult = result; + } + } + return overallResult; } result_t MessageMap::resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message)) { - ostringstream error; - result_t result = condition->resolve(this, error, readMessageFunc); - if (result != RESULT_OK) { - string errorMessage = error.str(); - if (errorMessage.length() > 0) { - if (m_lastError.length() > 0) { - m_lastError += ", "; - } - m_lastError += errorMessage; - } - } - return result; + ostringstream error; + result_t result = condition->resolve(this, error, readMessageFunc); + if (result != RESULT_OK) { + string errorMessage = error.str(); + if (errorMessage.length() > 0) { + if (m_lastError.length() > 0) { + m_lastError += ", "; + } + m_lastError += errorMessage; + } + } + return result; } result_t MessageMap::executeInstructions(ostringstream& log, void (*readMessageFunc)(Message* message)) { - m_lastError = ""; - result_t overallResult = RESULT_OK; - vector remove; - for (map >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) { - vector instructions = it->second; - bool removeSingletons = false; - vector remain; - for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { - Instruction* instruction = *lit; - if (removeSingletons && instruction->isSingleton()) { - delete instruction; - continue; - } - Condition* condition = instruction->getCondition(); - bool execute = condition == NULL; - if (!execute) { - result_t result = resolveCondition(condition, instruction->isSingleton()?readMessageFunc:NULL); - if (result != RESULT_OK) { - overallResult = result; - } else if (condition->isTrue()) { - execute = true; - } - } - if (execute) { - if (instruction->isSingleton()) { - removeSingletons = true; - } - result_t result = instruction->execute(this, log, condition); - if (result != RESULT_OK) { - overallResult = result; - } - delete instruction; - } else { - remain.push_back(instruction); - } - } - if (removeSingletons && !remain.empty()) { - instructions = remain; - remain.clear(); - for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { - Instruction* instruction = *lit; - if (!instruction->isSingleton()) { - remain.push_back(instruction); - continue; - } - delete instruction; - } - } - if (remain.empty()) { - remove.push_back(it->first); - } else { - it->second = remain; - } - } - for (vector::iterator it = remove.begin(); it != remove.end(); it++) { - m_instructions.erase(*it); - } - return overallResult; + m_lastError = ""; + result_t overallResult = RESULT_OK; + vector remove; + for (map >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) { + vector instructions = it->second; + bool removeSingletons = false; + vector remain; + for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { + Instruction* instruction = *lit; + if (removeSingletons && instruction->isSingleton()) { + delete instruction; + continue; + } + Condition* condition = instruction->getCondition(); + bool execute = condition == NULL; + if (!execute) { + result_t result = resolveCondition(condition, instruction->isSingleton()?readMessageFunc:NULL); + if (result != RESULT_OK) { + overallResult = result; + } else if (condition->isTrue()) { + execute = true; + } + } + if (execute) { + if (instruction->isSingleton()) { + removeSingletons = true; + } + result_t result = instruction->execute(this, log, condition); + if (result != RESULT_OK) { + overallResult = result; + } + delete instruction; + } else { + remain.push_back(instruction); + } + } + if (removeSingletons && !remain.empty()) { + instructions = remain; + remain.clear(); + for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { + Instruction* instruction = *lit; + if (!instruction->isSingleton()) { + remain.push_back(instruction); + continue; + } + delete instruction; + } + } + if (remain.empty()) { + remove.push_back(it->first); + } else { + it->second = remain; + } + } + for (vector::iterator it = remove.begin(); it != remove.end(); it++) { + m_instructions.erase(*it); + } + return overallResult; } void MessageMap::addLoadedFile(unsigned char address, string file, string comment) { - if (!file.empty()) { - string fileComment = "\""+file+"\""; - if (!comment.empty()) { - fileComment += " ("+comment+")"; - } - if (m_loadedFiles.find(address) == m_loadedFiles.end()) { - m_loadedFiles[address] = fileComment; - } else { - m_loadedFiles[address] += ", "+fileComment; - } - } + if (!file.empty()) { + string fileComment = "\""+file+"\""; + if (!comment.empty()) { + fileComment += " ("+comment+")"; + } + if (m_loadedFiles.find(address) == m_loadedFiles.end()) { + m_loadedFiles[address] = fileComment; + } else { + m_loadedFiles[address] += ", "+fileComment; + } + } } string MessageMap::getLoadedFiles(unsigned char address) { - if (m_loadedFiles.find(address) == m_loadedFiles.end()) { - return ""; - } - return m_loadedFiles[address]; + if (m_loadedFiles.find(address) == m_loadedFiles.end()) { + return ""; + } + return m_loadedFiles[address]; } vector* MessageMap::getByKey(const uint64_t key) { - map >::iterator it = m_messagesByKey.find(key); - if (it != m_messagesByKey.end()) { - return &it->second; - } - return NULL; + map >::iterator it = m_messagesByKey.find(key); + if (it != m_messagesByKey.end()) { + return &it->second; + } + return NULL; } Message* MessageMap::find(const string& circuit, const string& name, const bool isWrite, const bool isPassive) { - string lcircuit = circuit; - FileReader::tolower(lcircuit); - string lname = name; - FileReader::tolower(lname); - for (int i = 0; i < 2; i++) { - string key; - if (i == 0) { - key = string(isPassive ? "P" : (isWrite ? "W" : "R")) + lcircuit + FIELD_SEPARATOR + lname; - } else if (lcircuit.length() == 0) { - key = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + lname; // second try: without circuit - } else { - continue; // not allowed without circuit - } - map >::iterator it = m_messagesByName.find(key); - if (it != m_messagesByName.end()) { - Message* message = getFirstAvailable(it->second); - if (message) { - return message; - } - } - } - return NULL; + string lcircuit = circuit; + FileReader::tolower(lcircuit); + string lname = name; + FileReader::tolower(lname); + for (int i = 0; i < 2; i++) { + string key; + if (i == 0) { + key = string(isPassive ? "P" : (isWrite ? "W" : "R")) + lcircuit + FIELD_SEPARATOR + lname; + } else if (lcircuit.length() == 0) { + key = string(isPassive ? "-P" : (isWrite ? "-W" : "-R")) + lname; // second try: without circuit + } else { + continue; // not allowed without circuit + } + map >::iterator it = m_messagesByName.find(key); + if (it != m_messagesByName.end()) { + Message* message = getFirstAvailable(it->second); + if (message) { + return message; + } + } + } + return NULL; } deque MessageMap::findAll(const string& circuit, const string& name, const bool completeMatch, - const bool withRead, const bool withWrite, const bool withPassive, - const bool completeMatchIgnoreCircuitSuffix, const bool onlyAvailable, - const time_t since, const time_t until) { - bool checkCircuitIgnoreSuffix = completeMatch && completeMatchIgnoreCircuitSuffix; - deque ret; - string lcircuit = circuit; - FileReader::tolower(lcircuit); - string lname = name; - FileReader::tolower(lname); - bool checkCircuit = lcircuit.length() > 0; - bool checkName = name.length() > 0; - if (checkCircuit && checkCircuitIgnoreSuffix) { - size_t pos = lcircuit.find('#'); - if (pos != string::npos) { - lcircuit.resize(pos); - } - } - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - if (it->first[0] == '-') { // avoid duplicates: instances stored multiple times have a key starting with "-" - continue; - } - for (vector::iterator msgIt = it->second.begin(); msgIt != it->second.end(); msgIt++) { - Message* message = *msgIt; - if (checkCircuit) { - string check = message->getCircuit(); - FileReader::tolower(check); - if (checkCircuitIgnoreSuffix) { - size_t pos = check.find('#'); - if (pos != string::npos) { - check.resize(pos); - } - } - if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) { - continue; - } - } - if (checkName) { - string check = message->getName(); - FileReader::tolower(check); - if (completeMatch ? (check != lname) : (check.find(lname) == check.npos)) { - continue; - } - } - if (message->isPassive()) { - if (!withPassive) { - continue; - } - } else if (message->isWrite()) { - if (!withWrite) { - continue; - } - } else { - if (!withRead) { - continue; - } - } - if (since != 0 || until != 0) { - if (message->getDstAddress() == SYN) { - continue; - } - time_t lastchg = message->getLastChangeTime(); - if ((since != 0 && lastchg < since) - || (until != 0 && lastchg >= until)) { - continue; - } - } - if (!onlyAvailable || message->isAvailable()) { - ret.push_back(*msgIt); - } - } - } + const bool withRead, const bool withWrite, const bool withPassive, + const bool completeMatchIgnoreCircuitSuffix, const bool onlyAvailable, + const time_t since, const time_t until) { + bool checkCircuitIgnoreSuffix = completeMatch && completeMatchIgnoreCircuitSuffix; + deque ret; + string lcircuit = circuit; + FileReader::tolower(lcircuit); + string lname = name; + FileReader::tolower(lname); + bool checkCircuit = lcircuit.length() > 0; + bool checkName = name.length() > 0; + if (checkCircuit && checkCircuitIgnoreSuffix) { + size_t pos = lcircuit.find('#'); + if (pos != string::npos) { + lcircuit.resize(pos); + } + } + for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + if (it->first[0] == '-') { // avoid duplicates: instances stored multiple times have a key starting with "-" + continue; + } + for (vector::iterator msgIt = it->second.begin(); msgIt != it->second.end(); msgIt++) { + Message* message = *msgIt; + if (checkCircuit) { + string check = message->getCircuit(); + FileReader::tolower(check); + if (checkCircuitIgnoreSuffix) { + size_t pos = check.find('#'); + if (pos != string::npos) { + check.resize(pos); + } + } + if (completeMatch ? (check != lcircuit) : (check.find(lcircuit) == check.npos)) { + continue; + } + } + if (checkName) { + string check = message->getName(); + FileReader::tolower(check); + if (completeMatch ? (check != lname) : (check.find(lname) == check.npos)) { + continue; + } + } + if (message->isPassive()) { + if (!withPassive) { + continue; + } + } else if (message->isWrite()) { + if (!withWrite) { + continue; + } + } else { + if (!withRead) { + continue; + } + } + if (since != 0 || until != 0) { + if (message->getDstAddress() == SYN) { + continue; + } + time_t lastchg = message->getLastChangeTime(); + if ((since != 0 && lastchg < since) + || (until != 0 && lastchg >= until)) { + continue; + } + } + if (!onlyAvailable || message->isAvailable()) { + ret.push_back(*msgIt); + } + } + } - return ret; + return ret; } Message* MessageMap::find(SymbolString& master, bool anyDestination, - const bool withRead, const bool withWrite, const bool withPassive) { - if (master.size() >= 5 && master[4] == 0 && anyDestination && master[2] == 0x07 && master[3] == 0x04) { - return m_scanMessage; - } - uint64_t baseKey = Message::createKey(master, m_maxIdLength, anyDestination); - if (baseKey == INVALID_KEY) { - return NULL; - } - unsigned char maxIdLength = Message::getKeyLength(baseKey); - for (unsigned char idLength = maxIdLength; true; idLength--) { - uint64_t key = baseKey; - if (idLength == maxIdLength) { - baseKey &= ~ID_LENGTH_AND_IDS_MASK; - } else { - key |= (uint64_t)idLength << (8 * 7 + 5); - int exp = 3; - for (unsigned char i = 0; i < idLength; i++) { - key ^= (uint64_t)master[5 + i] << (8 * exp--); - if (exp == 0) { - exp = 3; - } - } - } - map >::iterator it; - if (withPassive) { - it = m_messagesByKey.find(key); - if (it != m_messagesByKey.end()) { - Message* message = getFirstAvailable(it->second, &master); - if (message) { - return message; - } - } - if ((key & ID_SOURCE_MASK) != 0) { - key &= ~ID_SOURCE_MASK; - it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master - if (it != m_messagesByKey.end()) { - Message* message = getFirstAvailable(it->second, &master); - if (message) { - return message; - } - } - } - } else { - key &= ~ID_SOURCE_MASK; - } - if (withRead) { - it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_READ); // try again with special value for active read - if (it != m_messagesByKey.end()) { - Message* message = getFirstAvailable(it->second, &master); - if (message) { - return message; - } - } - } - if (withWrite) { - it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_WRITE); // try again with special value for active write - if (it != m_messagesByKey.end()) { - Message* message = getFirstAvailable(it->second, &master); - if (message) { - return message; - } - } - } - if (idLength == 0) { - break; - } - } + const bool withRead, const bool withWrite, const bool withPassive) { + if (master.size() >= 5 && master[4] == 0 && anyDestination && master[2] == 0x07 && master[3] == 0x04) { + return m_scanMessage; + } + uint64_t baseKey = Message::createKey(master, m_maxIdLength, anyDestination); + if (baseKey == INVALID_KEY) { + return NULL; + } + unsigned char maxIdLength = Message::getKeyLength(baseKey); + for (unsigned char idLength = maxIdLength; true; idLength--) { + uint64_t key = baseKey; + if (idLength == maxIdLength) { + baseKey &= ~ID_LENGTH_AND_IDS_MASK; + } else { + key |= (uint64_t)idLength << (8 * 7 + 5); + int exp = 3; + for (unsigned char i = 0; i < idLength; i++) { + key ^= (uint64_t)master[5 + i] << (8 * exp--); + if (exp == 0) { + exp = 3; + } + } + } + map >::iterator it; + if (withPassive) { + it = m_messagesByKey.find(key); + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second, &master); + if (message) { + return message; + } + } + if ((key & ID_SOURCE_MASK) != 0) { + key &= ~ID_SOURCE_MASK; + it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second, &master); + if (message) { + return message; + } + } + } + } else { + key &= ~ID_SOURCE_MASK; + } + if (withRead) { + it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_READ); // try again with special value for active read + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second, &master); + if (message) { + return message; + } + } + } + if (withWrite) { + it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_WRITE); // try again with special value for active write + if (it != m_messagesByKey.end()) { + Message* message = getFirstAvailable(it->second, &master); + if (message) { + return message; + } + } + } + if (idLength == 0) { + break; + } + } - return NULL; + return NULL; } void MessageMap::invalidateCache(Message* message) { - if (message->m_data == DataFieldSet::getIdentFields()) { - return; - } - message->m_lastUpdateTime = 0; - string circuit = message->getCircuit(); - string name = message->getName(); - deque messages = findAll(circuit, name, true, true, true, true, true); - for (deque::iterator it = messages.begin(); it != messages.end(); it++) { - Message* checkMessage = *it; - if (checkMessage != message) { - checkMessage->m_lastUpdateTime = 0; - } - } + if (message->m_data == DataFieldSet::getIdentFields()) { + return; + } + message->m_lastUpdateTime = 0; + string circuit = message->getCircuit(); + string name = message->getName(); + deque messages = findAll(circuit, name, true, true, true, true, true); + for (deque::iterator it = messages.begin(); it != messages.end(); it++) { + Message* checkMessage = *it; + if (checkMessage != message) { + checkMessage->m_lastUpdateTime = 0; + } + } } void MessageMap::addPollMessage(Message* message, bool toFront) { - if (message != NULL && message->getPollPriority() > 0) { - message->m_lastPollTime = toFront ? 0 : m_pollMessages.size(); - m_pollMessages.push(message); - } + if (message != NULL && message->getPollPriority() > 0) { + message->m_lastPollTime = toFront ? 0 : m_pollMessages.size(); + m_pollMessages.push(message); + } } void MessageMap::clear() { - m_loadedFiles.clear(); - // clear poll messages - while (!m_pollMessages.empty()) { - m_pollMessages.top(); - m_pollMessages.pop(); - } - // free message instances by name - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - vector nameMessages = it->second; - if (it->first[0] != '-') { // avoid double free: instances stored multiple times have a key starting with "-" - for (vector::iterator nit = nameMessages.begin(); nit != nameMessages.end(); nit++) { - Message* message = *nit; - map >::iterator keyIt = m_messagesByKey.find(message->getKey()); - if (keyIt != m_messagesByKey.end()) { - vector* keyMessages = &keyIt->second; - if (!keyMessages->empty()) { - for (vector::iterator kit = keyMessages->begin(); kit != keyMessages->end(); kit++) { - if (*kit == message) { - keyMessages->erase(kit--); - } - } - } - } - delete message; - } - } - nameMessages.clear(); - } - // free remaining message instances by key - for (map >::iterator it = m_messagesByKey.begin(); it != m_messagesByKey.end(); it++) { - vector keyMessages = it->second; - for (vector::iterator kit = keyMessages.begin(); kit != keyMessages.end(); kit++) { - Message* message = *kit; - delete message; - } - keyMessages.clear(); - } - // free condition instances - for (map::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { - delete it->second; - } - // free instruction instances - for (map >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) { - vector instructions = it->second; - for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { - Instruction* instruction = *lit; - delete instruction; - } - instructions.clear(); - } - // clear messages by name - m_messageCount = 0; - m_conditionalMessageCount = 0; - m_passiveMessageCount = 0; - m_messagesByName.clear(); - // clear messages by key - m_messagesByKey.clear(); - m_conditions.clear(); - m_instructions.clear(); - m_maxIdLength = 0; + m_loadedFiles.clear(); + // clear poll messages + while (!m_pollMessages.empty()) { + m_pollMessages.top(); + m_pollMessages.pop(); + } + // free message instances by name + for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + vector nameMessages = it->second; + if (it->first[0] != '-') { // avoid double free: instances stored multiple times have a key starting with "-" + for (vector::iterator nit = nameMessages.begin(); nit != nameMessages.end(); nit++) { + Message* message = *nit; + map >::iterator keyIt = m_messagesByKey.find(message->getKey()); + if (keyIt != m_messagesByKey.end()) { + vector* keyMessages = &keyIt->second; + if (!keyMessages->empty()) { + for (vector::iterator kit = keyMessages->begin(); kit != keyMessages->end(); kit++) { + if (*kit == message) { + keyMessages->erase(kit--); + } + } + } + } + delete message; + } + } + nameMessages.clear(); + } + // free remaining message instances by key + for (map >::iterator it = m_messagesByKey.begin(); it != m_messagesByKey.end(); it++) { + vector keyMessages = it->second; + for (vector::iterator kit = keyMessages.begin(); kit != keyMessages.end(); kit++) { + Message* message = *kit; + delete message; + } + keyMessages.clear(); + } + // free condition instances + for (map::iterator it = m_conditions.begin(); it != m_conditions.end(); it++) { + delete it->second; + } + // free instruction instances + for (map >::iterator it = m_instructions.begin(); it != m_instructions.end(); it++) { + vector instructions = it->second; + for (vector::iterator lit = instructions.begin(); lit != instructions.end(); lit++) { + Instruction* instruction = *lit; + delete instruction; + } + instructions.clear(); + } + // clear messages by name + m_messageCount = 0; + m_conditionalMessageCount = 0; + m_passiveMessageCount = 0; + m_messagesByName.clear(); + // clear messages by key + m_messagesByKey.clear(); + m_conditions.clear(); + m_instructions.clear(); + m_maxIdLength = 0; } Message* MessageMap::getNextPoll() { - if (m_pollMessages.empty()) { - return NULL; - } - Message* ret = m_pollMessages.top(); - m_pollMessages.pop(); - ret->m_pollCount++; - time(&(ret->m_lastPollTime)); - m_pollMessages.push(ret); // re-insert at new position - return ret; + if (m_pollMessages.empty()) { + return NULL; + } + Message* ret = m_pollMessages.top(); + m_pollMessages.pop(); + ret->m_pollCount++; + time(&(ret->m_lastPollTime)); + m_pollMessages.push(ret); // re-insert at new position + return ret; } void MessageMap::dump(ostream& output, bool withConditions) { - bool first = true; - for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { - if (it->first[0] == '-') { // skip instances stored multiple times (key starting with "-") - continue; - } - if (m_addAll) { - for (vector::iterator mit = it->second.begin(); mit != it->second.end(); mit++) { - Message* message = *mit; - if (!message) { - continue; - } - if (first) { - first = false; - } else { - output << endl; - } - message->dump(output, NULL, withConditions); - } - } else { - Message* message = getFirstAvailable(it->second); - if (!message) { - continue; - } - if (first) { - first = false; - } else { - output << endl; - } - message->dump(output, NULL, withConditions); - } - } - if (!first) { - output << endl; - } + bool first = true; + for (map >::iterator it = m_messagesByName.begin(); it != m_messagesByName.end(); it++) { + if (it->first[0] == '-') { // skip instances stored multiple times (key starting with "-") + continue; + } + if (m_addAll) { + for (vector::iterator mit = it->second.begin(); mit != it->second.end(); mit++) { + Message* message = *mit; + if (!message) { + continue; + } + if (first) { + first = false; + } else { + output << endl; + } + message->dump(output, NULL, withConditions); + } + } else { + Message* message = getFirstAvailable(it->second); + if (!message) { + continue; + } + if (first) { + first = false; + } else { + output << endl; + } + message->dump(output, NULL, withConditions); + } + } + if (!first) { + output << endl; + } } } // namespace ebusd diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 2b131801..e7814953 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -99,518 +99,518 @@ class MessageMap; * Defines parameters of a message sent or received on the bus. */ class Message { - friend class MessageMap; - public: - /** - * Construct a new instance. - * @param circuit the optional circuit name. - * @param name the message name (unique within the same circuit and type). - * @param isWrite whether this is a write message. - * @param isPassive true if message can only be initiated by a participant other than us, - * false if message can be initiated by any participant. - * @param comment the comment. - * @param srcAddress the source address, or @a SYN for any (only relevant if passive). - * @param dstAddress the destination address, or @a SYN for any (set later). - * @param id the primary, secondary, and optional further ID bytes. - * @param data the @a DataField for encoding/decoding the message. - * @param deleteData whether to delete the @a DataField during destruction. - * @param pollPriority the priority for polling, or 0 for no polling at all. - * @param condition the @a Condition for this message, or NULL. - */ - Message(const string circuit, const string name, - const bool isWrite, const bool isPassive, const string comment, - const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, - DataField* data, const bool deleteData, - const unsigned char pollPriority = 0, - Condition* condition = NULL); - - - private: - /** - * Construct a new scan @a Message instance. - * @param circuit the circuit name, or empty for not storing by name. - * @param name the message name (unique within the same circuit and type), or empty for not storing by name. - * @param isWrite whether this is a write message. - * @param isPassive true if message can only be initiated by a participant other than us, - * false if message can be initiated by any participant. - * @param pb the primary ID byte. - * @param sb the secondary ID byte. - * @param data the @a DataField for encoding/decoding the message. - * @param deleteData whether to delete the @a DataField during destruction. - */ - Message(const string circuit, const string name, - const bool isWrite, const bool isPassive, - const unsigned char pb, const unsigned char sb, - DataField* data, const bool deleteData); - - - public: - /** - * Destructor. - */ - virtual ~Message() { if (m_deleteData) { delete m_data; } } - - /** - * Calculate the key for the ID. - * @param id the primary, secondary, and optional further ID bytes. - * @param isWrite whether this is a write message. - * @param isPassive true if message can only be initiated by a participant other than us, - * false if message can be initiated by any participant. - * @param srcAddress the source address, or @a SYN for any (only relevant if passive). - * @param dstAddress the destination address, or @a SYN for any (set later). - * @return the key for the ID. - */ - static uint64_t createKey(const vector id, - const bool isWrite, const bool isPassive, - const unsigned char srcAddress, const unsigned char dstAddress); - - /** - * Calculate the key for the master @a SymbolString. - * @param master the master @a SymbolString. - * @param maxIdLength the maximum ID length to use - * @param anyDestination @p true to use the special @a SYN as destination address in the key. - * @return the key for the ID, or -1LL if the data is invalid. - */ - static uint64_t createKey(SymbolString& master, - unsigned char maxIdLength, bool anyDestination = false); - - /** - * Get the length field from the key. - * @param key the key. - * @return the length field from the key. - */ - static unsigned char getKeyLength(uint64_t key) { return (unsigned char)(key >> (8 * 7 + 5)); } - - /** - * Parse an ID part from the input @a string. - * @param input the input @a string, hex digits optionally separated by space. - * @param id the vector to which to add the parsed values. - * @return @a RESULT_OK on success, or an error code. - */ - static result_t parseId(string input, vector& id); - - /** - * Factory method for creating new instances. - * @param it the iterator to traverse for the definition parts. - * @param end the iterator pointing to the end of the definition parts. - * @param defaultsRows a @a vector with rows containing defaults, or NULL. - * @param condition the @a Condition instance for the message, or NULL. - * @param filename the name of the file being read. - * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. - * @param messages the @a vector to which to add created instances. - * @return @a RESULT_OK on success, or an error code. - * Note: the caller needs to free the created instances. - */ - static result_t create(vector::iterator& it, const vector::iterator end, - vector< vector >* defaultsRows, Condition* condition, const string& filename, - DataFieldTemplates* templates, vector& messages); - - /** - * Create a new scan @a Message instance. - */ - static Message* createScanMessage(); - - /** - * Set that this is a special scanning @a Message instance. - */ - void setScanMessage() { m_isScanMessage = true; } - - /** - * Return whether this is a special scanning @a Message instance. - * @return whether this is a special scanning @a Message instance. - */ - bool isScanMessage() { return m_isScanMessage; } - - /** - * Derive a new @a Message from this message. - * @param dstAddress the new destination address. - * @param srcAddress the new source address, or @a SYN to keep the current source address. - * @param circuit the new circuit name, or empty to use the current circuit name. - * @return the derived @a Message instance. - */ - virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress = SYN, const string circuit = ""); - - /** - * Derive a new @a Message from this message. - * @param dstAddress the new destination address. - * @param extendCircuit whether to extend the current circuit name with a dot and the new destination address in hex. - * @return the derived @a ScanMessage instance. - */ - Message* derive(const unsigned char dstAddress, const bool extendCircuit); - - /** - * Get the optional circuit name. - * @return the optional circuit name. - */ - string getCircuit() const { return m_circuit; } - - /** - * Get the message name (unique within the same circuit and type). - * @return the message name (unique within the same circuit and type). - */ - string getName() const { return m_name; } - - /** - * Get the specified field name. - * @param fieldIndex the index of the field. - * @return the field name, or the index as string if not unique or not available. - */ - virtual string getFieldName(signed char fieldIndex) const { return m_data->getName(fieldIndex); } - - /** - * Get whether this is a write message. - * @return whether this is a write message. - */ - bool isWrite() const { return m_isWrite; } - - /** - * Get whether message can be initiated only by a participant other than us. - * @return true if message can only be initiated by a participant other than us, - * false if message can be initiated by any participant. - */ - bool isPassive() const { return m_isPassive; } - - /** - * Get the comment. - * @return the comment. - */ - string getComment() const { return m_comment; } - - /** - * Get the source address. - * @return the source address, or @a SYN for any. - */ - unsigned char getSrcAddress() const { return m_srcAddress; } - - /** - * Get the destination address. - * @return the destination address, or @a SYN for any. - */ - unsigned char getDstAddress() const { return m_dstAddress; } - - /** - * Get the primary command byte. - * @return the primary command byte. - */ - unsigned char getPrimaryCommand() const { return m_id[0]; } - - /** - * Get the secondary command byte. - * @return the secondary command byte. - */ - unsigned char getSecondaryCommand() const { return m_id[1]; } - - /** - * Get the length of the ID bytes (without primary and secondary command bytes). - * @return the length of the ID bytes (without primary and secondary command bytes). - */ - virtual unsigned char getIdLength() const { return (unsigned char)(m_id.size() - 2); } - - /** - * Check if the full command ID starts with the given value. - * @param id the ID bytes to check against. - * @return true if the full command ID starts with the given value. - */ - bool checkIdPrefix(vector& id); - - /** - * Check the ID against the master @a SymbolString data. - * @param master the master @a SymbolString to check against. - * @param index the variable in which to store the message part index, or NULL to ignore. - * @return true if the ID matches, false otherwise. - */ - virtual bool checkId(SymbolString& master, unsigned char* index = NULL); - - /** - * Check the ID against the other @a Message. - * @param other the other @a Message to check against. - * @return true if the ID matches, false otherwise. - */ - virtual bool checkId(Message& other); - - /** - * Return the key for storing in @a MessageMap. - * @return the key for storing in @a MessageMap. - */ - uint64_t getKey() { return m_key; } - - /** - * Return the derived key for storing in @a MessageMap. - * @param dstAddress the destination address for the derivation. - * @return the derived key for storing in @a MessageMap. - */ - uint64_t getDerivedKey(const unsigned char dstAddress); - - /** - * Get the polling priority, or 0 for no polling at all. - * @return the polling priority, or 0 for no polling at all. - */ - unsigned char getPollPriority() const { return m_pollPriority; } - - /** - * Set the polling priority. - * @param priority the polling priority, or 0 for no polling at all. - * @return true when the priority was changed and polling was not enabled before, false otherwise. - */ - bool setPollPriority(unsigned char priority); - - /** - * Set the poll priority suitable for resolving a @a Condition. - */ - void setUsedByCondition(); - - /** - * Return whether this @a Message depends on a @a Condition. - * @return true when this @a Message depends on a @a Condition. - */ - bool isConditional() const { return m_condition != NULL; } - - /** - * Return whether this @a Message is available (optionally depending on a @a Condition evaluation). - * @return true when this @a Message is available. - */ - bool isAvailable(); - - /** - * Return whether the field is available. - * @param fieldName the name of the field to find, or NULL for any. - * @param numeric true for a numeric field, false for a string field. - * @return true if the field is available. - */ - bool hasField(const char* fieldName, bool numeric = true); - - /** - * @return the number of parts this message is composed of. - */ - virtual unsigned char getCount() { return 1; } - - /** - * Prepare the master @a SymbolString for sending a query or command to the bus. - * @param srcAddress the source address to set. - * @param masterData the master data @a SymbolString for writing symbols to. - * @param input the @a istringstream to parse the formatted value(s) from. - * @param separator the separator character between multiple fields. - * @param dstAddress the destination address to set, or @a SYN to keep the address defined during construction. - * @param index the index of the part to prepare. - * @return @a RESULT_OK on success, or an error code. - */ - result_t prepareMaster(const unsigned char srcAddress, SymbolString& masterData, - istringstream& input, char separator = UI_FIELD_SEPARATOR, - const unsigned char dstAddress = SYN, unsigned char index = 0); - - - protected: - /** - * Prepare a part of the master data @a SymbolString for sending (everything including NN). - * @param master the master data @a SymbolString for writing symbols to. - * @param input the @a istringstream to parse the formatted value(s) from. - * @param separator the separator character between multiple fields. - * @param index the index of the part to prepare. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); - - - public: - /** - * Prepare the slave @a SymbolString for sending an answer to the bus. - * @param input the @a istringstream to parse the formatted value(s) from. - * @param slaveData the slave data @a SymbolString for writing symbols to. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t prepareSlave(istringstream& input, SymbolString& slaveData); - - /** - * Store the last seen master and slave data. - * @param master the last seen master data. - * @param slave the last seen slave data. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t storeLastData(SymbolString& master, SymbolString& slave); - - /** - * Store last seen master or slave data. - * @param partType the @a PartType of the data. - * @param data the last seen data. - * @param index the index of the part to store. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); - - /** - * Decode the value from the last stored data. - * @param partType the @a PartType of the data. - * @param output the @a ostringstream to append the formatted value to. - * @param outputFormat the @a OutputFormat options to use. - * @param leadingSeparator whether to prepend a separator before the formatted value. - * @param fieldName the optional name of a field to limit the output to. - * @param fieldIndex the optional index of the named field to limit the output to, or -1. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t decodeLastData(const PartType partType, - ostringstream& output, OutputFormat outputFormat = 0, - bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); - - /** - * Decode the value from the last stored data. - * @param output the @a ostringstream to append the formatted value to. - * @param outputFormat the @a OutputFormat options to use. - * @param leadingSeparator whether to prepend a separator before the formatted value. - * @param fieldName the optional name of a field to limit the output to. - * @param fieldIndex the optional index of the named field to limit the output to, or -1. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t decodeLastData(ostringstream& output, OutputFormat outputFormat = 0, - bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); - - /** - * Decode a particular numeric field value from the last stored data. - * @param output the variable in which to store the value. - * @param fieldName the name of the field to decode, or NULL for the first field. - * @param fieldIndex the optional index of the named field, or -1. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t decodeLastDataNumField(unsigned int& output, const char* fieldName, signed char fieldIndex = -1); - - /** - * Get the last seen master data. - * @return the last seen master @a SymbolString. - */ - SymbolString& getLastMasterData() { return m_lastMasterData; } - - /** - * Get the last seen slave data. - * @return the last seen slave @a SymbolString. - */ - SymbolString& getLastSlaveData() { return m_lastSlaveData; } - - /** - * Get the time when this message was last seen with reasonable data. - * @return the time when this message was last seen, or 0. - */ - time_t getLastUpdateTime() { return m_lastUpdateTime; } - - /** - * Get the time when the message data was last changed. - * @return the time when the message data was last changed, or 0 if this message was not decoded yet. - */ - time_t getLastChangeTime() { return m_lastChangeTime; } - - /** - * Get the time when this message was last polled for. - * @return the time when this message was last polled for, or 0 for never. - */ - time_t getLastPollTime() { return m_lastPollTime; } - - /** - * Return whether this @a Message needs to be polled after the other one. - * @param other the other @a Message to compare with. - * @return true if this @a Message needs to be polled after the other one. - */ - bool isLessPollWeight(const Message* other); - - /** - * Write the message definition or parts of it to the @a ostream. - * @param output the @a ostream to append the formatted value to. - * @param columns the list of column indexes to write, or NULL for all (see @p COLUMN_TYPE index constants). - * @param withConditions whether to include the optional conditions prefix. - */ - void dump(ostream& output, vector* columns = NULL, bool withConditions = false); - - /** - * Write the specified column to the @a ostream. - * @param output the @a ostream to append the formatted value to. - * @param column the column index to write (see @p COLUMN_TYPE index constants). - * @param withConditions whether to include the optional conditions prefix. - */ - virtual void dumpColumn(ostream& output, size_t column, bool withConditions = false); - - - protected: - /** the optional circuit name. */ - const string m_circuit; - - /** the message name (unique within the same circuit and type). */ - const string m_name; - - /** whether this is a write message. */ - const bool m_isWrite; - - /** true if message can only be initiated by a participant other than us, - * false if message can be initiated by any participant. */ - const bool m_isPassive; - - /** the comment. */ - const string m_comment; - - /** the source address, or @a SYN for any (only relevant if passive). */ - const unsigned char m_srcAddress; - - /** the destination address, or @a SYN for any (only for temporary scan messages). */ - const unsigned char m_dstAddress; - - /** the primary, secondary, and optionally further command ID bytes. */ - vector m_id; - - /** - * the key for storing in @a MessageMap. - *
    - *
  • byte 7: - *
      - *
    • bits 5-7: length of ID bytes (without PB/SB)
    • - *
    • bits 0-4: - *
        - *
      • master number (1..25) of sender for passive message
      • - *
      • 0x00 for passive message with any sender
      • - *
      • 0x1f for active write
      • - *
      • 0x1e for active read
      • - *
      - *
    - *
  • - *
  • byte 6: ZZ or SYN for any
  • - *
  • byte 5: PB
  • - *
  • byte 4: SB
  • - *
  • bytes 3-0: ID bytes (with cyclic xor if more than 4)
  • - *
- */ - uint64_t m_key; - - /** the @a DataField for encoding/decoding the message. */ - DataField* m_data; - - /** whether to delete the @a DataField during destruction. */ - const bool m_deleteData; - - /** the priority for polling, or 0 for no polling at all. */ - unsigned char m_pollPriority; - - /** whether this message is used by a @a Condition. */ - bool m_usedByCondition; - - /** whether this is a special scanning @a Message instance. */ - bool m_isScanMessage; - - /** the @a Condition for this message, or NULL. */ - Condition* m_condition; - - /** the last seen master data. */ - SymbolString m_lastMasterData; - - /** the last seen slave data. */ - SymbolString m_lastSlaveData; - - /** the system time when the message was last updated, 0 for never. */ - time_t m_lastUpdateTime; - - /** the system time when the message content was last changed, 0 for never. */ - time_t m_lastChangeTime; - - /** the number of times this messages was already polled for. */ - unsigned int m_pollCount; - - /** the system time when this message was last polled for, 0 for never. */ - time_t m_lastPollTime; + friend class MessageMap; + public: + /** + * Construct a new instance. + * @param circuit the optional circuit name. + * @param name the message name (unique within the same circuit and type). + * @param isWrite whether this is a write message. + * @param isPassive true if message can only be initiated by a participant other than us, + * false if message can be initiated by any participant. + * @param comment the comment. + * @param srcAddress the source address, or @a SYN for any (only relevant if passive). + * @param dstAddress the destination address, or @a SYN for any (set later). + * @param id the primary, secondary, and optional further ID bytes. + * @param data the @a DataField for encoding/decoding the message. + * @param deleteData whether to delete the @a DataField during destruction. + * @param pollPriority the priority for polling, or 0 for no polling at all. + * @param condition the @a Condition for this message, or NULL. + */ + Message(const string circuit, const string name, + const bool isWrite, const bool isPassive, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, + DataField* data, const bool deleteData, + const unsigned char pollPriority = 0, + Condition* condition = NULL); + + + private: + /** + * Construct a new scan @a Message instance. + * @param circuit the circuit name, or empty for not storing by name. + * @param name the message name (unique within the same circuit and type), or empty for not storing by name. + * @param isWrite whether this is a write message. + * @param isPassive true if message can only be initiated by a participant other than us, + * false if message can be initiated by any participant. + * @param pb the primary ID byte. + * @param sb the secondary ID byte. + * @param data the @a DataField for encoding/decoding the message. + * @param deleteData whether to delete the @a DataField during destruction. + */ + Message(const string circuit, const string name, + const bool isWrite, const bool isPassive, + const unsigned char pb, const unsigned char sb, + DataField* data, const bool deleteData); + + + public: + /** + * Destructor. + */ + virtual ~Message() { if (m_deleteData) { delete m_data; } } + + /** + * Calculate the key for the ID. + * @param id the primary, secondary, and optional further ID bytes. + * @param isWrite whether this is a write message. + * @param isPassive true if message can only be initiated by a participant other than us, + * false if message can be initiated by any participant. + * @param srcAddress the source address, or @a SYN for any (only relevant if passive). + * @param dstAddress the destination address, or @a SYN for any (set later). + * @return the key for the ID. + */ + static uint64_t createKey(const vector id, + const bool isWrite, const bool isPassive, + const unsigned char srcAddress, const unsigned char dstAddress); + + /** + * Calculate the key for the master @a SymbolString. + * @param master the master @a SymbolString. + * @param maxIdLength the maximum ID length to use + * @param anyDestination @p true to use the special @a SYN as destination address in the key. + * @return the key for the ID, or -1LL if the data is invalid. + */ + static uint64_t createKey(SymbolString& master, + unsigned char maxIdLength, bool anyDestination = false); + + /** + * Get the length field from the key. + * @param key the key. + * @return the length field from the key. + */ + static unsigned char getKeyLength(uint64_t key) { return (unsigned char)(key >> (8 * 7 + 5)); } + + /** + * Parse an ID part from the input @a string. + * @param input the input @a string, hex digits optionally separated by space. + * @param id the vector to which to add the parsed values. + * @return @a RESULT_OK on success, or an error code. + */ + static result_t parseId(string input, vector& id); + + /** + * Factory method for creating new instances. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @param defaultsRows a @a vector with rows containing defaults, or NULL. + * @param condition the @a Condition instance for the message, or NULL. + * @param filename the name of the file being read. + * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. + * @param messages the @a vector to which to add created instances. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller needs to free the created instances. + */ + static result_t create(vector::iterator& it, const vector::iterator end, + vector< vector >* defaultsRows, Condition* condition, const string& filename, + DataFieldTemplates* templates, vector& messages); + + /** + * Create a new scan @a Message instance. + */ + static Message* createScanMessage(); + + /** + * Set that this is a special scanning @a Message instance. + */ + void setScanMessage() { m_isScanMessage = true; } + + /** + * Return whether this is a special scanning @a Message instance. + * @return whether this is a special scanning @a Message instance. + */ + bool isScanMessage() { return m_isScanMessage; } + + /** + * Derive a new @a Message from this message. + * @param dstAddress the new destination address. + * @param srcAddress the new source address, or @a SYN to keep the current source address. + * @param circuit the new circuit name, or empty to use the current circuit name. + * @return the derived @a Message instance. + */ + virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress = SYN, const string circuit = ""); + + /** + * Derive a new @a Message from this message. + * @param dstAddress the new destination address. + * @param extendCircuit whether to extend the current circuit name with a dot and the new destination address in hex. + * @return the derived @a ScanMessage instance. + */ + Message* derive(const unsigned char dstAddress, const bool extendCircuit); + + /** + * Get the optional circuit name. + * @return the optional circuit name. + */ + string getCircuit() const { return m_circuit; } + + /** + * Get the message name (unique within the same circuit and type). + * @return the message name (unique within the same circuit and type). + */ + string getName() const { return m_name; } + + /** + * Get the specified field name. + * @param fieldIndex the index of the field. + * @return the field name, or the index as string if not unique or not available. + */ + virtual string getFieldName(signed char fieldIndex) const { return m_data->getName(fieldIndex); } + + /** + * Get whether this is a write message. + * @return whether this is a write message. + */ + bool isWrite() const { return m_isWrite; } + + /** + * Get whether message can be initiated only by a participant other than us. + * @return true if message can only be initiated by a participant other than us, + * false if message can be initiated by any participant. + */ + bool isPassive() const { return m_isPassive; } + + /** + * Get the comment. + * @return the comment. + */ + string getComment() const { return m_comment; } + + /** + * Get the source address. + * @return the source address, or @a SYN for any. + */ + unsigned char getSrcAddress() const { return m_srcAddress; } + + /** + * Get the destination address. + * @return the destination address, or @a SYN for any. + */ + unsigned char getDstAddress() const { return m_dstAddress; } + + /** + * Get the primary command byte. + * @return the primary command byte. + */ + unsigned char getPrimaryCommand() const { return m_id[0]; } + + /** + * Get the secondary command byte. + * @return the secondary command byte. + */ + unsigned char getSecondaryCommand() const { return m_id[1]; } + + /** + * Get the length of the ID bytes (without primary and secondary command bytes). + * @return the length of the ID bytes (without primary and secondary command bytes). + */ + virtual unsigned char getIdLength() const { return (unsigned char)(m_id.size() - 2); } + + /** + * Check if the full command ID starts with the given value. + * @param id the ID bytes to check against. + * @return true if the full command ID starts with the given value. + */ + bool checkIdPrefix(vector& id); + + /** + * Check the ID against the master @a SymbolString data. + * @param master the master @a SymbolString to check against. + * @param index the variable in which to store the message part index, or NULL to ignore. + * @return true if the ID matches, false otherwise. + */ + virtual bool checkId(SymbolString& master, unsigned char* index = NULL); + + /** + * Check the ID against the other @a Message. + * @param other the other @a Message to check against. + * @return true if the ID matches, false otherwise. + */ + virtual bool checkId(Message& other); + + /** + * Return the key for storing in @a MessageMap. + * @return the key for storing in @a MessageMap. + */ + uint64_t getKey() { return m_key; } + + /** + * Return the derived key for storing in @a MessageMap. + * @param dstAddress the destination address for the derivation. + * @return the derived key for storing in @a MessageMap. + */ + uint64_t getDerivedKey(const unsigned char dstAddress); + + /** + * Get the polling priority, or 0 for no polling at all. + * @return the polling priority, or 0 for no polling at all. + */ + unsigned char getPollPriority() const { return m_pollPriority; } + + /** + * Set the polling priority. + * @param priority the polling priority, or 0 for no polling at all. + * @return true when the priority was changed and polling was not enabled before, false otherwise. + */ + bool setPollPriority(unsigned char priority); + + /** + * Set the poll priority suitable for resolving a @a Condition. + */ + void setUsedByCondition(); + + /** + * Return whether this @a Message depends on a @a Condition. + * @return true when this @a Message depends on a @a Condition. + */ + bool isConditional() const { return m_condition != NULL; } + + /** + * Return whether this @a Message is available (optionally depending on a @a Condition evaluation). + * @return true when this @a Message is available. + */ + bool isAvailable(); + + /** + * Return whether the field is available. + * @param fieldName the name of the field to find, or NULL for any. + * @param numeric true for a numeric field, false for a string field. + * @return true if the field is available. + */ + bool hasField(const char* fieldName, bool numeric = true); + + /** + * @return the number of parts this message is composed of. + */ + virtual unsigned char getCount() { return 1; } + + /** + * Prepare the master @a SymbolString for sending a query or command to the bus. + * @param srcAddress the source address to set. + * @param masterData the master data @a SymbolString for writing symbols to. + * @param input the @a istringstream to parse the formatted value(s) from. + * @param separator the separator character between multiple fields. + * @param dstAddress the destination address to set, or @a SYN to keep the address defined during construction. + * @param index the index of the part to prepare. + * @return @a RESULT_OK on success, or an error code. + */ + result_t prepareMaster(const unsigned char srcAddress, SymbolString& masterData, + istringstream& input, char separator = UI_FIELD_SEPARATOR, + const unsigned char dstAddress = SYN, unsigned char index = 0); + + + protected: + /** + * Prepare a part of the master data @a SymbolString for sending (everything including NN). + * @param master the master data @a SymbolString for writing symbols to. + * @param input the @a istringstream to parse the formatted value(s) from. + * @param separator the separator character between multiple fields. + * @param index the index of the part to prepare. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); + + + public: + /** + * Prepare the slave @a SymbolString for sending an answer to the bus. + * @param input the @a istringstream to parse the formatted value(s) from. + * @param slaveData the slave data @a SymbolString for writing symbols to. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t prepareSlave(istringstream& input, SymbolString& slaveData); + + /** + * Store the last seen master and slave data. + * @param master the last seen master data. + * @param slave the last seen slave data. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t storeLastData(SymbolString& master, SymbolString& slave); + + /** + * Store last seen master or slave data. + * @param partType the @a PartType of the data. + * @param data the last seen data. + * @param index the index of the part to store. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); + + /** + * Decode the value from the last stored data. + * @param partType the @a PartType of the data. + * @param output the @a ostringstream to append the formatted value to. + * @param outputFormat the @a OutputFormat options to use. + * @param leadingSeparator whether to prepend a separator before the formatted value. + * @param fieldName the optional name of a field to limit the output to. + * @param fieldIndex the optional index of the named field to limit the output to, or -1. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t decodeLastData(const PartType partType, + ostringstream& output, OutputFormat outputFormat = 0, + bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); + + /** + * Decode the value from the last stored data. + * @param output the @a ostringstream to append the formatted value to. + * @param outputFormat the @a OutputFormat options to use. + * @param leadingSeparator whether to prepend a separator before the formatted value. + * @param fieldName the optional name of a field to limit the output to. + * @param fieldIndex the optional index of the named field to limit the output to, or -1. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t decodeLastData(ostringstream& output, OutputFormat outputFormat = 0, + bool leadingSeparator = false, const char* fieldName = NULL, signed char fieldIndex = -1); + + /** + * Decode a particular numeric field value from the last stored data. + * @param output the variable in which to store the value. + * @param fieldName the name of the field to decode, or NULL for the first field. + * @param fieldIndex the optional index of the named field, or -1. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t decodeLastDataNumField(unsigned int& output, const char* fieldName, signed char fieldIndex = -1); + + /** + * Get the last seen master data. + * @return the last seen master @a SymbolString. + */ + SymbolString& getLastMasterData() { return m_lastMasterData; } + + /** + * Get the last seen slave data. + * @return the last seen slave @a SymbolString. + */ + SymbolString& getLastSlaveData() { return m_lastSlaveData; } + + /** + * Get the time when this message was last seen with reasonable data. + * @return the time when this message was last seen, or 0. + */ + time_t getLastUpdateTime() { return m_lastUpdateTime; } + + /** + * Get the time when the message data was last changed. + * @return the time when the message data was last changed, or 0 if this message was not decoded yet. + */ + time_t getLastChangeTime() { return m_lastChangeTime; } + + /** + * Get the time when this message was last polled for. + * @return the time when this message was last polled for, or 0 for never. + */ + time_t getLastPollTime() { return m_lastPollTime; } + + /** + * Return whether this @a Message needs to be polled after the other one. + * @param other the other @a Message to compare with. + * @return true if this @a Message needs to be polled after the other one. + */ + bool isLessPollWeight(const Message* other); + + /** + * Write the message definition or parts of it to the @a ostream. + * @param output the @a ostream to append the formatted value to. + * @param columns the list of column indexes to write, or NULL for all (see @p COLUMN_TYPE index constants). + * @param withConditions whether to include the optional conditions prefix. + */ + void dump(ostream& output, vector* columns = NULL, bool withConditions = false); + + /** + * Write the specified column to the @a ostream. + * @param output the @a ostream to append the formatted value to. + * @param column the column index to write (see @p COLUMN_TYPE index constants). + * @param withConditions whether to include the optional conditions prefix. + */ + virtual void dumpColumn(ostream& output, size_t column, bool withConditions = false); + + + protected: + /** the optional circuit name. */ + const string m_circuit; + + /** the message name (unique within the same circuit and type). */ + const string m_name; + + /** whether this is a write message. */ + const bool m_isWrite; + + /** true if message can only be initiated by a participant other than us, + * false if message can be initiated by any participant. */ + const bool m_isPassive; + + /** the comment. */ + const string m_comment; + + /** the source address, or @a SYN for any (only relevant if passive). */ + const unsigned char m_srcAddress; + + /** the destination address, or @a SYN for any (only for temporary scan messages). */ + const unsigned char m_dstAddress; + + /** the primary, secondary, and optionally further command ID bytes. */ + vector m_id; + + /** + * the key for storing in @a MessageMap. + *
    + *
  • byte 7: + *
      + *
    • bits 5-7: length of ID bytes (without PB/SB)
    • + *
    • bits 0-4: + *
        + *
      • master number (1..25) of sender for passive message
      • + *
      • 0x00 for passive message with any sender
      • + *
      • 0x1f for active write
      • + *
      • 0x1e for active read
      • + *
      + *
    + *
  • + *
  • byte 6: ZZ or SYN for any
  • + *
  • byte 5: PB
  • + *
  • byte 4: SB
  • + *
  • bytes 3-0: ID bytes (with cyclic xor if more than 4)
  • + *
+ */ + uint64_t m_key; + + /** the @a DataField for encoding/decoding the message. */ + DataField* m_data; + + /** whether to delete the @a DataField during destruction. */ + const bool m_deleteData; + + /** the priority for polling, or 0 for no polling at all. */ + unsigned char m_pollPriority; + + /** whether this message is used by a @a Condition. */ + bool m_usedByCondition; + + /** whether this is a special scanning @a Message instance. */ + bool m_isScanMessage; + + /** the @a Condition for this message, or NULL. */ + Condition* m_condition; + + /** the last seen master data. */ + SymbolString m_lastMasterData; + + /** the last seen slave data. */ + SymbolString m_lastSlaveData; + + /** the system time when the message was last updated, 0 for never. */ + time_t m_lastUpdateTime; + + /** the system time when the message content was last changed, 0 for never. */ + time_t m_lastChangeTime; + + /** the number of times this messages was already polled for. */ + unsigned int m_pollCount; + + /** the system time when this message was last polled for, 0 for never. */ + time_t m_lastPollTime; }; @@ -618,89 +618,89 @@ class Message { * A chained @a Message that needs more than one read/write on the bus to collect/send the data. */ class ChainedMessage : public Message { - public: - /** - * Construct a new instance. - * @param circuit the optional circuit name. - * @param name the message name (unique within the same circuit and type). - * @param isWrite whether this is a write message. - * @param comment the comment. - * @param srcAddress the source address, or @a SYN for any (only relevant if passive). - * @param dstAddress the destination address, or @a SYN for any (set later). - * @param id the primary, secondary, and optional further ID bytes common to each part of the chain. - * @param ids the primary, secondary, and optional further ID bytes for each part of the chain. - * @param lengths the data length for each part of the chain. - * @param data the @a DataField for encoding/decoding the chained message. - * @param deleteData whether to delete the @a DataField during destruction. - * @param pollPriority the priority for polling, or 0 for no polling at all. - * @param condition the @a Condition for this message, or NULL. - */ - ChainedMessage(const string circuit, const string name, - const bool isWrite, const string comment, - const unsigned char srcAddress, const unsigned char dstAddress, - const vector id, - vector< vector > ids, vector lengths, - DataField* data, const bool deleteData, - const unsigned char pollPriority, - Condition* condition = NULL); + public: + /** + * Construct a new instance. + * @param circuit the optional circuit name. + * @param name the message name (unique within the same circuit and type). + * @param isWrite whether this is a write message. + * @param comment the comment. + * @param srcAddress the source address, or @a SYN for any (only relevant if passive). + * @param dstAddress the destination address, or @a SYN for any (set later). + * @param id the primary, secondary, and optional further ID bytes common to each part of the chain. + * @param ids the primary, secondary, and optional further ID bytes for each part of the chain. + * @param lengths the data length for each part of the chain. + * @param data the @a DataField for encoding/decoding the chained message. + * @param deleteData whether to delete the @a DataField during destruction. + * @param pollPriority the priority for polling, or 0 for no polling at all. + * @param condition the @a Condition for this message, or NULL. + */ + ChainedMessage(const string circuit, const string name, + const bool isWrite, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, + vector< vector > ids, vector lengths, + DataField* data, const bool deleteData, + const unsigned char pollPriority, + Condition* condition = NULL); - virtual ~ChainedMessage(); + virtual ~ChainedMessage(); - // @copydoc - virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress = SYN, const string circuit = ""); + // @copydoc + virtual Message* derive(const unsigned char dstAddress, const unsigned char srcAddress = SYN, const string circuit = ""); - // @copydoc - virtual unsigned char getIdLength() const { return (unsigned char)(m_ids[0].size() - 2); } + // @copydoc + virtual unsigned char getIdLength() const { return (unsigned char)(m_ids[0].size() - 2); } - // @copydoc - virtual bool checkId(SymbolString& master, unsigned char* index = NULL); + // @copydoc + virtual bool checkId(SymbolString& master, unsigned char* index = NULL); - // @copydoc - virtual bool checkId(Message& other); + // @copydoc + virtual bool checkId(Message& other); - // @copydoc - virtual unsigned char getCount() { return (unsigned char)m_ids.size(); } + // @copydoc + virtual unsigned char getCount() { return (unsigned char)m_ids.size(); } - protected: - // @copydoc - virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); + protected: + // @copydoc + virtual result_t prepareMasterPart(SymbolString& master, istringstream& input, char separator, unsigned char index); - public: - // @copydoc - virtual result_t storeLastData(SymbolString& master, SymbolString& slave); + public: + // @copydoc + virtual result_t storeLastData(SymbolString& master, SymbolString& slave); - // @copydoc - virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); + // @copydoc + virtual result_t storeLastData(const PartType partType, SymbolString& data, unsigned char index); - protected: - // @copydoc - virtual void dumpColumn(ostream& output, size_t column, bool withConditions = false); + protected: + // @copydoc + virtual void dumpColumn(ostream& output, size_t column, bool withConditions = false); - private: - /** the primary, secondary, and optional further ID bytes for each part of the chain. */ - const vector< vector > m_ids; + private: + /** the primary, secondary, and optional further ID bytes for each part of the chain. */ + const vector< vector > m_ids; - /** the data length for each part of the chain. */ - const vector m_lengths; + /** the data length for each part of the chain. */ + const vector m_lengths; - /** the maximum allowed time difference of any data pair. */ - const time_t m_maxTimeDiff; + /** the maximum allowed time difference of any data pair. */ + const time_t m_maxTimeDiff; - /** array of the last seen master datas. */ - SymbolString** m_lastMasterDatas; + /** array of the last seen master datas. */ + SymbolString** m_lastMasterDatas; - /** array of the last seen slave datas. */ - SymbolString** m_lastSlaveDatas; + /** array of the last seen slave datas. */ + SymbolString** m_lastSlaveDatas; - /** array of the system times when the corresponding master data was last updated, 0 for never. */ - time_t* m_lastMasterUpdateTimes; + /** array of the system times when the corresponding master data was last updated, 0 for never. */ + time_t* m_lastMasterUpdateTimes; - /** array of the system times when the corresponding slave data was last updated, 0 for never. */ - time_t* m_lastSlaveUpdateTimes; + /** array of the system times when the corresponding slave data was last updated, 0 for never. */ + time_t* m_lastSlaveUpdateTimes; }; @@ -708,13 +708,13 @@ class ChainedMessage : public Message { * A function that compares the weighted poll priority of two @a Message instances. */ struct compareMessagePriority : binary_function { - /** - * Compare the weighted poll priority of the two @a Message instances. - * @param x the first @a Message. - * @param y the second @a Message. - * @return whether @a x is smaller than @a y with regard to their weighted poll priority. - */ - bool operator() (Message* x, Message* y) const { return x->isLessPollWeight(y); } + /** + * Compare the weighted poll priority of the two @a Message instances. + * @param x the first @a Message. + * @param y the second @a Message. + * @return whether @a x is smaller than @a y with regard to their weighted poll priority. + */ + bool operator() (Message* x, Message* y) const { return x->isLessPollWeight(y); } }; @@ -722,21 +722,21 @@ struct compareMessagePriority : binary_function { * Helper class extending @a priority_queue to hold distinct values only. */ class MessagePriorityQueue - : public priority_queue, compareMessagePriority> { - public: - /** - * Add data to the queue and ensure it is contained only once. - * @param __x the element to add. - */ - void push(const value_type& __x) { - for (vector::iterator it = c.begin(); it != c.end(); it++) { - if (*it == __x) { - c.erase(it); - break; - } - } - priority_queue, compareMessagePriority>::push(__x); - } + : public priority_queue, compareMessagePriority> { + public: + /** + * Add data to the queue and ensure it is contained only once. + * @param __x the element to add. + */ + void push(const value_type& __x) { + for (vector::iterator it = c.begin(); it != c.end(); it++) { + if (*it == __x) { + c.erase(it); + break; + } + } + priority_queue, compareMessagePriority>::push(__x); + } }; @@ -744,73 +744,73 @@ class MessagePriorityQueue * An abstract condition based on the value of one or more @a Message instances. */ class Condition { - public: - /** - * Construct a new instance. - */ - Condition() - : m_lastCheckTime(0), m_isTrue(false) { } + public: + /** + * Construct a new instance. + */ + Condition() + : m_lastCheckTime(0), m_isTrue(false) { } - /** - * Destructor. - */ - virtual ~Condition() { } + /** + * Destructor. + */ + virtual ~Condition() { } - /** - * Factory method for creating a new instance. - * @param condName the name of the condition. - * @param it the iterator to traverse for the definition parts. - * @param end the iterator pointing to the end of the definition parts. - * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. - * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. - * @param returnValue the variable in which to store the created instance. - * @return @a RESULT_OK on success, or an error code. - */ - static result_t create(const string condName, vector::iterator& it, const vector::iterator end, string defaultDest, string defaultCircuit, SimpleCondition*& returnValue); + /** + * Factory method for creating a new instance. + * @param condName the name of the condition. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. + * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. + * @param returnValue the variable in which to store the created instance. + * @return @a RESULT_OK on success, or an error code. + */ + static result_t create(const string condName, vector::iterator& it, const vector::iterator end, string defaultDest, string defaultCircuit, SimpleCondition*& returnValue); - /** - * Derive a new @a SimpleCondition from this condition. - * @param valueList the @a string with the new list of values. - * @return the derived @a SimpleCondition instance, or NULL if the value list is invalid. - */ - virtual SimpleCondition* derive(string valueList) { return NULL; } + /** + * Derive a new @a SimpleCondition from this condition. + * @param valueList the @a string with the new list of values. + * @return the derived @a SimpleCondition instance, or NULL if the value list is invalid. + */ + virtual SimpleCondition* derive(string valueList) { return NULL; } - /** - * Write the condition definition or resolved expression to the @a ostream. - * @param output the @a ostream to append to. - * @param matched true for dumping the matched value if the condition is true, false for dumping the definition. - */ - virtual void dump(ostream& output, bool matched = false) = 0; + /** + * Write the condition definition or resolved expression to the @a ostream. + * @param output the @a ostream to append to. + * @param matched true for dumping the matched value if the condition is true, false for dumping the definition. + */ + virtual void dump(ostream& output, bool matched = false) = 0; - /** - * Combine this condition with another instance using a logical and. - * @param other the @a Condition to combine with. - * @return the @a CombinedCondition instance. - */ - virtual CombinedCondition* combineAnd(Condition* other) = 0; + /** + * Combine this condition with another instance using a logical and. + * @param other the @a Condition to combine with. + * @return the @a CombinedCondition instance. + */ + virtual CombinedCondition* combineAnd(Condition* other) = 0; - /** - * Resolve the referred @a Message instance(s) and field index(es). - * @param messages the @a MessageMap instance for resolving. - * @param errorMessage a @a ostringstream to which to add optional error messages. - * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL) = 0; + /** + * Resolve the referred @a Message instance(s) and field index(es). + * @param messages the @a MessageMap instance for resolving. + * @param errorMessage a @a ostringstream to which to add optional error messages. + * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL) = 0; - /** - * Check and return whether this condition is fulfilled. - * @return whether this condition is fulfilled. - */ - virtual bool isTrue() = 0; + /** + * Check and return whether this condition is fulfilled. + * @return whether this condition is fulfilled. + */ + virtual bool isTrue() = 0; - protected: - /** the system time when the condition was last checked, 0 for never. */ - time_t m_lastCheckTime; + protected: + /** the system time when the condition was last checked, 0 for never. */ + time_t m_lastCheckTime; - /** whether the condition was @a true during the last check. */ - bool m_isTrue; + /** whether the condition was @a true during the last check. */ + bool m_isTrue; }; @@ -818,85 +818,85 @@ class Condition { * A simple @a Condition based on the value of one @a Message. */ class SimpleCondition : public Condition { - public: - /** - * Construct a new instance. - * @param condName the name of the condition. - * @param refName the reference name for dumping. - * @param circuit the circuit name. - * @param name the message name, or empty for scan message. - * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). - * @param field the field name. - * @param hasValues whether a value has to be checked against. - */ - SimpleCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const bool hasValues = false) - : Condition(), - m_condName(condName), m_refName(refName), m_circuit(circuit), m_name(name), m_dstAddress(dstAddress), m_field(field), m_hasValues(hasValues), m_message(NULL) { } + public: + /** + * Construct a new instance. + * @param condName the name of the condition. + * @param refName the reference name for dumping. + * @param circuit the circuit name. + * @param name the message name, or empty for scan message. + * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). + * @param field the field name. + * @param hasValues whether a value has to be checked against. + */ + SimpleCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const bool hasValues = false) + : Condition(), + m_condName(condName), m_refName(refName), m_circuit(circuit), m_name(name), m_dstAddress(dstAddress), m_field(field), m_hasValues(hasValues), m_message(NULL) { } - /** - * Destructor. - */ - virtual ~SimpleCondition() {} + /** + * Destructor. + */ + virtual ~SimpleCondition() {} - // @copydoc - virtual SimpleCondition* derive(string valueList); + // @copydoc + virtual SimpleCondition* derive(string valueList); - // @copydoc - virtual void dump(ostream& output, bool matched = false); + // @copydoc + virtual void dump(ostream& output, bool matched = false); - // @copydoc - virtual CombinedCondition* combineAnd(Condition* other); + // @copydoc + virtual CombinedCondition* combineAnd(Condition* other); - // @copydoc - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL); + // @copydoc + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL); - // @copydoc - virtual bool isTrue(); + // @copydoc + virtual bool isTrue(); - /** - * Return whether the condition is based on a numeric value. - * @return whether the condition is based on a numeric value. - */ - virtual bool isNumeric() { return true; } + /** + * Return whether the condition is based on a numeric value. + * @return whether the condition is based on a numeric value. + */ + virtual bool isNumeric() { return true; } - protected: - /** - * Check the values against the field in the @a Message. - * @param message the @a Message to check against. - * @param field the field name to check against, or empty for first field. - * @return whether the field matches one of the valid values. - */ - virtual bool checkValue(Message* message, const string field) { return true; } + protected: + /** + * Check the values against the field in the @a Message. + * @param message the @a Message to check against. + * @param field the field name to check against, or empty for first field. + * @return whether the field matches one of the valid values. + */ + virtual bool checkValue(Message* message, const string field) { return true; } - /** the value that matched in @a checkValue. */ - string m_matchedValue; + /** the value that matched in @a checkValue. */ + string m_matchedValue; - private: - /** the condition name. */ - const string m_condName; + private: + /** the condition name. */ + const string m_condName; - /** the reference name for dumping. */ - const string m_refName; + /** the reference name for dumping. */ + const string m_refName; - /** the circuit name. */ - const string m_circuit; + /** the circuit name. */ + const string m_circuit; - /** the message name, or empty for scan message. */ - const string m_name; + /** the message name, or empty for scan message. */ + const string m_name; - /** the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). */ - const unsigned char m_dstAddress; + /** the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). */ + const unsigned char m_dstAddress; - /** the field name, or empty for first field. */ - const string m_field; + /** the field name, or empty for first field. */ + const string m_field; - /** whether a value has to be checked against. */ - const bool m_hasValues; + /** whether a value has to be checked against. */ + const bool m_hasValues; - /** the resolved @a Message instance, or NULL. */ - Message* m_message; + /** the resolved @a Message instance, or NULL. */ + Message* m_message; }; @@ -904,35 +904,35 @@ class SimpleCondition : public Condition { * A simple @a Condition based on the numeric value of one @a Message. */ class SimpleNumericCondition : public SimpleCondition { - public: - /** - * Construct a new instance. - * @param condName the name of the condition. - * @param refName the reference name for dumping. - * @param circuit the circuit name. - * @param name the message name, or empty for scan message. - * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). - * @param field the field name. - * @param valueRanges the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. - */ - SimpleNumericCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const vector valueRanges) - : SimpleCondition(condName, refName, circuit, name, dstAddress, field, true), - m_valueRanges(valueRanges) { } + public: + /** + * Construct a new instance. + * @param condName the name of the condition. + * @param refName the reference name for dumping. + * @param circuit the circuit name. + * @param name the message name, or empty for scan message. + * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). + * @param field the field name. + * @param valueRanges the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. + */ + SimpleNumericCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const vector valueRanges) + : SimpleCondition(condName, refName, circuit, name, dstAddress, field, true), + m_valueRanges(valueRanges) { } - /** - * Destructor. - */ - virtual ~SimpleNumericCondition() {} + /** + * Destructor. + */ + virtual ~SimpleNumericCondition() {} - protected: - // @copydoc - virtual bool checkValue(Message* message, const string field); + protected: + // @copydoc + virtual bool checkValue(Message* message, const string field); - private: - /** the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. */ - const vector m_valueRanges; + private: + /** the valid value ranges (pairs of from/to inclusive), empty for @a m_message seen check. */ + const vector m_valueRanges; }; @@ -940,38 +940,38 @@ class SimpleNumericCondition : public SimpleCondition { * A simple @a Condition based on the string value of one @a Message. */ class SimpleStringCondition : public SimpleCondition { - public: - /** - * Construct a new instance. - * @param condName the name of the condition. - * @param refName the reference name for dumping. - * @param circuit the circuit name. - * @param name the message name, or empty for scan message. - * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). - * @param field the field name. - * @param values the valid values. - */ - SimpleStringCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const vector values) - : SimpleCondition(condName, refName, circuit, name, dstAddress, field, true), - m_values(values) { } + public: + /** + * Construct a new instance. + * @param condName the name of the condition. + * @param refName the reference name for dumping. + * @param circuit the circuit name. + * @param name the message name, or empty for scan message. + * @param dstAddress the override destination address, or @a SYN (only for @a Message without specific destination as well as scan message). + * @param field the field name. + * @param values the valid values. + */ + SimpleStringCondition(const string condName, const string refName, const string circuit, const string name, const unsigned char dstAddress, const string field, const vector values) + : SimpleCondition(condName, refName, circuit, name, dstAddress, field, true), + m_values(values) { } - /** - * Destructor. - */ - virtual ~SimpleStringCondition() {} + /** + * Destructor. + */ + virtual ~SimpleStringCondition() {} - // @copydoc - virtual bool isNumeric() { return false; } + // @copydoc + virtual bool isNumeric() { return false; } - protected: - // @copydoc - virtual bool checkValue(Message* message, const string field); + protected: + // @copydoc + virtual bool checkValue(Message* message, const string field); - private: - /** the valid values. */ - const vector m_values; + private: + /** the valid values. */ + const vector m_values; }; @@ -979,34 +979,34 @@ class SimpleStringCondition : public SimpleCondition { * A @a Condition combining two or more @a SimpleCondition instances with a logical and. */ class CombinedCondition : public Condition { - public: - /** - * Construct a new instance. - */ - CombinedCondition() - : Condition() { } + public: + /** + * Construct a new instance. + */ + CombinedCondition() + : Condition() { } - /** - * Destructor. - */ - virtual ~CombinedCondition() {} + /** + * Destructor. + */ + virtual ~CombinedCondition() {} - // @copydoc - virtual void dump(ostream& output, bool matched = false); + // @copydoc + virtual void dump(ostream& output, bool matched = false); - // @copydoc - virtual CombinedCondition* combineAnd(Condition* other) { m_conditions.push_back(other); return this; } + // @copydoc + virtual CombinedCondition* combineAnd(Condition* other) { m_conditions.push_back(other); return this; } - // @copydoc - virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL); + // @copydoc + virtual result_t resolve(MessageMap* messages, ostringstream& errorMessage, void (*readMessageFunc)(Message* message) = NULL); - // @copydoc - virtual bool isTrue(); + // @copydoc + virtual bool isTrue(); - private: - /** the @a Condition instances used. */ - vector m_conditions; + private: + /** the @a Condition instances used. */ + vector m_conditions; }; @@ -1014,84 +1014,84 @@ class CombinedCondition : public Condition { * An abstract instruction based on the value of one or more @a Message instances. */ class Instruction { - public: - /** - * Construct a new instance. - * @param condition the @a Condition this instruction requires, or null. - * @param singleton whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. - * @param defaultDest the default destination address, or empty. - * @param defaultCircuit the default circuit name, or empty. - * @param defaultSuffix the default circuit name suffix (starting with a "."), or empty. - */ - Instruction(Condition* condition, const bool singleton, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix) - : m_condition(condition), m_singleton(singleton), m_defaultDest(defaultDest), m_defaultCircuit(defaultCircuit), m_defaultSuffix(defaultSuffix) { } + public: + /** + * Construct a new instance. + * @param condition the @a Condition this instruction requires, or null. + * @param singleton whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. + * @param defaultDest the default destination address, or empty. + * @param defaultCircuit the default circuit name, or empty. + * @param defaultSuffix the default circuit name suffix (starting with a "."), or empty. + */ + Instruction(Condition* condition, const bool singleton, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix) + : m_condition(condition), m_singleton(singleton), m_defaultDest(defaultDest), m_defaultCircuit(defaultCircuit), m_defaultSuffix(defaultSuffix) { } - /** - * Destructor. - */ - virtual ~Instruction() { } + /** + * Destructor. + */ + virtual ~Instruction() { } - /** - * Factory method for creating a new instance. - * @param contextPath the path and/or filename context being loaded. - * @param defaultDest the default destination address, or empty. - * @param defaultCircuit the default circuit name, or empty. - * @param defaultSuffix the default circuit name suffix (starting with a "."), or empty. - * @param condition the @a Condition for the instruction, or NULL. - * @param type the type of the instruction. - * @param it the iterator to traverse for the definition parts. - * @param end the iterator pointing to the end of the definition parts. - * @param returnValue the variable in which to store the created instance. - * @return @a RESULT_OK on success, or an error code. - */ - static result_t create(const string contextPath, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - Condition* condition, const string type, vector::iterator& it, const vector::iterator end, Instruction*& returnValue); + /** + * Factory method for creating a new instance. + * @param contextPath the path and/or filename context being loaded. + * @param defaultDest the default destination address, or empty. + * @param defaultCircuit the default circuit name, or empty. + * @param defaultSuffix the default circuit name suffix (starting with a "."), or empty. + * @param condition the @a Condition for the instruction, or NULL. + * @param type the type of the instruction. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + * @param returnValue the variable in which to store the created instance. + * @return @a RESULT_OK on success, or an error code. + */ + static result_t create(const string contextPath, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + Condition* condition, const string type, vector::iterator& it, const vector::iterator end, Instruction*& returnValue); - /** - * Return the @a Condition this instruction requires. - * @return the @a Condition this instruction requires, or null. - */ - Condition* getCondition() { return m_condition; } + /** + * Return the @a Condition this instruction requires. + * @return the @a Condition this instruction requires, or null. + */ + Condition* getCondition() { return m_condition; } - /** - * Return whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. - * @return whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. - */ - bool isSingleton() { return m_singleton; } + /** + * Return whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. + * @return whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. + */ + bool isSingleton() { return m_singleton; } - /** - * Return a string describing the destination from the stored default values. - * @return a string describing the destination. - */ - string getDestination(); + /** + * Return a string describing the destination from the stored default values. + * @return a string describing the destination. + */ + string getDestination(); - /** - * Execute the instruction. - * @param messages the @a MessageMap. - * @param log the @a ostringstream to log success messages to (if necessary). - * @param condition the @a Condition that was successfully evaluated for execution, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t execute(MessageMap* messages, ostringstream& log, Condition* condition) = 0; + /** + * Execute the instruction. + * @param messages the @a MessageMap. + * @param log the @a ostringstream to log success messages to (if necessary). + * @param condition the @a Condition that was successfully evaluated for execution, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t execute(MessageMap* messages, ostringstream& log, Condition* condition) = 0; - private: - /** the @a Condition this instruction requires, or null. */ - Condition* m_condition; + private: + /** the @a Condition this instruction requires, or null. */ + Condition* m_condition; - /** whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. */ - bool m_singleton; + /** whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. */ + bool m_singleton; - protected: - /** the default destination address, or empty. */ - const string m_defaultDest; + protected: + /** the default destination address, or empty. */ + const string m_defaultDest; - /** the default circuit name, or empty. */ - const string m_defaultCircuit; + /** the default circuit name, or empty. */ + const string m_defaultCircuit; - /** the default circuit name suffix (starting with a "."), or empty. */ - const string m_defaultSuffix; + /** the default circuit name suffix (starting with a "."), or empty. */ + const string m_defaultSuffix; }; @@ -1099,31 +1099,31 @@ class Instruction { * An @a Instruction allowing to load another file. */ class LoadInstruction : public Instruction { - public: - /** - * Construct a new instance. - * @param condition the @a Condition this instruction requires, or null. - * @param singleton whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. - * @param defaultDest the default destination address (may be overwritten by file name), or empty. - * @param defaultCircuit the default circuit name (may be overwritten by file name), or empty. - * @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name), or empty. - * @param filename the name of the file to load. - */ - LoadInstruction(Condition* condition, const bool singleton, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, const string filename) - : Instruction(condition, singleton, defaultDest, defaultCircuit, defaultSuffix), m_filename(filename) { } + public: + /** + * Construct a new instance. + * @param condition the @a Condition this instruction requires, or null. + * @param singleton whether this @a Instruction belongs to a set of instructions of which only the first one may be executed for the same source file. + * @param defaultDest the default destination address (may be overwritten by file name), or empty. + * @param defaultCircuit the default circuit name (may be overwritten by file name), or empty. + * @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name), or empty. + * @param filename the name of the file to load. + */ + LoadInstruction(Condition* condition, const bool singleton, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, const string filename) + : Instruction(condition, singleton, defaultDest, defaultCircuit, defaultSuffix), m_filename(filename) { } - /** - * Destructor. - */ - virtual ~LoadInstruction() { } + /** + * Destructor. + */ + virtual ~LoadInstruction() { } - // @copydoc - virtual result_t execute(MessageMap* messages, ostringstream& log, Condition* condition); + // @copydoc + virtual result_t execute(MessageMap* messages, ostringstream& log, Condition* condition); - private: - /** the name of the file to load. */ - const string m_filename; + private: + /** the name of the file to load. */ + const string m_filename; }; @@ -1131,255 +1131,255 @@ class LoadInstruction : public Instruction { * Holds a map of all known @a Message instances. */ class MessageMap : public FileReader { - public: - /** - * Construct a new instance. - * @param addAll whether to add all messages, even if duplicate. - */ - explicit MessageMap(const bool addAll = false) : FileReader::FileReader(true), - m_addAll(addAll), m_maxIdLength(0), m_messageCount(0), m_conditionalMessageCount(0), m_passiveMessageCount(0) { - m_scanMessage = Message::createScanMessage(); - } + public: + /** + * Construct a new instance. + * @param addAll whether to add all messages, even if duplicate. + */ + explicit MessageMap(const bool addAll = false) : FileReader::FileReader(true), + m_addAll(addAll), m_maxIdLength(0), m_messageCount(0), m_conditionalMessageCount(0), m_passiveMessageCount(0) { + m_scanMessage = Message::createScanMessage(); + } - /** - * Destructor. - */ - virtual ~MessageMap() { - clear(); - delete m_scanMessage; - } + /** + * Destructor. + */ + virtual ~MessageMap() { + clear(); + delete m_scanMessage; + } - /** - * Add a @a Message instance to this set. - * @param message the @a Message instance to add. - * @param storeByName whether to store the @a Message by name. - * @return @a RESULT_OK on success, or an error code. - * Note: the caller may not free the added instance on success. - */ - result_t add(Message* message, bool storeByName = true); + /** + * Add a @a Message instance to this set. + * @param message the @a Message instance to add. + * @param storeByName whether to store the @a Message by name. + * @return @a RESULT_OK on success, or an error code. + * Note: the caller may not free the added instance on success. + */ + result_t add(Message* message, bool storeByName = true); - // @copydoc - virtual result_t addDefaultFromFile(vector< vector >& defaults, vector& row, - vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, - const string& filename, unsigned int lineNo); + // @copydoc + virtual result_t addDefaultFromFile(vector< vector >& defaults, vector& row, + vector::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, + const string& filename, unsigned int lineNo); - /** - * Read the @a Condition instance(s) from the types field. - * @param types the field from which to read the @a Condition instance(s). - * @param filename the name of the file being read. - * @param condition the variable in which to store the result. - * @return @a RESULT_OK on success, or an error code. - */ - result_t readConditions(string& types, const string& filename, Condition*& condition); + /** + * Read the @a Condition instance(s) from the types field. + * @param types the field from which to read the @a Condition instance(s). + * @param filename the name of the file being read. + * @param condition the variable in which to store the result. + * @return @a RESULT_OK on success, or an error code. + */ + result_t readConditions(string& types, const string& filename, Condition*& condition); - // @copydoc - virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, - vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, - const string& filename, unsigned int lineNo); + // @copydoc + virtual result_t addFromFile(vector::iterator& begin, const vector::iterator end, + vector< vector >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, + const string& filename, unsigned int lineNo); - /** - * Get the scan @a Message instance for the specified address. - * @param dstAddress the destination address, or @a SYN for the base scan @a Message. - * @return the scan @a Message instance, or NULL if the dstAddress is no slave. - */ - Message* getScanMessage(const unsigned char dstAddress = SYN); + /** + * Get the scan @a Message instance for the specified address. + * @param dstAddress the destination address, or @a SYN for the base scan @a Message. + * @return the scan @a Message instance, or NULL if the dstAddress is no slave. + */ + Message* getScanMessage(const unsigned char dstAddress = SYN); - /** - * Resolve all @a Condition instances. - * @param verbose whether to verbosely add all problems to the error message. - * @return @a RESULT_OK on success, or an error code. - */ - result_t resolveConditions(bool verbose = false); + /** + * Resolve all @a Condition instances. + * @param verbose whether to verbosely add all problems to the error message. + * @return @a RESULT_OK on success, or an error code. + */ + result_t resolveConditions(bool verbose = false); - /** - * Resolve a @a Condition. - * @param condition the @a Condition to resolve. - * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - result_t resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message) = NULL); + /** + * Resolve a @a Condition. + * @param condition the @a Condition to resolve. + * @param readMessageFunc the function to call for immediate reading of a @a Message from the bus, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + result_t resolveCondition(Condition* condition, void (*readMessageFunc)(Message* message) = NULL); - /** - * Run all executable @a Instruction instances. - * @param log the @a ostringstream to log success messages to (if necessary). - * @param readMessageFunc the function to call for immediate reading of a - * @a Message values from the bus required for singleton instructions, or NULL. - * @return @a RESULT_OK on success, or an error code. - */ - result_t executeInstructions(ostringstream& log, void (*readMessageFunc)(Message* message) = NULL); + /** + * Run all executable @a Instruction instances. + * @param log the @a ostringstream to log success messages to (if necessary). + * @param readMessageFunc the function to call for immediate reading of a + * @a Message values from the bus required for singleton instructions, or NULL. + * @return @a RESULT_OK on success, or an error code. + */ + result_t executeInstructions(ostringstream& log, void (*readMessageFunc)(Message* message) = NULL); - /** - * Add a loaded file to a participant. - * @param address the slave address. - * @param file the name of the file from which a configuration part was loaded for the participant. - * @param comment an optional comment. - */ - void addLoadedFile(unsigned char address, string file, string comment); + /** + * Add a loaded file to a participant. + * @param address the slave address. + * @param file the name of the file from which a configuration part was loaded for the participant. + * @param comment an optional comment. + */ + void addLoadedFile(unsigned char address, string file, string comment); - /** - * Get the loaded files for a participant. - * @param address the slave address. - * @return the name of the file(s) loaded for the participant (separated by comma and enclosed in double quotes), or empty. - */ - string getLoadedFiles(unsigned char address); + /** + * Get the loaded files for a participant. + * @param address the slave address. + * @return the name of the file(s) loaded for the participant (separated by comma and enclosed in double quotes), or empty. + */ + string getLoadedFiles(unsigned char address); - /** - * Get the stored @a Message instances for the key. - * @param key the key of the @a Message. - * @return the found @a Message instances, or NULL. - * Note: the caller may not free the returned instances. - */ - vector* getByKey(const uint64_t key); + /** + * Get the stored @a Message instances for the key. + * @param key the key of the @a Message. + * @return the found @a Message instances, or NULL. + * Note: the caller may not free the returned instances. + */ + vector* getByKey(const uint64_t key); - /** - * Find the @a Message instance for the specified circuit and name. - * @param circuit the optional circuit name. - * @param name the message name. - * @param isWrite whether this is a write message. - * @param isPassive whether this is a passive message. - * @return the @a Message instance, or NULL. - * Note: the caller may not free the returned instance. - */ - Message* find(const string& circuit, const string& name, const bool isWrite, const bool isPassive = false); + /** + * Find the @a Message instance for the specified circuit and name. + * @param circuit the optional circuit name. + * @param name the message name. + * @param isWrite whether this is a write message. + * @param isPassive whether this is a passive message. + * @return the @a Message instance, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* find(const string& circuit, const string& name, const bool isWrite, const bool isPassive = false); - /** - * Find all active get @a Message instances for the specified circuit and name. - * @param circuit the circuit name, or empty for any. - * @param name the message name, or empty for any. - * @param completeMatch false to also include messages where the circuit and name matches only a part of the given circuit and name (default true). - * @param withRead true to include read messages (default true). - * @param withWrite true to include write messages (default false). - * @param withPassive true to include passive messages (default false). - * @return the found @a Message instances. - * @param completeMatchIgnoreCircuitSuffix ignore different circuit suffixes (after "#") for completeMatch. - * @param onlyAvailable true to include only available messages (default true), false to also include messages that are currently not available (e.g. due to unresolved or false conditions). - * @param since the start time from which to add updates (inclusive, also removes messages with unset destination address), or 0 to ignore. - * @param until the end time to which to add updates (exclusive, also removes messages with unset destination address), or 0 to ignore. - * Note: the caller may not free the returned instances. - */ - deque findAll(const string& circuit, const string& name, const bool completeMatch = true, - const bool withRead = true, const bool withWrite = false, const bool withPassive = false, - const bool completeMatchIgnoreCircuitSuffix = false, const bool onlyAvailable = true, - const time_t since = 0, const time_t until = 0); + /** + * Find all active get @a Message instances for the specified circuit and name. + * @param circuit the circuit name, or empty for any. + * @param name the message name, or empty for any. + * @param completeMatch false to also include messages where the circuit and name matches only a part of the given circuit and name (default true). + * @param withRead true to include read messages (default true). + * @param withWrite true to include write messages (default false). + * @param withPassive true to include passive messages (default false). + * @return the found @a Message instances. + * @param completeMatchIgnoreCircuitSuffix ignore different circuit suffixes (after "#") for completeMatch. + * @param onlyAvailable true to include only available messages (default true), false to also include messages that are currently not available (e.g. due to unresolved or false conditions). + * @param since the start time from which to add updates (inclusive, also removes messages with unset destination address), or 0 to ignore. + * @param until the end time to which to add updates (exclusive, also removes messages with unset destination address), or 0 to ignore. + * Note: the caller may not free the returned instances. + */ + deque findAll(const string& circuit, const string& name, const bool completeMatch = true, + const bool withRead = true, const bool withWrite = false, const bool withPassive = false, + const bool completeMatchIgnoreCircuitSuffix = false, const bool onlyAvailable = true, + const time_t since = 0, const time_t until = 0); - /** - * Find the @a Message instance for the specified master data. - * @param master the master @a SymbolString for identifying the @a Message. - * @param anyDestination true to only return messages without a particular destination. - * @param withRead true to include read messages (default true). - * @param withWrite true to include write messages (default true). - * @param withPassive true to include passive messages (default true). - * @return the @a Message instance, or NULL. - * Note: the caller may not free the returned instance. - */ - Message* find(SymbolString& master, bool anyDestination = false, - const bool withRead = true, const bool withWrite = true, const bool withPassive = true); + /** + * Find the @a Message instance for the specified master data. + * @param master the master @a SymbolString for identifying the @a Message. + * @param anyDestination true to only return messages without a particular destination. + * @param withRead true to include read messages (default true). + * @param withWrite true to include write messages (default true). + * @param withPassive true to include passive messages (default true). + * @return the @a Message instance, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* find(SymbolString& master, bool anyDestination = false, + const bool withRead = true, const bool withWrite = true, const bool withPassive = true); - /** - * Invalidate cached data of the @a Message and all other instances with a matching name key. - * @param message the @a Message to invalidate. - */ - void invalidateCache(Message* message); + /** + * Invalidate cached data of the @a Message and all other instances with a matching name key. + * @param message the @a Message to invalidate. + */ + void invalidateCache(Message* message); - /** - * Add a @a Message to the list of instances to poll. - * @param message the @a Message to poll. - * @param toFront whether to add the @a Message to the very front of the poll queue. - */ - void addPollMessage(Message* message, bool toFront = false); + /** + * Add a @a Message to the list of instances to poll. + * @param message the @a Message to poll. + * @param toFront whether to add the @a Message to the very front of the poll queue. + */ + void addPollMessage(Message* message, bool toFront = false); - /** - * Removes all @a Message instances. - */ - void clear(); + /** + * Removes all @a Message instances. + */ + void clear(); - /** - * Get the number of all stored @a Message instances. - * @return the the number of all stored @a Message instances. - */ - size_t size() { return m_messageCount; } + /** + * Get the number of all stored @a Message instances. + * @return the the number of all stored @a Message instances. + */ + size_t size() { return m_messageCount; } - /** - * Get the number of stored conditional @a Message instances. - * @return the the number of stored conditional @a Message instances. - */ - size_t sizeConditional() { return m_conditionalMessageCount; } + /** + * Get the number of stored conditional @a Message instances. + * @return the the number of stored conditional @a Message instances. + */ + size_t sizeConditional() { return m_conditionalMessageCount; } - /** - * Get the number of stored passive @a Message instances. - * @return the the number of stored passive @a Message instances. - */ - size_t sizePassive() { return m_passiveMessageCount; } + /** + * Get the number of stored passive @a Message instances. + * @return the the number of stored passive @a Message instances. + */ + size_t sizePassive() { return m_passiveMessageCount; } - /** - * Get the number of stored @a Message instances with a poll priority. - * @return the the number of stored @a Message instances with a poll priority. - */ - size_t sizePoll() { return m_pollMessages.size(); } + /** + * Get the number of stored @a Message instances with a poll priority. + * @return the the number of stored @a Message instances with a poll priority. + */ + size_t sizePoll() { return m_pollMessages.size(); } - /** - * Get the next @a Message to poll. - * @return the next @a Message to poll, or NULL. - * Note: the caller may not free the returned instance. - */ - Message* getNextPoll(); + /** + * Get the next @a Message to poll. + * @return the next @a Message to poll, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* getNextPoll(); - /** - * Get the number of stored @a Condition instances. - * @return the number of stored @a Condition instances. - */ - size_t sizeConditions() { return m_conditions.size(); } + /** + * Get the number of stored @a Condition instances. + * @return the number of stored @a Condition instances. + */ + size_t sizeConditions() { return m_conditions.size(); } - /** - * Get the stored @a Condition instances. - * @return the @a Condition instances by filename and condition name. - */ - map& getConditions() { return m_conditions; } + /** + * Get the stored @a Condition instances. + * @return the @a Condition instances by filename and condition name. + */ + map& getConditions() { return m_conditions; } - /** - * Write the message definitions to the @a ostream. - * @param output the @a ostream to append the formatted messages to. - * @param withConditions whether to include the optional conditions prefix. - */ - void dump(ostream& output, bool withConditions = false); + /** + * Write the message definitions to the @a ostream. + * @param output the @a ostream to append the formatted messages to. + * @param withConditions whether to include the optional conditions prefix. + */ + void dump(ostream& output, bool withConditions = false); - private: - /** whether to add all messages, even if duplicate. */ - const bool m_addAll; + private: + /** whether to add all messages, even if duplicate. */ + const bool m_addAll; - /** the @a Message instance used for scanning. */ - Message* m_scanMessage; + /** the @a Message instance used for scanning. */ + Message* m_scanMessage; - /** the loaded configuration files by slave address. */ - map m_loadedFiles; + /** the loaded configuration files by slave address. */ + map m_loadedFiles; - /** the maximum ID length used by any of the known @a Message instances. */ - unsigned char m_maxIdLength; + /** the maximum ID length used by any of the known @a Message instances. */ + unsigned char m_maxIdLength; - /** the number of distinct @a Message instances stored in @a m_messagesByName. */ - size_t m_messageCount; + /** the number of distinct @a Message instances stored in @a m_messagesByName. */ + size_t m_messageCount; - /** the number of conditional @a Message instances part of @a m_messageCount. */ - size_t m_conditionalMessageCount; + /** the number of conditional @a Message instances part of @a m_messageCount. */ + size_t m_conditionalMessageCount; - /** the number of distinct passive @a Message instances stored in @a m_messagesByKey. */ - size_t m_passiveMessageCount; + /** the number of distinct passive @a Message instances stored in @a m_messagesByKey. */ + size_t m_passiveMessageCount; - /** the known @a Message instances by lowercase circuit and name. */ - map > m_messagesByName; + /** the known @a Message instances by lowercase circuit and name. */ + map > m_messagesByName; - /** the known @a Message instances by key. */ - map > m_messagesByKey; + /** the known @a Message instances by key. */ + map > m_messagesByKey; - /** the known @a Message instances to poll, by priority. */ - MessagePriorityQueue m_pollMessages; + /** the known @a Message instances to poll, by priority. */ + MessagePriorityQueue m_pollMessages; - /** the @a Condition instances by filename and condition name. */ - map m_conditions; + /** the @a Condition instances by filename and condition name. */ + map m_conditions; - /** the list of @a Instruction instances by filename. */ - map > m_instructions; + /** the list of @a Instruction instances by filename. */ + map > m_instructions; }; } // namespace ebusd diff --git a/src/lib/ebus/result.cpp b/src/lib/ebus/result.cpp index c2e8bc1e..ef6865e1 100644 --- a/src/lib/ebus/result.cpp +++ b/src/lib/ebus/result.cpp @@ -22,40 +22,40 @@ namespace ebusd { const char* getResultCode(result_t resultCode) { - switch (resultCode) { - case RESULT_OK: return "done"; - case RESULT_CONTINUE: return "continue"; - case RESULT_EMPTY: return "empty"; - case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error"; - case RESULT_ERR_DEVICE: return "ERR: generic device error"; - case RESULT_ERR_SEND: return "ERR: send error"; - case RESULT_ERR_ESC: return "ERR: invalid escape sequence"; - case RESULT_ERR_TIMEOUT: return "ERR: read timeout"; - case RESULT_ERR_NOTFOUND: return "ERR: element not found"; - case RESULT_ERR_EOF: return "ERR: end of input reached"; - case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument"; - case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument"; - case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address"; - case RESULT_ERR_INVALID_POS: return "ERR: invalid position"; - case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range"; - case RESULT_ERR_INVALID_PART: return "ERR: invalid part type"; - case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type"; - case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list"; - case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry"; - case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name"; - case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost"; - case RESULT_ERR_CRC: return "ERR: CRC error"; - case RESULT_ERR_ACK: return "ERR: ACK error"; - case RESULT_ERR_NAK: return "ERR: NAK received"; - case RESULT_ERR_NO_SIGNAL: return "ERR: no signal"; - case RESULT_ERR_SYN: return "ERR: SYN received"; + switch (resultCode) { + case RESULT_OK: return "done"; + case RESULT_CONTINUE: return "continue"; + case RESULT_EMPTY: return "empty"; + case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error"; + case RESULT_ERR_DEVICE: return "ERR: generic device error"; + case RESULT_ERR_SEND: return "ERR: send error"; + case RESULT_ERR_ESC: return "ERR: invalid escape sequence"; + case RESULT_ERR_TIMEOUT: return "ERR: read timeout"; + case RESULT_ERR_NOTFOUND: return "ERR: element not found"; + case RESULT_ERR_EOF: return "ERR: end of input reached"; + case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument"; + case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument"; + case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address"; + case RESULT_ERR_INVALID_POS: return "ERR: invalid position"; + case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range"; + case RESULT_ERR_INVALID_PART: return "ERR: invalid part type"; + case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type"; + case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list"; + case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry"; + case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name"; + case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost"; + case RESULT_ERR_CRC: return "ERR: CRC error"; + case RESULT_ERR_ACK: return "ERR: ACK error"; + case RESULT_ERR_NAK: return "ERR: NAK received"; + case RESULT_ERR_NO_SIGNAL: return "ERR: no signal"; + case RESULT_ERR_SYN: return "ERR: SYN received"; - default: - if (resultCode >= 0) { - return "done: unknown result code"; - } - return "ERR: unknown result code"; - } + default: + if (resultCode >= 0) { + return "done: unknown result code"; + } + return "ERR: unknown result code"; + } } } // namespace ebusd diff --git a/src/lib/ebus/result.h b/src/lib/ebus/result.h index 4ca373a7..4b3b51d3 100644 --- a/src/lib/ebus/result.h +++ b/src/lib/ebus/result.h @@ -31,37 +31,37 @@ namespace ebusd { /** type for result code. */ enum result_t { - RESULT_OK = 0, //!< success + RESULT_OK = 0, //!< success - RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received) - RESULT_EMPTY = 2, //!< empty result + RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received) + RESULT_EMPTY = 2, //!< empty result - RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal) - RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal) - RESULT_ERR_SEND = -3, //!< send error - RESULT_ERR_ESC = -4, //!< invalid escape sequence - RESULT_ERR_TIMEOUT = -5, //!< read timeout + RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal) + RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal) + RESULT_ERR_SEND = -3, //!< send error + RESULT_ERR_ESC = -4, //!< invalid escape sequence + RESULT_ERR_TIMEOUT = -5, //!< read timeout - RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable - RESULT_ERR_EOF = -7, //!< end of input reached - RESULT_ERR_INVALID_ARG = -8, //!< invalid argument - RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument - RESULT_ERR_INVALID_ADDR = -10, //!< invalid address - RESULT_ERR_INVALID_POS = -11, //!< invalid position - RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range - RESULT_ERR_INVALID_PART = -13, //!< invalid part type value - RESULT_ERR_MISSING_TYPE = -14, //!< missing data type - RESULT_ERR_INVALID_LIST = -15, //!< invalid value list - RESULT_ERR_DUPLICATE = -16, //!< duplicate entry - RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name) + RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable + RESULT_ERR_EOF = -7, //!< end of input reached + RESULT_ERR_INVALID_ARG = -8, //!< invalid argument + RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument + RESULT_ERR_INVALID_ADDR = -10, //!< invalid address + RESULT_ERR_INVALID_POS = -11, //!< invalid position + RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range + RESULT_ERR_INVALID_PART = -13, //!< invalid part type value + RESULT_ERR_MISSING_TYPE = -14, //!< missing data type + RESULT_ERR_INVALID_LIST = -15, //!< invalid value list + RESULT_ERR_DUPLICATE = -16, //!< duplicate entry + RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name) - RESULT_ERR_BUS_LOST = -18, //!< arbitration lost - RESULT_ERR_CRC = -19, //!< CRC error - RESULT_ERR_ACK = -20, //!< ACK error - RESULT_ERR_NAK = -21, //!< NAK received + RESULT_ERR_BUS_LOST = -18, //!< arbitration lost + RESULT_ERR_CRC = -19, //!< CRC error + RESULT_ERR_ACK = -20, //!< ACK error + RESULT_ERR_NAK = -21, //!< NAK received - RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus - RESULT_ERR_SYN = -23, //!< SYN received instead of answer + RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus + RESULT_ERR_SYN = -23, //!< SYN received instead of answer }; diff --git a/src/lib/ebus/symbol.cpp b/src/lib/ebus/symbol.cpp index cf76d060..2776f68d 100644 --- a/src/lib/ebus/symbol.cpp +++ b/src/lib/ebus/symbol.cpp @@ -35,160 +35,160 @@ using std::setfill; * CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1. */ static const unsigned char CRC_LOOKUP_TABLE[] = { - 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee, - 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc, - 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a, - 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8, - 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26, - 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14, - 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42, - 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70, - 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5, - 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7, - 0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81, - 0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3, - 0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d, - 0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f, - 0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49, - 0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b, + 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee, + 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc, + 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a, + 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8, + 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26, + 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14, + 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42, + 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70, + 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5, + 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7, + 0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81, + 0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3, + 0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d, + 0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f, + 0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49, + 0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b, }; void SymbolString::addAll(const SymbolString& str, bool skipLastSymbol) { - bool addCrc = m_unescapeState == 0; - bool isEscaped = str.m_unescapeState == 0; - vector data = str.m_data; - size_t end = data.size(); - if (end > 0 && skipLastSymbol) { - end--; - } - for (size_t i = 0; i < end; i++) { - push_back(data[i], isEscaped, addCrc); - } - if (addCrc) { - push_back(m_crc, false, false); // add CRC - } + bool addCrc = m_unescapeState == 0; + bool isEscaped = str.m_unescapeState == 0; + vector data = str.m_data; + size_t end = data.size(); + if (end > 0 && skipLastSymbol) { + end--; + } + for (size_t i = 0; i < end; i++) { + push_back(data[i], isEscaped, addCrc); + } + if (addCrc) { + push_back(m_crc, false, false); // add CRC + } } result_t SymbolString::parseHex(const string& str, const bool isEscaped) { - bool addCrc = m_unescapeState == 0; - for (size_t i = 0; i < str.size(); i += 2) { - char* strEnd = NULL; - const char* strBegin = str.substr(i, 2).c_str(); - unsigned long value = strtoul(strBegin, &strEnd, 16); + bool addCrc = m_unescapeState == 0; + for (size_t i = 0; i < str.size(); i += 2) { + char* strEnd = NULL; + const char* strBegin = str.substr(i, 2).c_str(); + unsigned long value = strtoul(strBegin, &strEnd, 16); - if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) { - return RESULT_ERR_INVALID_NUM; // invalid value - } - push_back((unsigned char)value, isEscaped, addCrc); - } - if (addCrc) { - push_back(m_crc, false, false); // add CRC - } - return RESULT_OK; + if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) { + return RESULT_ERR_INVALID_NUM; // invalid value + } + push_back((unsigned char)value, isEscaped, addCrc); + } + if (addCrc) { + push_back(m_crc, false, false); // add CRC + } + return RESULT_OK; } const string SymbolString::getDataStr(const bool unescape, const bool skipLastSymbol) { - ostringstream sstr; - bool previousEscape = false; + ostringstream sstr; + bool previousEscape = false; - for (size_t i = 0; i < m_data.size(); i++) { - unsigned char value = m_data[i]; - if (m_unescapeState == 0 && unescape && previousEscape) { - if (!skipLastSymbol || i+1 < m_data.size()) { - if (value == 0x00) { - sstr << "a9"; // ESC - } else if (value == 0x01) { - sstr << "aa"; // SYN - } else { - sstr << "XX"; // invalid escape sequence - } - } - previousEscape = false; - } else if (m_unescapeState == 0 && unescape && value == ESC) { - previousEscape = true; // escape sequence not yet finished - } else if (!skipLastSymbol || i+1 < m_data.size()) { - sstr << nouppercase << setw(2) << hex - << setfill('0') << static_cast(value); - } - } - return sstr.str(); + for (size_t i = 0; i < m_data.size(); i++) { + unsigned char value = m_data[i]; + if (m_unescapeState == 0 && unescape && previousEscape) { + if (!skipLastSymbol || i+1 < m_data.size()) { + if (value == 0x00) { + sstr << "a9"; // ESC + } else if (value == 0x01) { + sstr << "aa"; // SYN + } else { + sstr << "XX"; // invalid escape sequence + } + } + previousEscape = false; + } else if (m_unescapeState == 0 && unescape && value == ESC) { + previousEscape = true; // escape sequence not yet finished + } else if (!skipLastSymbol || i+1 < m_data.size()) { + sstr << nouppercase << setw(2) << hex + << setfill('0') << static_cast(value); + } + } + return sstr.str(); } result_t SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) { - if (m_unescapeState == 0) { // store escaped data - if (!isEscaped && value == ESC) { - m_data.push_back(ESC); - m_data.push_back(0x00); - if (updateCRC) { - addCRC(ESC); - addCRC(0x00); - } - } else if (!isEscaped && value == SYN) { - m_data.push_back(ESC); - m_data.push_back(0x01); - if (updateCRC) { - addCRC(ESC); - addCRC(0x01); - } - } else { - m_data.push_back(value); - if (updateCRC) { - addCRC(value); - } - } - return RESULT_OK; - } - if (!isEscaped) { - if (m_unescapeState != 1) { - return RESULT_ERR_ESC; // invalid unescape state - } - m_data.push_back(value); - if (updateCRC) { - if (value == ESC) { - addCRC(ESC); - addCRC(0x00); - } else if (value == SYN) { - addCRC(ESC); - addCRC(0x01); - } else { - addCRC(value); - } - } - return RESULT_OK; - } - if (m_unescapeState != 1) { - if (updateCRC) { - addCRC(value); - } - if (value == 0x00) { - m_data.push_back(ESC); - m_unescapeState = 1; - return RESULT_OK; - } - if (value == 0x01) { - m_data.push_back(SYN); - m_unescapeState = 1; - return RESULT_OK; - } - return RESULT_ERR_ESC; // invalid escape sequence - } - if (value == ESC) { - if (updateCRC) { - addCRC(value); - } - m_unescapeState = 2; - return RESULT_CONTINUE; - } - if (updateCRC) { - addCRC(value); - } - m_data.push_back(value); - return RESULT_OK; + if (m_unescapeState == 0) { // store escaped data + if (!isEscaped && value == ESC) { + m_data.push_back(ESC); + m_data.push_back(0x00); + if (updateCRC) { + addCRC(ESC); + addCRC(0x00); + } + } else if (!isEscaped && value == SYN) { + m_data.push_back(ESC); + m_data.push_back(0x01); + if (updateCRC) { + addCRC(ESC); + addCRC(0x01); + } + } else { + m_data.push_back(value); + if (updateCRC) { + addCRC(value); + } + } + return RESULT_OK; + } + if (!isEscaped) { + if (m_unescapeState != 1) { + return RESULT_ERR_ESC; // invalid unescape state + } + m_data.push_back(value); + if (updateCRC) { + if (value == ESC) { + addCRC(ESC); + addCRC(0x00); + } else if (value == SYN) { + addCRC(ESC); + addCRC(0x01); + } else { + addCRC(value); + } + } + return RESULT_OK; + } + if (m_unescapeState != 1) { + if (updateCRC) { + addCRC(value); + } + if (value == 0x00) { + m_data.push_back(ESC); + m_unescapeState = 1; + return RESULT_OK; + } + if (value == 0x01) { + m_data.push_back(SYN); + m_unescapeState = 1; + return RESULT_OK; + } + return RESULT_ERR_ESC; // invalid escape sequence + } + if (value == ESC) { + if (updateCRC) { + addCRC(value); + } + m_unescapeState = 2; + return RESULT_CONTINUE; + } + if (updateCRC) { + addCRC(value); + } + m_data.push_back(value); + return RESULT_OK; } void SymbolString::addCRC(const unsigned char value) { - m_crc = CRC_LOOKUP_TABLE[m_crc]^value; + m_crc = CRC_LOOKUP_TABLE[m_crc]^value; } @@ -199,66 +199,66 @@ void SymbolString::addCRC(const unsigned char value) { * @return the 1-based index of the upper or lower 4 bits of a master address (1 to 5), or 0. */ unsigned char getMasterPartIndex(unsigned char bits) { - switch (bits) { - case 0x0: - return 1; - case 0x1: - return 2; - case 0x3: - return 3; - case 0x7: - return 4; - case 0xF: - return 5; - default: - return 0; - } + switch (bits) { + case 0x0: + return 1; + case 0x1: + return 2; + case 0x3: + return 3; + case 0x7: + return 4; + case 0xF: + return 5; + default: + return 0; + } } bool isMaster(unsigned char addr) { - return getMasterPartIndex(addr & 0x0F) > 0 - && getMasterPartIndex((addr & 0xF0)>>4) > 0; + return getMasterPartIndex(addr & 0x0F) > 0 + && getMasterPartIndex((addr & 0xF0)>>4) > 0; } bool isSlaveMaster(unsigned char addr) { - return isMaster((unsigned char)(addr+256-5)); + return isMaster((unsigned char)(addr+256-5)); } unsigned char getSlaveAddress(unsigned char addr) { - if (isMaster(addr)) { - return (unsigned char)(addr+5); - } - if (isValidAddress(addr, false)) { - return addr; - } - return SYN; + if (isMaster(addr)) { + return (unsigned char)(addr+5); + } + if (isValidAddress(addr, false)) { + return addr; + } + return SYN; } unsigned char getMasterAddress(unsigned char addr) { - if (isMaster(addr)) { - return addr; - } - addr = (unsigned char)(addr+256-5); - if (isMaster(addr)) { - return addr; - } - return SYN; + if (isMaster(addr)) { + return addr; + } + addr = (unsigned char)(addr+256-5); + if (isMaster(addr)) { + return addr; + } + return SYN; } unsigned char getMasterNumber(unsigned char addr) { - unsigned char priority = getMasterPartIndex(addr & 0x0F); - if (priority == 0) { - return 0; - } - unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4); - if (index == 0) { - return 0; - } - return (unsigned char)(5*(priority-1) + index); + unsigned char priority = getMasterPartIndex(addr & 0x0F); + if (priority == 0) { + return 0; + } + unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4); + if (index == 0) { + return 0; + } + return (unsigned char)(5*(priority-1) + index); } bool isValidAddress(unsigned char addr, bool allowBroadcast) { - return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST); + return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST); } } // namespace ebusd diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index 9789a6e6..6df81849 100644 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -79,141 +79,141 @@ static const unsigned char BROADCAST = 0xFE; //!< the broadcast destination addr * A string of escaped or unescaped bus symbols. */ class SymbolString { - public: - /** - * Creates a new empty escaped or unescaped instance. - * @param escaped whether to create an escaped instance. - */ - explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {} + public: + /** + * Creates a new empty escaped or unescaped instance. + * @param escaped whether to create an escaped instance. + */ + explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {} - /** - * Add all symbols from the other @a SymbolString and the calculated CRC if escaped. - * @param str the @a SymbolString to copy from. - * @param skipLastSymbol whether to skip the last symbol (probably the CRC). - */ - void addAll(const SymbolString& str, const bool skipLastSymbol = false); + /** + * Add all symbols from the other @a SymbolString and the calculated CRC if escaped. + * @param str the @a SymbolString to copy from. + * @param skipLastSymbol whether to skip the last symbol (probably the CRC). + */ + void addAll(const SymbolString& str, const bool skipLastSymbol = false); - /** - * Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped. - * @param str the hex @a string. - * @param isEscaped whether the hex string is escaped. - * @return @a RESULT_OK on success, or an error code. - */ - result_t parseHex(const string& str, const bool isEscaped = false); + /** + * Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped. + * @param str the hex @a string. + * @param isEscaped whether the hex string is escaped. + * @return @a RESULT_OK on success, or an error code. + */ + result_t parseHex(const string& str, const bool isEscaped = false); - /** - * Return the symbols as hex string. - * @param unescape whether to unescape an escaped instance. - * @param skipLastSymbol whether to skip the last symbol (probably the CRC). - * @return the symbols as hex string. - */ - const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true); + /** + * Return the symbols as hex string. + * @param unescape whether to unescape an escaped instance. + * @param skipLastSymbol whether to skip the last symbol (probably the CRC). + * @return the symbols as hex string. + */ + const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true); - /** - * Return a reference to the symbol at the specified index. - * @param index the index of the symbol to return. - * @return the reference to the symbol at the specified index. - */ - unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; } + /** + * Return a reference to the symbol at the specified index. + * @param index the index of the symbol to return. + * @return the reference to the symbol at the specified index. + */ + unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; } - /** - * Return whether this instance is equal to the other instance. - * @param other the other instance. - * @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). - */ - bool operator == (SymbolString& other) { return m_unescapeState == other.m_unescapeState && m_data == other.m_data; } + /** + * Return whether this instance is equal to the other instance. + * @param other the other instance. + * @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). + */ + bool operator == (SymbolString& other) { return m_unescapeState == other.m_unescapeState && m_data == other.m_data; } - /** - * Return whether this instance is different from the other instance. - * @param other the other instance. - * @return true if this instance is different from the other instance. - */ - bool operator != (SymbolString& other) { return m_unescapeState != other.m_unescapeState || m_data != other.m_data; } + /** + * Return whether this instance is different from the other instance. + * @param other the other instance. + * @return true if this instance is different from the other instance. + */ + bool operator != (SymbolString& other) { return m_unescapeState != other.m_unescapeState || m_data != other.m_data; } - /** - * Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC). - * @param other the other instance. - * @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols), - * 1 if this instance is completely different to the other instance, - * 2 if this instance only differs from the other instance in the first byte (the master address). - */ - int compareMaster(SymbolString& other) { - if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) { - return 1; - } - if (m_data == other.m_data) { - return 0; - } - if (m_data.size() == 1) { - return 2; - } - if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) { - return 2; - } - return 1; - } + /** + * Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC). + * @param other the other instance. + * @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols), + * 1 if this instance is completely different to the other instance, + * 2 if this instance only differs from the other instance in the first byte (the master address). + */ + int compareMaster(SymbolString& other) { + if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) { + return 1; + } + if (m_data == other.m_data) { + return 0; + } + if (m_data.size() == 1) { + return 2; + } + if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) { + return 2; + } + return 1; + } - /** - * Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. - * @param value the symbol to append. - * @param isEscaped whether the symbol is escaped. - * @param updateCRC whether to update the calculated CRC in @a m_crc. - * @return RESULT_OK if another symbol was appended, - * RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received, - * RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected. - */ - result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true); + /** + * Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. + * @param value the symbol to append. + * @param isEscaped whether the symbol is escaped. + * @param updateCRC whether to update the calculated CRC in @a m_crc. + * @return RESULT_OK if another symbol was appended, + * RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received, + * RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected. + */ + result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true); - /** - * Return the number of symbols in this symbol string. - * @return the number of available symbols. - */ - unsigned char size() const { return (unsigned char)m_data.size(); } + /** + * Return the number of symbols in this symbol string. + * @return the number of available symbols. + */ + unsigned char size() const { return (unsigned char)m_data.size(); } - /** - * Return the calculated CRC. - * @return the calculated CRC. - */ - unsigned char getCRC() const { return m_crc; } + /** + * Return the calculated CRC. + * @return the calculated CRC. + */ + unsigned char getCRC() const { return m_crc; } - /** - * Clear the symbols. - */ - void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; } + /** + * Clear the symbols. + */ + void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; } - /** - * Clear the symbols and adjust the escape mode. - * @param escape true to set to an escaped instance, false to set to an unescaped instance. - */ - void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; } + /** + * Clear the symbols and adjust the escape mode. + * @param escape true to set to an escaped instance, false to set to an unescaped instance. + */ + void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; } - private: - /** - * Hidden copy constructor. - * @param str the @a SymbolString to copy from. - */ - SymbolString(const SymbolString& str) - : m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {} + private: + /** + * Hidden copy constructor. + * @param str the @a SymbolString to copy from. + */ + SymbolString(const SymbolString& str) + : m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {} - /** - * Update the calculated CRC in @a m_crc by adding a value. - * @param value the (escaped) value to add to the calculated CRC in @a m_crc. - */ - void addCRC(const unsigned char value); + /** + * Update the calculated CRC in @a m_crc by adding a value. + * @param value the (escaped) value to add to the calculated CRC in @a m_crc. + */ + void addCRC(const unsigned char value); - /** the string of bus symbols. */ - vector m_data; + /** the string of bus symbols. */ + vector m_data; - /** - * 0 if the symbols in @a m_data are escaped, - * 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol, - * 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol. - */ - int m_unescapeState; + /** + * 0 if the symbols in @a m_data are escaped, + * 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol, + * 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol. + */ + int m_unescapeState; - /** the calculated CRC. */ - unsigned char m_crc; + /** the calculated CRC. */ + unsigned char m_crc; }; diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index 977274ae..0624a739 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -27,594 +27,594 @@ using namespace ebusd; static bool error = false; void verify(bool expectFailMatch, string type, string input, - bool match, string expectStr, string gotStr) { - match = match && expectStr == gotStr; - if (expectFailMatch) { - if (match) { - cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed " << type << " match >" << input << "< OK" << endl; - } - } else if (match) { - cout << " " << type << " match >" << input << "< OK" << endl; - } else { - cout << " " << type << " match >" << input << "< error: got >" - << gotStr << "<, expected >" << expectStr << "<" << endl; - error = true; - } + bool match, string expectStr, string gotStr) { + match = match && expectStr == gotStr; + if (expectFailMatch) { + if (match) { + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + } else if (match) { + cout << " " << type << " match >" << input << "< OK" << endl; + } else { + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; + error = true; + } } int main() { - string checks[][5] = { - // entry: definition, decoded value, master data, slave data, flags - // definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] - {"x,,ign:10", "", "10fe07000a00000000000000000000", "00", ""}, - {"x,,ign:*", "", "10fe07000a00000000000000000000", "00", "W"}, - {"x,,ign,2", "", "", "", "c"}, - {"x,,str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x,,str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x,,str:10", "Hallo, Du ", "10fe07000a48616c6c6f2c20447520", "00", ""}, - {"x,,str:10", " ", "10fe07000a20202020202020202020", "00", ""}, - {"x,,str:10", "", "10fe07000a20202020202020202020", "00", "R"}, - {"x,,str:11", "", "10fe07000a20202020202020202020", "00", "rW"}, - {"x,,str:24", "abcdefghijklmnopqrstuvwx", "10fe0700186162636465666768696a6b6c6d6e6f707172737475767778", "00", ""}, - {"x,,str:*", "abcde", "10fe0700056162636465", "00", ""}, - {"x,,str,2", "", "", "", "c"}, - {"x,,str:10,=dummy", "", "10fe07000a48616c6c6f2044752120", "00", "W"}, - {"x,,str:10,==dummy", "", "10fe07000a48616c6c6f2044752120", "00", "rW"}, - {"x,,str:10,=dummy", "", "10fe07000a64756d6d792020202020", "00", ""}, - {"x,,str:10,==dummy", "", "10fe07000a64756d6d792020202020", "00", ""}, - {"x,,nts:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x,,nts:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x,,nts:10", "Hallo, Du", "10fe07000a48616c6c6f2c20447500", "00", ""}, - {"x,,nts:10", " ", "10fe07000a20202020202020202020", "00", ""}, - {"x,,nts:10", "", "10fe07000a00000000000000000000", "00", ""}, - {"x,,nts:10", "abc", "10fe07000a6162630065666768696a", "00", "W"}, - {"x,,nts:11", "", "10fe07000a20202020202020202020", "00", "rW"}, - {"x,,nts:24", "abcdefghijklmnopqrstuvwx", "10fe0700186162636465666768696a6b6c6d6e6f707172737475767778", "00", ""}, - {"x,,nts:*", "abcde", "10fe0700056162636465", "00", "W"}, - {"x,,nts:*", "abcde", "10fe070006616263646500", "00", ""}, - {"x,,nts,2", "", "", "", "c"}, - {"x,,hex", "20", "10fe07000120", "00", ""}, - {"x,,hex:10", "48 61 6c 6c 6f 2c 20 44 75 21", "10fe07000a48616c6c6f2c20447521", "00", ""}, - {"x,,hex:*", "48 61 6c 6c 6f", "10fe07000548616c6c6f", "00", ""}, - {"x,,hex:11", "", "10fe07000a48616c6c6f2c20447521", "00", "rW"}, - {"x,,hex,2", "", "", "", "c"}, - {"x,,hex:5,=48 61 6c 6c 6f", "", "10fe070005ababababab", "00", "W"}, - {"x,,hex:5,==48 61 6c 6c 6f", "", "10fe070005ababababab", "00", "rW"}, - {"x,,hex:5,=48 61 6c 6c 6f", "", "10fe07000548616c6c6f", "00", ""}, - {"x,,hex:5,==48 61 6c 6c 6f", "", "10fe07000548616c6c6f", "00", ""}, - {"x,,bda", "26.10.2014", "10fe07000426100614", "00", ""}, // Sunday - {"x,,bda", "01.01.2000", "10fe07000401010500", "00", ""}, // Saturday - {"x,,bda", "31.12.2099", "10fe07000431120399", "00", ""}, // Thursday - {"x,,bda", "-.-.-", "10fe070004ffff00ff", "00", ""}, - {"x,,bda", "", "10fe07000432100014", "00", "rw"}, - {"x,,bda:3", "26.10.2014", "10fe070003261014", "00", ""}, - {"x,,bda:3", "01.01.2000", "10fe070003010100", "00", ""}, - {"x,,bda:3", "31.12.2099", "10fe070003311299", "00", ""}, - {"x,,bda:3", "-.-.-", "10fe070003ffffff", "00", ""}, - {"x,,bda:3", "", "10fe070003321299", "00", "rw"}, - {"x,,bda,2", "", "", "", "c"}, - {"x,,hda", "26.10.2014", "10fe0700041a0a070e", "00", ""}, // Sunday - {"x,,hda", "01.01.2000", "10fe07000401010600", "00", ""}, // Saturday - {"x,,hda", "31.12.2099", "10fe0700041f0c0463", "00", ""}, // Thursday - {"x,,hda", "-.-.-", "10fe070004ffff00ff", "00", ""}, - {"x,,hda", "", "10fe070004200c0463", "00", "rw"}, - {"x,,hda:3", "26.10.2014", "10fe0700031a0a0e", "00", ""}, - {"x,,hda:3", "01.01.2000", "10fe070003010100", "00", ""}, - {"x,,hda:3", "31.12.2099", "10fe0700031f0c63", "00", ""}, - {"x,,hda:3", "-.-.-", "10fe070003ffffff", "00", ""}, - {"x,,hda:3", "", "10fe070003200c63", "00", "rw"}, - {"x,,hda,2", "", "", "", "c"}, - {"x,,day", "26.10.2014", "10fe070002d0a3", "00", ""}, - {"x,,day", "01.01.2000", "10fe070002ac8e", "00", ""}, - {"x,,day", "31.12.2078", "10fe07000262ff", "00", ""}, - {"x,,day", "-.-.-", "10fe070002ffff", "00", ""}, - {"x,,day", "", "10fe0700020000", "00", "Rw"}, - {"x,,bti", "21:04:58", "10fe070003580421", "00", ""}, - {"x,,bti", "00:00:00", "10fe070003000000", "00", ""}, - {"x,,bti", "23:59:59", "10fe070003595923", "00", ""}, - {"x,,bti", "-:-:-", "10fe070003ffffff", "00", ""}, - {"x,,bti", "", "10fe070003605923", "00", "rw"}, - {"x,,bti,2", "", "", "", "c"}, - {"x,,hti", "21:04:58", "10fe07000315043a", "00", ""}, - {"x,,hti", "-:-:-", "10fe070003ffffff", "00", ""}, - {"x,,hti,2", "", "", "", "c"}, - {"x,,vti", "21:04:58", "10fe0700033a0415", "00", ""}, - {"x,,vti", "-:-:-", "10fe070003636363", "00", ""}, - {"x,,vti,2", "", "", "", "c"}, - {"x,,btm", "21:04", "10fe0700020421", "00", ""}, - {"x,,btm", "00:00", "10fe0700020000", "00", ""}, - {"x,,btm", "23:59", "10fe0700025923", "00", ""}, - {"x,,btm", "24:00", "10fe0700020024", "00", ""}, - {"x,,btm", "-:-", "10fe070002ffff", "00", ""}, - {"x,,btm", "", "10fe0700025924", "00", "rw"}, - {"x,,btm", "24:01", "10fe0700020124", "00", "rw"}, - {"x,,btm,2", "", "", "", "c"}, - {"x,,htm", "21:04", "10fe0700021504", "00", ""}, - {"x,,htm", "00:00", "10fe0700020000", "00", ""}, - {"x,,htm", "23:59", "10fe070002173b", "00", ""}, - {"x,,htm", "24:00", "10fe0700021800", "00", ""}, - {"x,,htm", "-:-", "10fe070002ffff", "00", ""}, - {"x,,htm", "", "10fe070002183b", "00", "rw"}, - {"x,,htm", "24:01", "10fe0700021801", "00", "rw"}, - {"x,,htm,2", "", "", "", "c"}, - {"x,,vtm", "21:04", "10fe0700020415", "00", ""}, - {"x,,vtm", "00:00", "10fe0700020000", "00", ""}, - {"x,,vtm", "23:59", "10fe0700023b17", "00", ""}, - {"x,,vtm", "24:00", "10fe0700020018", "00", ""}, - {"x,,vtm", "-:-", "10fe070002ffff", "00", ""}, - {"x,,vtm", "", "10fe0700023b18", "00", "rw"}, - {"x,,vtm", "24:01", "10fe0700020118", "00", "rw"}, - {"x,,vtm,2", "", "", "", "c"}, - {"x,,min", "21:04", "10fe070002f004", "00", ""}, - {"x,,min", "00:00", "10fe0700020000", "00", ""}, - {"x,,min", "23:59", "10fe0700029f05", "00", ""}, - {"x,,min", "24:00", "10fe070002a005", "00", ""}, - {"x,,min", "-:-", "10fe070002ffff", "00", ""}, - {"x,,min", "", "10fe070002bbbb", "00", "rw"}, - {"x,,min", "24:01", "10fe070002a105", "00", "rw"}, - {"x,,min,2", "", "", "", "c"}, - {"x,,ttm", "22:40", "10fe07000188", "00", ""}, - {"x,,ttm", "00:00", "10fe07000100", "00", ""}, - {"x,,ttm", "23:50", "10fe0700018f", "00", ""}, - {"x,,ttm", "23:54", "10fe0700018f", "00", "R"}, - {"x,,ttm", "23:55", "10fe07000190", "00", "R"}, - {"x,,ttm", "23:56", "10fe07000190", "00", "R"}, - {"x,,ttm", "23:59", "10fe07000190", "00", "R"}, - {"x,,ttm", "-:-", "10fe07000190", "00", ""}, - {"x,,ttm", "", "10fe07000191", "00", "rw"}, - {"x,,ttm,2", "", "", "", "c"}, - {"x,,tth", "22:30", "10fe0700012d", "00", ""}, - {"x,,tth", "00:30", "10fe07000101", "00", ""}, - {"x,,tth", "23:31", "10fe0700012f", "00", "R"}, - {"x,,tth", "23:44", "10fe0700012f", "00", "R"}, - {"x,,tth", "23:45", "10fe07000130", "00", "R"}, - {"x,,tth", "24:00", "10fe07000130", "00", ""}, - {"x,,tth", "-:-", "10fe07000100", "00", ""}, - {"x,,tth", "", "10fe07000131", "00", "rw"}, - {"x,,tth,2", "", "", "", "c"}, - {"x,,ttq", "22:30", "10fe0700015a", "00", ""}, - {"x,,ttq", "00:30", "10fe07000102", "00", ""}, - {"x,,ttq", "23:31", "10fe0700015e", "00", "R"}, - {"x,,ttq", "23:44", "10fe0700015f", "00", "R"}, - {"x,,ttq", "23:45", "10fe0700015f", "00", ""}, - {"x,,ttq", "24:00", "10fe07000160", "00", ""}, - {"x,,ttq", "-:-", "10fe07000100", "00", ""}, - {"x,,ttq", "", "10fe07000161", "00", "rw"}, - {"x,,ttq,2", "", "", "", "c"}, - {"x,,bdy", "Mon", "10fe07000100", "00", ""}, - {"x,,bdy", "Sun", "10fe07000106", "00", ""}, - {"x,,bdy", "8", "10fe07000108", "00", "w"}, - {"x,,hdy", "Mon", "10fe07000101", "00", ""}, - {"x,,hdy", "Sun", "10fe07000107", "00", ""}, - {"x,,hdy", "8", "10fe07000108", "00", "w"}, - {"x,,pin", "1234", "10feffff021234", "00", ""}, - {"x,,pin", "0000", "10feffff020000", "00", ""}, - {"x,,pin", "9999", "10feffff029999", "00", ""}, - {"x,,pin", "", "10feffff020000", "00", "Rw"}, - {"x,,pin", "100", "10feffff020100", "00", "R"}, - {"x,,bcd", "26", "10feffff0126", "00", ""}, - {"x,,bcd", "0", "10feffff0100", "00", ""}, - {"x,,bcd", "99", "10feffff0199", "00", ""}, - {"x,,bcd", "100", "10feffff0199", "00", "Rw"}, - {"x,,bcd", "-", "10feffff01ff", "00", ""}, - {"x,,bcd", "", "10feffff019a", "00", "rw"}, - {"x,,bcd:1", "26", "10feffff0126", "00", ""}, - {"x,,bcd:1", "0", "10feffff0100", "00", ""}, - {"x,,bcd:1", "99", "10feffff0199", "00", ""}, - {"x,,bcd:1", "100", "10feffff0199", "00", "Rw"}, - {"x,,bcd:1", "-", "10feffff01ff", "00", ""}, - {"x,,bcd:1", "", "10feffff019a", "00", "rw"}, - {"x,,bcd:2", "126", "10feffff022601", "00", ""}, - {"x,,bcd:2", "0", "10feffff020000", "00", ""}, - {"x,,bcd:2", "9999", "10feffff029999", "00", ""}, - {"x,,bcd:2", "10000", "10feffff029999", "00", "Rw"}, - {"x,,bcd:2", "-", "10feffff02ffff", "00", ""}, - {"x,,bcd:2", "", "10feffff029a00", "00", "rw"}, - {"x,,bcd:3", "12346", "10feffff03462301", "00", ""}, - {"x,,bcd:3", "0", "10feffff03000000", "00", ""}, - {"x,,bcd:3", "999999", "10feffff03999999", "00", ""}, - {"x,,bcd:3", "1000000", "10feffff03999999", "00", "Rw"}, - {"x,,bcd:3", "-", "10feffff03ffffff", "00", ""}, - {"x,,bcd:3", "", "10feffff03009a00", "00", "rw"}, - {"x,,bcd:4", "1234567", "10feffff0467452301", "00", ""}, - {"x,,bcd:4", "0", "10feffff0400000000", "00", ""}, - {"x,,bcd:4", "99999999", "10feffff0499999999", "00", ""}, - {"x,,bcd:4", "100000000", "10feffff0499999999", "00", "Rw"}, - {"x,,bcd:4", "-", "10feffff04ffffffff", "00", ""}, - {"x,,bcd:4", "", "10feffff0400009a00", "00", "rw"}, - {"x,,hcd:1", "26", "10feffff011a", "00", ""}, - {"x,,hcd:1", "0", "10feffff0100", "00", ""}, - {"x,,hcd:1", "99", "10feffff0163", "00", ""}, - {"x,,hcd:1", "100", "10feffff0163", "00", "Rw"}, - {"x,,hcd:1", "", "10feffff019a", "00", "rw"}, - {"x,,hcd:2", "126", "10feffff021a01", "00", ""}, - {"x,,hcd:2", "0", "10feffff020000", "00", ""}, - {"x,,hcd:2", "9999", "10feffff026363", "00", ""}, - {"x,,hcd:2", "10000", "10feffff026363", "00", "Rw"}, - {"x,,hcd:2", "", "10feffff029a00", "00", "rw"}, - {"x,,hcd:3", "12346", "10feffff032e1701", "00", ""}, - {"x,,hcd:3", "0", "10feffff03000000", "00", ""}, - {"x,,hcd:3", "999999", "10feffff03636363", "00", ""}, - {"x,,hcd:3", "1000000", "10feffff03636363", "00", "Rw"}, - {"x,,hcd:3", "", "10feffff03009a00", "00", "rw"}, - {"x,,hcd:4", "1234567", "10feffff04432d1701", "00", ""}, - {"x,,hcd:4", "0", "10feffff0400000000", "00", ""}, - {"x,,hcd:4", "99999999", "10feffff0463636363", "00", ""}, - {"x,,hcd:4", "100000000", "10feffff0463636363", "00", "Rw"}, - {"x,,hcd", "", "10feffff0400006400", "00", "rw"}, - {"x,,hcd", "1234567", "10feffff04432d1701", "00", ""}, - {"x,,hcd", "0", "10feffff0400000000", "00", ""}, - {"x,,hcd", "99999999", "10feffff0463636363", "00", ""}, - {"x,,hcd", "100000000", "10feffff0463636363", "00", "Rw"}, - {"x,,hcd", "", "10feffff0400006400", "00", "rw"}, - {"x,,str:16", "0123456789ABCDEF", "10feffff1130313233343536373839414243444546", "00", ""}, - {"x,,uch:17", "", "10feffff00", "00", "c"}, - {"x,s,uch", "0", "1025ffff0310111213", "0300010203", "W"}, - {"x,s,uch", "0", "1025ffff00", "0100", ""}, - {"x,s,uch,,,,y,m,uch", "3;2", "1025ffff0103", "0102", ""}, - {"x,,uch", "38", "10feffff0126", "00", ""}, - {"x,,uch", "38.5", "10feffff0126", "00", "R"}, - {"x,,uch", "0", "10feffff0100", "00", ""}, - {"x,,uch", "254", "10feffff01fe", "00", ""}, - {"x,,uch", "-", "10feffff01ff", "00", ""}, - {"x,,uch,10", "3.8", "10feffff0126", "00", ""}, - {"x,,uch,-10", "380", "10feffff0126", "00", ""}, - {"x,,uch,=48", "", "10feffff01ab", "00", "W"}, - {"x,,uch,==48", "", "10feffff01ab", "00", "rW"}, - {"x,,uch,=48", "", "10feffff0130", "00", ""}, - {"x,,uch,==48", "", "10feffff0130", "00", ""}, - {"x,,sch", "-90", "10feffff01a6", "00", ""}, - {"x,,sch", "0", "10feffff0100", "00", ""}, - {"x,,sch", "-1", "10feffff01ff", "00", ""}, - {"x,,sch", "-", "10feffff0180", "00", ""}, - {"x,,sch", "-127", "10feffff0181", "00", ""}, - {"x,,sch", "127", "10feffff017f", "00", ""}, - {"x,,sch,10", "-9.0", "10feffff01a6", "00", ""}, - {"x,,sch,-10", "-900", "10feffff01a6", "00", ""}, - {"x,,d1b", "-90", "10feffff01a6", "00", ""}, - {"x,,d1b", "0", "10feffff0100", "00", ""}, - {"x,,d1b", "-1", "10feffff01ff", "00", ""}, - {"x,,d1b", "-", "10feffff0180", "00", ""}, - {"x,,d1b", "-127", "10feffff0181", "00", ""}, - {"x,,d1b", "127", "10feffff017f", "00", ""}, - {"x,,d1b,-10", "-900", "10feffff01a6", "00", ""}, - {"x,,d1c", "19.5", "10feffff0127", "00", ""}, - {"x,,d1c", "0.0", "10feffff0100", "00", ""}, - {"x,,d1c", "100.0", "10feffff01c8", "00", ""}, - {"x,,d1c", "-", "10feffff01ff", "00", ""}, - {"x,,uin", "38", "10feffff022600", "00", ""}, - {"x,,uin", "0", "10feffff020000", "00", ""}, - {"x,,uin", "65534", "10feffff02feff", "00", ""}, - {"x,,uin", "-", "10feffff02ffff", "00", ""}, - {"x,,uin,10", "3.8", "10feffff022600", "00", ""}, - {"x,,uin,-10", "380", "10feffff022600", "00", ""}, - {"x,,uir", "38", "10feffff020026", "00", ""}, - {"x,,uir", "0", "10feffff020000", "00", ""}, - {"x,,uir", "65534", "10feffff02fffe", "00", ""}, - {"x,,uir", "-", "10feffff02ffff", "00", ""}, - {"x,,uir,10", "3.8", "10feffff020026", "00", ""}, - {"x,,uir,-10", "380", "10feffff020026", "00", ""}, - {"uin10,uin,-10", "", "", "", "t"}, // template - {"x,,uin10", "380", "10feffff022600", "00", ""}, // template reference - {"x,,uin10,-10", "3800", "10feffff022600", "00", ""}, // template reference, valid divider product - {"x,,uin10,10", "", "", "", "c"}, // template reference, invalid divider product - {"x,,sin", "-90", "10feffff02a6ff", "00", ""}, - {"x,,sin", "0", "10feffff020000", "00", ""}, - {"x,,sin", "-1", "10feffff02ffff", "00", ""}, - {"x,,sin", "-", "10feffff020080", "00", ""}, - {"x,,sin", "-32767", "10feffff020180", "00", ""}, - {"x,,sin", "32767", "10feffff02ff7f", "00", ""}, - {"x,,sin,10", "-9.0", "10feffff02a6ff", "00", ""}, - {"x,,sin,-10", "-900", "10feffff02a6ff", "00", ""}, - {"x,,sir", "-90", "10feffff02ffa6", "00", ""}, - {"x,,sir", "0", "10feffff020000", "00", ""}, - {"x,,sir", "-1", "10feffff02ffff", "00", ""}, - {"x,,sir", "-", "10feffff028000", "00", ""}, - {"x,,sir", "-32767", "10feffff028001", "00", ""}, - {"x,,sir", "32767", "10feffff027fff", "00", ""}, - {"x,,sir,10", "-9.0", "10feffff02ffa6", "00", ""}, - {"x,,sir,-10", "-900", "10feffff02ffa6", "00", ""}, - {"x,,u3n", "38", "10feffff03260000", "00", ""}, - {"x,,u3n", "0", "10feffff03000000", "00", ""}, - {"x,,u3n", "16777214", "10feffff03feffff", "00", ""}, - {"x,,u3n", "-", "10feffff03ffffff", "00", ""}, - {"x,,u3n,10", "3.8", "10feffff03260000", "00", ""}, - {"x,,u3n,-10", "380", "10feffff03260000", "00", ""}, - {"x,,u3r", "38", "10feffff03000026", "00", ""}, - {"x,,u3r", "0", "10feffff03000000", "00", ""}, - {"x,,u3r", "16777214", "10feffff03fffffe", "00", ""}, - {"x,,u3r", "-", "10feffff03ffffff", "00", ""}, - {"x,,u3r,10", "3.8", "10feffff03000026", "00", ""}, - {"x,,u3r,-10", "380", "10feffff03000026", "00", ""}, - {"x,,s3n", "-90", "10feffff03a6ffff", "00", ""}, - {"x,,s3n", "0", "10feffff03000000", "00", ""}, - {"x,,s3n", "-1", "10feffff03ffffff", "00", ""}, - {"x,,s3n", "-", "10feffff03000080", "00", ""}, - {"x,,s3n", "-8388607", "10feffff03010080", "00", ""}, - {"x,,s3n", "8388607", "10feffff03ffff7f", "00", ""}, - {"x,,s3n,10", "-9.0", "10feffff03a6ffff", "00", ""}, - {"x,,s3n,-10", "-900", "10feffff03a6ffff", "00", ""}, - {"x,,s3r", "-90", "10feffff03ffffa6", "00", ""}, - {"x,,s3r", "0", "10feffff03000000", "00", ""}, - {"x,,s3r", "-1", "10feffff03ffffff", "00", ""}, - {"x,,s3r", "-", "10feffff03800000", "00", ""}, - {"x,,s3r", "-8388607", "10feffff03800001", "00", ""}, - {"x,,s3r", "8388607", "10feffff037fffff", "00", ""}, - {"x,,s3r,10", "-9.0", "10feffff03ffffa6", "00", ""}, - {"x,,s3r,-10", "-900", "10feffff03ffffa6", "00", ""}, - {"x,,flt", "-0.090", "10feffff02a6ff", "00", ""}, - {"x,,flt", "0.000", "10feffff020000", "00", ""}, - {"x,,flt", "-0.001", "10feffff02ffff", "00", ""}, - {"x,,flt", "-", "10feffff020080", "00", ""}, - {"x,,flt", "-32.767", "10feffff020180", "00", ""}, - {"x,,flt", "32.767", "10feffff02ff7f", "00", ""}, - {"x,,flr", "-0.090", "10feffff02ffa6", "00", ""}, - {"x,,flr", "0.000", "10feffff020000", "00", ""}, - {"x,,flr", "-0.001", "10feffff02ffff", "00", ""}, - {"x,,flr", "-", "10feffff028000", "00", ""}, - {"x,,flr", "-32.767", "10feffff028001", "00", ""}, - {"x,,flr", "32.767", "10feffff027fff", "00", ""}, - {"x,,exp", "-0.09", "10feffff04ec51b8bd", "00", ""}, - {"x,,exp", "0.0", "10feffff0400000000", "00", ""}, - {"x,,exp", "-0.001", "10feffff046f1283ba", "00", ""}, - {"x,,exp", "-", "10feffff040000807f", "00", ""}, - {"x,,exp", "-32.767", "10feffff04681103c2", "00", ""}, - {"x,,exp,1000", "-0.000090000", "10feffff04ec51b8bd", "00", ""}, - {"x,,exp,-100", "-9", "10feffff04ec51b8bd", "00", ""}, - {"x,,exr", "-0.09", "10feffff04bdb851ec", "00", ""}, - {"x,,exr", "0.0", "10feffff0400000000", "00", ""}, - {"x,,exr", "-0.001", "10feffff04ba83126f", "00", ""}, - {"x,,exr", "-", "10feffff047f800000", "00", ""}, - {"x,,exr", "-32.767", "10feffff04c2031168", "00", ""}, - {"x,,exr,1000", "-0.000090000", "10feffff04bdb851ec", "00", ""}, - {"x,,exr,-100", "-9", "10feffff04bdb851ec", "00", ""}, - {"x,,d2b", "18.004", "10fe0700090112", "00", ""}, - {"x,,d2b", "0.000", "10feffff020000", "00", ""}, - {"x,,d2b", "-0.004", "10feffff02ffff", "00", ""}, - {"x,,d2b", "-", "10feffff020080", "00", ""}, - {"x,,d2b", "-127.996", "10feffff020180", "00", ""}, - {"x,,d2b", "127.996", "10feffff02ff7f", "00", ""}, - {"x,,d2c", "288.06", "10fe0700090112", "00", ""}, - {"x,,d2c", "0.00", "10feffff020000", "00", ""}, - {"x,,d2c", "-0.06", "10feffff02ffff", "00", ""}, - {"x,,d2c", "-", "10feffff020080", "00", ""}, - {"x,,d2c", "-2047.94", "10feffff020180", "00", ""}, - {"x,,d2c", "2047.94", "10feffff02ff7f", "00", ""}, - {"x,,ulg", "38", "10feffff0426000000", "00", ""}, - {"x,,ulg", "0", "10feffff0400000000", "00", ""}, - {"x,,ulg", "4294967294", "10feffff04feffffff", "00", ""}, - {"x,,ulg", "-", "10feffff04ffffffff", "00", ""}, - {"x,,ulg,10", "3.8", "10feffff0426000000", "00", ""}, - {"x,,ulg,-10", "380", "10feffff0426000000", "00", ""}, - {"x,,ulg,0x0FF0F00F = VRT 350 ;0x33CCCC33=VRT 360;0x3CC3C33C=SD 17;0x66999966=SD 37;0x69969669=VRT 360+", - "VRT 350", "10feffff040FF0F00F", "00", ""}, - {"x,,ulg,0x=test", "", "10feffff040FF0F00F", "00", "c"}, - {"x,,ulr", "38", "10feffff0400000026", "00", ""}, - {"x,,ulr", "0", "10feffff0400000000", "00", ""}, - {"x,,ulr", "4294967294", "10feffff04fffffffe", "00", ""}, - {"x,,ulr", "-", "10feffff04ffffffff", "00", ""}, - {"x,,ulr,10", "3.8", "10feffff0400000026", "00", ""}, - {"x,,ulr,-10", "380", "10feffff0400000026", "00", ""}, - {"x,,slg", "-90", "10feffff04a6ffffff", "00", ""}, - {"x,,slg", "0", "10feffff0400000000", "00", ""}, - {"x,,slg", "-1", "10feffff04ffffffff", "00", ""}, - {"x,,slg,10", "-9.0", "10feffff04a6ffffff", "00", ""}, - {"x,,slg,-10", "-900", "10feffff04a6ffffff", "00", ""}, - {"x,,slr", "-90", "10feffff04ffffffa6", "00", ""}, - {"x,,slr", "0", "10feffff0400000000", "00", ""}, - {"x,,slr", "-1", "10feffff04ffffffff", "00", ""}, - {"x,,slr,10", "-9.0", "10feffff04ffffffa6", "00", ""}, - {"x,,slr,-10", "-900", "10feffff04ffffffa6", "00", ""}, - {"x,,bi3", "1", "10feffff0108", "00", ""}, - {"x,,bi3", "0", "10feffff0100", "00", ""}, - {"x,,bi3,0=off;1=on", "on", "10feffff0108", "00", ""}, - {"x,,bi3,0=off;1=on", "off", "10feffff0100", "00", ""}, - {"x,,bi3:2", "1", "10feffff0108", "00", ""}, - {"x,,bi3:2", "1", "10feffff01ef", "00", "W"}, - {"x,,bi3:2", "0", "10feffff0100", "00", ""}, - {"x,,bi3:2", "3", "10feffff0118", "00", ""}, - {"x,,bi3:2,1=on", "on", "10feffff0108", "00", ""}, - {"x,,bi3:2,1=on", "-", "10feffff0100", "00", ""}, - {"x,,bi3:2,0=off;1=on;2=auto;3=eco", "auto", "10feffff0110", "00", ""}, - {"x,,bi3:2,0=off;1=on;2=auto;3=eco", "2.5", "10feffff0110", "00", "R"}, - {"x,,bi3:2,0=off;1=on", "on", "10feffff0108", "00", ""}, - {"x,,bi3:2,0=off;1=on", "off", "10feffff0100", "00", ""}, - {"x,,bi3:2,0=off;1=on", "1", "10feffff0108", "00", "n"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "x=on ja/nein [Wahrheitswert]", "10feffff0108", "00", "vvv"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "x=1 ja/nein [Wahrheitswert]", "10feffff0108", "00", "vvvn"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": \"on\"}", "10feffff0108", "00", "vj"}, - {",,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"\", \"value\": \"on\"}", "10feffff0108", "00", "vj"}, - {",,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"\", \"value\": \"on\"}", "10feffff0108", "00", "j"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": \"on\", \"unit\": \"ja/nein\", \"comment\": \"Wahrheitswert\"}", "10feffff0108", "00", "vvvj"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": 1}", "10feffff0108", "00", "vnj"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": 1, \"unit\": \"ja/nein\", \"comment\": \"Wahrheitswert\"}", "10feffff0108", "00", "vvvnj"}, - {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"x\", \"value\": 1}", "10feffff0108", "00", "nj"}, - {"x,,uch,1=test;2=high;3=off;0x10=on", "on", "10feffff0110", "00", ""}, - {"x,s,uch", "3", "1050ffff00", "0103", ""}, - {"x,,d2b,,°C,Aussentemperatur", "x=18.004 °C [Aussentemperatur]", "10fe0700090112", "00", "vvv"}, - {"x,,bti,,,,y,,bda,,,,z,,bdy", "21:04:58;26.10.2014;Sun", "10fe0700085804212610061406", "00", ""}, // combination - {"x,,bi3,,,,y,,bi5", "1;0", "10feffff0108", "00", ""}, // bit combination - {"x,,bi3,,,,y,,bi5", "1;1", "10feffff0128", "00", ""}, // bit combination - {"x,,bi3,,,,y,,bi5", "0;1", "10feffff0120", "00", ""}, // bit combination - {"x,,bi3,,,,y,,bi5", "0;0", "10feffff0100", "00", ""}, // bit combination - {"x,,bi3,,,,y,,bi7,,,,t,,uch", "0;0;9", "10feffff020009", "00", ""}, // bit combination - {"x,,bi6:2,,,,y,,bi0:2,,,,t,,uch", "2;1;9", "10feffff03800109", "00", ""}, // bit combination - {"x,,BI0;BI1;BI2;BI3;BI4;BI5;BI6;BI7", "0;0;1;0;0;0;0;0", "ff75b50900", "0104", ""}, // bits - {"temp,d2b,,°C,Aussentemperatur", "", "", "", "t"}, // template with relative pos - {"x,,temp", "18.004", "10fe0700020112", "00", ""}, // reference to template - {"x,,temp,10", "1.8004", "10fe0700020112", "00", ""}, // reference to template, valid divider product - {"x,,temp,-10", "", "", "", "c"}, // reference to template, invalid divider product - {"relrel,d2b,,,,y,d1c", "", "", "", "t"}, // template struct with relative pos - {"x,,relrel", "18.004;9.5", "10fe070003011213", "00", ""}, // reference to template struct - {"trelrel,temp;temp", "", "", "", "t"}, // template struct with relative pos and ref to templates - {"x,,trelrel", "18.004;19.008", "10fe07000401120213", "00", ""}, // reference to template struct - {"x,,temp,,,,y,,d1c", "18.004;9.5", "10fe070003011213", "00", ""}, // reference to template, normal def - {"x,,temp;HEX:2", "18.004;13 14", "10fe07000401121314", "00", ""}, // reference to template and base type - {"x,,temp;HEX:2", "temp=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type - {"x,,temp:degrees;HEX:2", "degrees=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type - }; - DataFieldTemplates* templates = new DataFieldTemplates(); - DataField* fields = NULL; - for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { - string check[5] = checks[i]; - istringstream isstr(check[0]); - string expectStr = check[1]; - SymbolString mstr(false); - result_t result = mstr.parseHex(check[2]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; - error = true; - continue; - } - SymbolString sstr(false); - result = sstr.parseHex(check[3]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; - error = true; - continue; - } - string flags = check[4]; - bool isSet = flags.find('s') != string::npos; - bool failedCreate = flags.find('c') != string::npos; - bool failedRead = flags.find('r') != string::npos; - bool failedReadMatch = flags.find('R') != string::npos; - bool failedWrite = flags.find('w') != string::npos; - bool failedWriteMatch = flags.find('W') != string::npos; - OutputFormat verbosity = 0; - if (flags.find("v") != string::npos) { - verbosity |= OF_NAMES; - } - if (flags.find("vv") != string::npos) { - verbosity |= OF_UNITS; - } - if (flags.find("vvv") != string::npos) { - verbosity |= OF_COMMENTS; - } - if (flags.find('j') != string::npos) { - verbosity |= OF_JSON; - } - bool numeric = flags.find('n') != string::npos; - bool isTemplate = flags.find('t') != string::npos; - string item; - vector entries; + string checks[][5] = { + // entry: definition, decoded value, master data, slave data, flags + // definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] + {"x,,ign:10", "", "10fe07000a00000000000000000000", "00", ""}, + {"x,,ign:*", "", "10fe07000a00000000000000000000", "00", "W"}, + {"x,,ign,2", "", "", "", "c"}, + {"x,,str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x,,str:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x,,str:10", "Hallo, Du ", "10fe07000a48616c6c6f2c20447520", "00", ""}, + {"x,,str:10", " ", "10fe07000a20202020202020202020", "00", ""}, + {"x,,str:10", "", "10fe07000a20202020202020202020", "00", "R"}, + {"x,,str:11", "", "10fe07000a20202020202020202020", "00", "rW"}, + {"x,,str:24", "abcdefghijklmnopqrstuvwx", "10fe0700186162636465666768696a6b6c6d6e6f707172737475767778", "00", ""}, + {"x,,str:*", "abcde", "10fe0700056162636465", "00", ""}, + {"x,,str,2", "", "", "", "c"}, + {"x,,str:10,=dummy", "", "10fe07000a48616c6c6f2044752120", "00", "W"}, + {"x,,str:10,==dummy", "", "10fe07000a48616c6c6f2044752120", "00", "rW"}, + {"x,,str:10,=dummy", "", "10fe07000a64756d6d792020202020", "00", ""}, + {"x,,str:10,==dummy", "", "10fe07000a64756d6d792020202020", "00", ""}, + {"x,,nts:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x,,nts:10", "Hallo, Du!", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x,,nts:10", "Hallo, Du", "10fe07000a48616c6c6f2c20447500", "00", ""}, + {"x,,nts:10", " ", "10fe07000a20202020202020202020", "00", ""}, + {"x,,nts:10", "", "10fe07000a00000000000000000000", "00", ""}, + {"x,,nts:10", "abc", "10fe07000a6162630065666768696a", "00", "W"}, + {"x,,nts:11", "", "10fe07000a20202020202020202020", "00", "rW"}, + {"x,,nts:24", "abcdefghijklmnopqrstuvwx", "10fe0700186162636465666768696a6b6c6d6e6f707172737475767778", "00", ""}, + {"x,,nts:*", "abcde", "10fe0700056162636465", "00", "W"}, + {"x,,nts:*", "abcde", "10fe070006616263646500", "00", ""}, + {"x,,nts,2", "", "", "", "c"}, + {"x,,hex", "20", "10fe07000120", "00", ""}, + {"x,,hex:10", "48 61 6c 6c 6f 2c 20 44 75 21", "10fe07000a48616c6c6f2c20447521", "00", ""}, + {"x,,hex:*", "48 61 6c 6c 6f", "10fe07000548616c6c6f", "00", ""}, + {"x,,hex:11", "", "10fe07000a48616c6c6f2c20447521", "00", "rW"}, + {"x,,hex,2", "", "", "", "c"}, + {"x,,hex:5,=48 61 6c 6c 6f", "", "10fe070005ababababab", "00", "W"}, + {"x,,hex:5,==48 61 6c 6c 6f", "", "10fe070005ababababab", "00", "rW"}, + {"x,,hex:5,=48 61 6c 6c 6f", "", "10fe07000548616c6c6f", "00", ""}, + {"x,,hex:5,==48 61 6c 6c 6f", "", "10fe07000548616c6c6f", "00", ""}, + {"x,,bda", "26.10.2014", "10fe07000426100614", "00", ""}, // Sunday + {"x,,bda", "01.01.2000", "10fe07000401010500", "00", ""}, // Saturday + {"x,,bda", "31.12.2099", "10fe07000431120399", "00", ""}, // Thursday + {"x,,bda", "-.-.-", "10fe070004ffff00ff", "00", ""}, + {"x,,bda", "", "10fe07000432100014", "00", "rw"}, + {"x,,bda:3", "26.10.2014", "10fe070003261014", "00", ""}, + {"x,,bda:3", "01.01.2000", "10fe070003010100", "00", ""}, + {"x,,bda:3", "31.12.2099", "10fe070003311299", "00", ""}, + {"x,,bda:3", "-.-.-", "10fe070003ffffff", "00", ""}, + {"x,,bda:3", "", "10fe070003321299", "00", "rw"}, + {"x,,bda,2", "", "", "", "c"}, + {"x,,hda", "26.10.2014", "10fe0700041a0a070e", "00", ""}, // Sunday + {"x,,hda", "01.01.2000", "10fe07000401010600", "00", ""}, // Saturday + {"x,,hda", "31.12.2099", "10fe0700041f0c0463", "00", ""}, // Thursday + {"x,,hda", "-.-.-", "10fe070004ffff00ff", "00", ""}, + {"x,,hda", "", "10fe070004200c0463", "00", "rw"}, + {"x,,hda:3", "26.10.2014", "10fe0700031a0a0e", "00", ""}, + {"x,,hda:3", "01.01.2000", "10fe070003010100", "00", ""}, + {"x,,hda:3", "31.12.2099", "10fe0700031f0c63", "00", ""}, + {"x,,hda:3", "-.-.-", "10fe070003ffffff", "00", ""}, + {"x,,hda:3", "", "10fe070003200c63", "00", "rw"}, + {"x,,hda,2", "", "", "", "c"}, + {"x,,day", "26.10.2014", "10fe070002d0a3", "00", ""}, + {"x,,day", "01.01.2000", "10fe070002ac8e", "00", ""}, + {"x,,day", "31.12.2078", "10fe07000262ff", "00", ""}, + {"x,,day", "-.-.-", "10fe070002ffff", "00", ""}, + {"x,,day", "", "10fe0700020000", "00", "Rw"}, + {"x,,bti", "21:04:58", "10fe070003580421", "00", ""}, + {"x,,bti", "00:00:00", "10fe070003000000", "00", ""}, + {"x,,bti", "23:59:59", "10fe070003595923", "00", ""}, + {"x,,bti", "-:-:-", "10fe070003ffffff", "00", ""}, + {"x,,bti", "", "10fe070003605923", "00", "rw"}, + {"x,,bti,2", "", "", "", "c"}, + {"x,,hti", "21:04:58", "10fe07000315043a", "00", ""}, + {"x,,hti", "-:-:-", "10fe070003ffffff", "00", ""}, + {"x,,hti,2", "", "", "", "c"}, + {"x,,vti", "21:04:58", "10fe0700033a0415", "00", ""}, + {"x,,vti", "-:-:-", "10fe070003636363", "00", ""}, + {"x,,vti,2", "", "", "", "c"}, + {"x,,btm", "21:04", "10fe0700020421", "00", ""}, + {"x,,btm", "00:00", "10fe0700020000", "00", ""}, + {"x,,btm", "23:59", "10fe0700025923", "00", ""}, + {"x,,btm", "24:00", "10fe0700020024", "00", ""}, + {"x,,btm", "-:-", "10fe070002ffff", "00", ""}, + {"x,,btm", "", "10fe0700025924", "00", "rw"}, + {"x,,btm", "24:01", "10fe0700020124", "00", "rw"}, + {"x,,btm,2", "", "", "", "c"}, + {"x,,htm", "21:04", "10fe0700021504", "00", ""}, + {"x,,htm", "00:00", "10fe0700020000", "00", ""}, + {"x,,htm", "23:59", "10fe070002173b", "00", ""}, + {"x,,htm", "24:00", "10fe0700021800", "00", ""}, + {"x,,htm", "-:-", "10fe070002ffff", "00", ""}, + {"x,,htm", "", "10fe070002183b", "00", "rw"}, + {"x,,htm", "24:01", "10fe0700021801", "00", "rw"}, + {"x,,htm,2", "", "", "", "c"}, + {"x,,vtm", "21:04", "10fe0700020415", "00", ""}, + {"x,,vtm", "00:00", "10fe0700020000", "00", ""}, + {"x,,vtm", "23:59", "10fe0700023b17", "00", ""}, + {"x,,vtm", "24:00", "10fe0700020018", "00", ""}, + {"x,,vtm", "-:-", "10fe070002ffff", "00", ""}, + {"x,,vtm", "", "10fe0700023b18", "00", "rw"}, + {"x,,vtm", "24:01", "10fe0700020118", "00", "rw"}, + {"x,,vtm,2", "", "", "", "c"}, + {"x,,min", "21:04", "10fe070002f004", "00", ""}, + {"x,,min", "00:00", "10fe0700020000", "00", ""}, + {"x,,min", "23:59", "10fe0700029f05", "00", ""}, + {"x,,min", "24:00", "10fe070002a005", "00", ""}, + {"x,,min", "-:-", "10fe070002ffff", "00", ""}, + {"x,,min", "", "10fe070002bbbb", "00", "rw"}, + {"x,,min", "24:01", "10fe070002a105", "00", "rw"}, + {"x,,min,2", "", "", "", "c"}, + {"x,,ttm", "22:40", "10fe07000188", "00", ""}, + {"x,,ttm", "00:00", "10fe07000100", "00", ""}, + {"x,,ttm", "23:50", "10fe0700018f", "00", ""}, + {"x,,ttm", "23:54", "10fe0700018f", "00", "R"}, + {"x,,ttm", "23:55", "10fe07000190", "00", "R"}, + {"x,,ttm", "23:56", "10fe07000190", "00", "R"}, + {"x,,ttm", "23:59", "10fe07000190", "00", "R"}, + {"x,,ttm", "-:-", "10fe07000190", "00", ""}, + {"x,,ttm", "", "10fe07000191", "00", "rw"}, + {"x,,ttm,2", "", "", "", "c"}, + {"x,,tth", "22:30", "10fe0700012d", "00", ""}, + {"x,,tth", "00:30", "10fe07000101", "00", ""}, + {"x,,tth", "23:31", "10fe0700012f", "00", "R"}, + {"x,,tth", "23:44", "10fe0700012f", "00", "R"}, + {"x,,tth", "23:45", "10fe07000130", "00", "R"}, + {"x,,tth", "24:00", "10fe07000130", "00", ""}, + {"x,,tth", "-:-", "10fe07000100", "00", ""}, + {"x,,tth", "", "10fe07000131", "00", "rw"}, + {"x,,tth,2", "", "", "", "c"}, + {"x,,ttq", "22:30", "10fe0700015a", "00", ""}, + {"x,,ttq", "00:30", "10fe07000102", "00", ""}, + {"x,,ttq", "23:31", "10fe0700015e", "00", "R"}, + {"x,,ttq", "23:44", "10fe0700015f", "00", "R"}, + {"x,,ttq", "23:45", "10fe0700015f", "00", ""}, + {"x,,ttq", "24:00", "10fe07000160", "00", ""}, + {"x,,ttq", "-:-", "10fe07000100", "00", ""}, + {"x,,ttq", "", "10fe07000161", "00", "rw"}, + {"x,,ttq,2", "", "", "", "c"}, + {"x,,bdy", "Mon", "10fe07000100", "00", ""}, + {"x,,bdy", "Sun", "10fe07000106", "00", ""}, + {"x,,bdy", "8", "10fe07000108", "00", "w"}, + {"x,,hdy", "Mon", "10fe07000101", "00", ""}, + {"x,,hdy", "Sun", "10fe07000107", "00", ""}, + {"x,,hdy", "8", "10fe07000108", "00", "w"}, + {"x,,pin", "1234", "10feffff021234", "00", ""}, + {"x,,pin", "0000", "10feffff020000", "00", ""}, + {"x,,pin", "9999", "10feffff029999", "00", ""}, + {"x,,pin", "", "10feffff020000", "00", "Rw"}, + {"x,,pin", "100", "10feffff020100", "00", "R"}, + {"x,,bcd", "26", "10feffff0126", "00", ""}, + {"x,,bcd", "0", "10feffff0100", "00", ""}, + {"x,,bcd", "99", "10feffff0199", "00", ""}, + {"x,,bcd", "100", "10feffff0199", "00", "Rw"}, + {"x,,bcd", "-", "10feffff01ff", "00", ""}, + {"x,,bcd", "", "10feffff019a", "00", "rw"}, + {"x,,bcd:1", "26", "10feffff0126", "00", ""}, + {"x,,bcd:1", "0", "10feffff0100", "00", ""}, + {"x,,bcd:1", "99", "10feffff0199", "00", ""}, + {"x,,bcd:1", "100", "10feffff0199", "00", "Rw"}, + {"x,,bcd:1", "-", "10feffff01ff", "00", ""}, + {"x,,bcd:1", "", "10feffff019a", "00", "rw"}, + {"x,,bcd:2", "126", "10feffff022601", "00", ""}, + {"x,,bcd:2", "0", "10feffff020000", "00", ""}, + {"x,,bcd:2", "9999", "10feffff029999", "00", ""}, + {"x,,bcd:2", "10000", "10feffff029999", "00", "Rw"}, + {"x,,bcd:2", "-", "10feffff02ffff", "00", ""}, + {"x,,bcd:2", "", "10feffff029a00", "00", "rw"}, + {"x,,bcd:3", "12346", "10feffff03462301", "00", ""}, + {"x,,bcd:3", "0", "10feffff03000000", "00", ""}, + {"x,,bcd:3", "999999", "10feffff03999999", "00", ""}, + {"x,,bcd:3", "1000000", "10feffff03999999", "00", "Rw"}, + {"x,,bcd:3", "-", "10feffff03ffffff", "00", ""}, + {"x,,bcd:3", "", "10feffff03009a00", "00", "rw"}, + {"x,,bcd:4", "1234567", "10feffff0467452301", "00", ""}, + {"x,,bcd:4", "0", "10feffff0400000000", "00", ""}, + {"x,,bcd:4", "99999999", "10feffff0499999999", "00", ""}, + {"x,,bcd:4", "100000000", "10feffff0499999999", "00", "Rw"}, + {"x,,bcd:4", "-", "10feffff04ffffffff", "00", ""}, + {"x,,bcd:4", "", "10feffff0400009a00", "00", "rw"}, + {"x,,hcd:1", "26", "10feffff011a", "00", ""}, + {"x,,hcd:1", "0", "10feffff0100", "00", ""}, + {"x,,hcd:1", "99", "10feffff0163", "00", ""}, + {"x,,hcd:1", "100", "10feffff0163", "00", "Rw"}, + {"x,,hcd:1", "", "10feffff019a", "00", "rw"}, + {"x,,hcd:2", "126", "10feffff021a01", "00", ""}, + {"x,,hcd:2", "0", "10feffff020000", "00", ""}, + {"x,,hcd:2", "9999", "10feffff026363", "00", ""}, + {"x,,hcd:2", "10000", "10feffff026363", "00", "Rw"}, + {"x,,hcd:2", "", "10feffff029a00", "00", "rw"}, + {"x,,hcd:3", "12346", "10feffff032e1701", "00", ""}, + {"x,,hcd:3", "0", "10feffff03000000", "00", ""}, + {"x,,hcd:3", "999999", "10feffff03636363", "00", ""}, + {"x,,hcd:3", "1000000", "10feffff03636363", "00", "Rw"}, + {"x,,hcd:3", "", "10feffff03009a00", "00", "rw"}, + {"x,,hcd:4", "1234567", "10feffff04432d1701", "00", ""}, + {"x,,hcd:4", "0", "10feffff0400000000", "00", ""}, + {"x,,hcd:4", "99999999", "10feffff0463636363", "00", ""}, + {"x,,hcd:4", "100000000", "10feffff0463636363", "00", "Rw"}, + {"x,,hcd", "", "10feffff0400006400", "00", "rw"}, + {"x,,hcd", "1234567", "10feffff04432d1701", "00", ""}, + {"x,,hcd", "0", "10feffff0400000000", "00", ""}, + {"x,,hcd", "99999999", "10feffff0463636363", "00", ""}, + {"x,,hcd", "100000000", "10feffff0463636363", "00", "Rw"}, + {"x,,hcd", "", "10feffff0400006400", "00", "rw"}, + {"x,,str:16", "0123456789ABCDEF", "10feffff1130313233343536373839414243444546", "00", ""}, + {"x,,uch:17", "", "10feffff00", "00", "c"}, + {"x,s,uch", "0", "1025ffff0310111213", "0300010203", "W"}, + {"x,s,uch", "0", "1025ffff00", "0100", ""}, + {"x,s,uch,,,,y,m,uch", "3;2", "1025ffff0103", "0102", ""}, + {"x,,uch", "38", "10feffff0126", "00", ""}, + {"x,,uch", "38.5", "10feffff0126", "00", "R"}, + {"x,,uch", "0", "10feffff0100", "00", ""}, + {"x,,uch", "254", "10feffff01fe", "00", ""}, + {"x,,uch", "-", "10feffff01ff", "00", ""}, + {"x,,uch,10", "3.8", "10feffff0126", "00", ""}, + {"x,,uch,-10", "380", "10feffff0126", "00", ""}, + {"x,,uch,=48", "", "10feffff01ab", "00", "W"}, + {"x,,uch,==48", "", "10feffff01ab", "00", "rW"}, + {"x,,uch,=48", "", "10feffff0130", "00", ""}, + {"x,,uch,==48", "", "10feffff0130", "00", ""}, + {"x,,sch", "-90", "10feffff01a6", "00", ""}, + {"x,,sch", "0", "10feffff0100", "00", ""}, + {"x,,sch", "-1", "10feffff01ff", "00", ""}, + {"x,,sch", "-", "10feffff0180", "00", ""}, + {"x,,sch", "-127", "10feffff0181", "00", ""}, + {"x,,sch", "127", "10feffff017f", "00", ""}, + {"x,,sch,10", "-9.0", "10feffff01a6", "00", ""}, + {"x,,sch,-10", "-900", "10feffff01a6", "00", ""}, + {"x,,d1b", "-90", "10feffff01a6", "00", ""}, + {"x,,d1b", "0", "10feffff0100", "00", ""}, + {"x,,d1b", "-1", "10feffff01ff", "00", ""}, + {"x,,d1b", "-", "10feffff0180", "00", ""}, + {"x,,d1b", "-127", "10feffff0181", "00", ""}, + {"x,,d1b", "127", "10feffff017f", "00", ""}, + {"x,,d1b,-10", "-900", "10feffff01a6", "00", ""}, + {"x,,d1c", "19.5", "10feffff0127", "00", ""}, + {"x,,d1c", "0.0", "10feffff0100", "00", ""}, + {"x,,d1c", "100.0", "10feffff01c8", "00", ""}, + {"x,,d1c", "-", "10feffff01ff", "00", ""}, + {"x,,uin", "38", "10feffff022600", "00", ""}, + {"x,,uin", "0", "10feffff020000", "00", ""}, + {"x,,uin", "65534", "10feffff02feff", "00", ""}, + {"x,,uin", "-", "10feffff02ffff", "00", ""}, + {"x,,uin,10", "3.8", "10feffff022600", "00", ""}, + {"x,,uin,-10", "380", "10feffff022600", "00", ""}, + {"x,,uir", "38", "10feffff020026", "00", ""}, + {"x,,uir", "0", "10feffff020000", "00", ""}, + {"x,,uir", "65534", "10feffff02fffe", "00", ""}, + {"x,,uir", "-", "10feffff02ffff", "00", ""}, + {"x,,uir,10", "3.8", "10feffff020026", "00", ""}, + {"x,,uir,-10", "380", "10feffff020026", "00", ""}, + {"uin10,uin,-10", "", "", "", "t"}, // template + {"x,,uin10", "380", "10feffff022600", "00", ""}, // template reference + {"x,,uin10,-10", "3800", "10feffff022600", "00", ""}, // template reference, valid divider product + {"x,,uin10,10", "", "", "", "c"}, // template reference, invalid divider product + {"x,,sin", "-90", "10feffff02a6ff", "00", ""}, + {"x,,sin", "0", "10feffff020000", "00", ""}, + {"x,,sin", "-1", "10feffff02ffff", "00", ""}, + {"x,,sin", "-", "10feffff020080", "00", ""}, + {"x,,sin", "-32767", "10feffff020180", "00", ""}, + {"x,,sin", "32767", "10feffff02ff7f", "00", ""}, + {"x,,sin,10", "-9.0", "10feffff02a6ff", "00", ""}, + {"x,,sin,-10", "-900", "10feffff02a6ff", "00", ""}, + {"x,,sir", "-90", "10feffff02ffa6", "00", ""}, + {"x,,sir", "0", "10feffff020000", "00", ""}, + {"x,,sir", "-1", "10feffff02ffff", "00", ""}, + {"x,,sir", "-", "10feffff028000", "00", ""}, + {"x,,sir", "-32767", "10feffff028001", "00", ""}, + {"x,,sir", "32767", "10feffff027fff", "00", ""}, + {"x,,sir,10", "-9.0", "10feffff02ffa6", "00", ""}, + {"x,,sir,-10", "-900", "10feffff02ffa6", "00", ""}, + {"x,,u3n", "38", "10feffff03260000", "00", ""}, + {"x,,u3n", "0", "10feffff03000000", "00", ""}, + {"x,,u3n", "16777214", "10feffff03feffff", "00", ""}, + {"x,,u3n", "-", "10feffff03ffffff", "00", ""}, + {"x,,u3n,10", "3.8", "10feffff03260000", "00", ""}, + {"x,,u3n,-10", "380", "10feffff03260000", "00", ""}, + {"x,,u3r", "38", "10feffff03000026", "00", ""}, + {"x,,u3r", "0", "10feffff03000000", "00", ""}, + {"x,,u3r", "16777214", "10feffff03fffffe", "00", ""}, + {"x,,u3r", "-", "10feffff03ffffff", "00", ""}, + {"x,,u3r,10", "3.8", "10feffff03000026", "00", ""}, + {"x,,u3r,-10", "380", "10feffff03000026", "00", ""}, + {"x,,s3n", "-90", "10feffff03a6ffff", "00", ""}, + {"x,,s3n", "0", "10feffff03000000", "00", ""}, + {"x,,s3n", "-1", "10feffff03ffffff", "00", ""}, + {"x,,s3n", "-", "10feffff03000080", "00", ""}, + {"x,,s3n", "-8388607", "10feffff03010080", "00", ""}, + {"x,,s3n", "8388607", "10feffff03ffff7f", "00", ""}, + {"x,,s3n,10", "-9.0", "10feffff03a6ffff", "00", ""}, + {"x,,s3n,-10", "-900", "10feffff03a6ffff", "00", ""}, + {"x,,s3r", "-90", "10feffff03ffffa6", "00", ""}, + {"x,,s3r", "0", "10feffff03000000", "00", ""}, + {"x,,s3r", "-1", "10feffff03ffffff", "00", ""}, + {"x,,s3r", "-", "10feffff03800000", "00", ""}, + {"x,,s3r", "-8388607", "10feffff03800001", "00", ""}, + {"x,,s3r", "8388607", "10feffff037fffff", "00", ""}, + {"x,,s3r,10", "-9.0", "10feffff03ffffa6", "00", ""}, + {"x,,s3r,-10", "-900", "10feffff03ffffa6", "00", ""}, + {"x,,flt", "-0.090", "10feffff02a6ff", "00", ""}, + {"x,,flt", "0.000", "10feffff020000", "00", ""}, + {"x,,flt", "-0.001", "10feffff02ffff", "00", ""}, + {"x,,flt", "-", "10feffff020080", "00", ""}, + {"x,,flt", "-32.767", "10feffff020180", "00", ""}, + {"x,,flt", "32.767", "10feffff02ff7f", "00", ""}, + {"x,,flr", "-0.090", "10feffff02ffa6", "00", ""}, + {"x,,flr", "0.000", "10feffff020000", "00", ""}, + {"x,,flr", "-0.001", "10feffff02ffff", "00", ""}, + {"x,,flr", "-", "10feffff028000", "00", ""}, + {"x,,flr", "-32.767", "10feffff028001", "00", ""}, + {"x,,flr", "32.767", "10feffff027fff", "00", ""}, + {"x,,exp", "-0.09", "10feffff04ec51b8bd", "00", ""}, + {"x,,exp", "0.0", "10feffff0400000000", "00", ""}, + {"x,,exp", "-0.001", "10feffff046f1283ba", "00", ""}, + {"x,,exp", "-", "10feffff040000807f", "00", ""}, + {"x,,exp", "-32.767", "10feffff04681103c2", "00", ""}, + {"x,,exp,1000", "-0.000090000", "10feffff04ec51b8bd", "00", ""}, + {"x,,exp,-100", "-9", "10feffff04ec51b8bd", "00", ""}, + {"x,,exr", "-0.09", "10feffff04bdb851ec", "00", ""}, + {"x,,exr", "0.0", "10feffff0400000000", "00", ""}, + {"x,,exr", "-0.001", "10feffff04ba83126f", "00", ""}, + {"x,,exr", "-", "10feffff047f800000", "00", ""}, + {"x,,exr", "-32.767", "10feffff04c2031168", "00", ""}, + {"x,,exr,1000", "-0.000090000", "10feffff04bdb851ec", "00", ""}, + {"x,,exr,-100", "-9", "10feffff04bdb851ec", "00", ""}, + {"x,,d2b", "18.004", "10fe0700090112", "00", ""}, + {"x,,d2b", "0.000", "10feffff020000", "00", ""}, + {"x,,d2b", "-0.004", "10feffff02ffff", "00", ""}, + {"x,,d2b", "-", "10feffff020080", "00", ""}, + {"x,,d2b", "-127.996", "10feffff020180", "00", ""}, + {"x,,d2b", "127.996", "10feffff02ff7f", "00", ""}, + {"x,,d2c", "288.06", "10fe0700090112", "00", ""}, + {"x,,d2c", "0.00", "10feffff020000", "00", ""}, + {"x,,d2c", "-0.06", "10feffff02ffff", "00", ""}, + {"x,,d2c", "-", "10feffff020080", "00", ""}, + {"x,,d2c", "-2047.94", "10feffff020180", "00", ""}, + {"x,,d2c", "2047.94", "10feffff02ff7f", "00", ""}, + {"x,,ulg", "38", "10feffff0426000000", "00", ""}, + {"x,,ulg", "0", "10feffff0400000000", "00", ""}, + {"x,,ulg", "4294967294", "10feffff04feffffff", "00", ""}, + {"x,,ulg", "-", "10feffff04ffffffff", "00", ""}, + {"x,,ulg,10", "3.8", "10feffff0426000000", "00", ""}, + {"x,,ulg,-10", "380", "10feffff0426000000", "00", ""}, + {"x,,ulg,0x0FF0F00F = VRT 350 ;0x33CCCC33=VRT 360;0x3CC3C33C=SD 17;0x66999966=SD 37;0x69969669=VRT 360+", + "VRT 350", "10feffff040FF0F00F", "00", ""}, + {"x,,ulg,0x=test", "", "10feffff040FF0F00F", "00", "c"}, + {"x,,ulr", "38", "10feffff0400000026", "00", ""}, + {"x,,ulr", "0", "10feffff0400000000", "00", ""}, + {"x,,ulr", "4294967294", "10feffff04fffffffe", "00", ""}, + {"x,,ulr", "-", "10feffff04ffffffff", "00", ""}, + {"x,,ulr,10", "3.8", "10feffff0400000026", "00", ""}, + {"x,,ulr,-10", "380", "10feffff0400000026", "00", ""}, + {"x,,slg", "-90", "10feffff04a6ffffff", "00", ""}, + {"x,,slg", "0", "10feffff0400000000", "00", ""}, + {"x,,slg", "-1", "10feffff04ffffffff", "00", ""}, + {"x,,slg,10", "-9.0", "10feffff04a6ffffff", "00", ""}, + {"x,,slg,-10", "-900", "10feffff04a6ffffff", "00", ""}, + {"x,,slr", "-90", "10feffff04ffffffa6", "00", ""}, + {"x,,slr", "0", "10feffff0400000000", "00", ""}, + {"x,,slr", "-1", "10feffff04ffffffff", "00", ""}, + {"x,,slr,10", "-9.0", "10feffff04ffffffa6", "00", ""}, + {"x,,slr,-10", "-900", "10feffff04ffffffa6", "00", ""}, + {"x,,bi3", "1", "10feffff0108", "00", ""}, + {"x,,bi3", "0", "10feffff0100", "00", ""}, + {"x,,bi3,0=off;1=on", "on", "10feffff0108", "00", ""}, + {"x,,bi3,0=off;1=on", "off", "10feffff0100", "00", ""}, + {"x,,bi3:2", "1", "10feffff0108", "00", ""}, + {"x,,bi3:2", "1", "10feffff01ef", "00", "W"}, + {"x,,bi3:2", "0", "10feffff0100", "00", ""}, + {"x,,bi3:2", "3", "10feffff0118", "00", ""}, + {"x,,bi3:2,1=on", "on", "10feffff0108", "00", ""}, + {"x,,bi3:2,1=on", "-", "10feffff0100", "00", ""}, + {"x,,bi3:2,0=off;1=on;2=auto;3=eco", "auto", "10feffff0110", "00", ""}, + {"x,,bi3:2,0=off;1=on;2=auto;3=eco", "2.5", "10feffff0110", "00", "R"}, + {"x,,bi3:2,0=off;1=on", "on", "10feffff0108", "00", ""}, + {"x,,bi3:2,0=off;1=on", "off", "10feffff0100", "00", ""}, + {"x,,bi3:2,0=off;1=on", "1", "10feffff0108", "00", "n"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "x=on ja/nein [Wahrheitswert]", "10feffff0108", "00", "vvv"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "x=1 ja/nein [Wahrheitswert]", "10feffff0108", "00", "vvvn"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": \"on\"}", "10feffff0108", "00", "vj"}, + {",,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"\", \"value\": \"on\"}", "10feffff0108", "00", "vj"}, + {",,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"\", \"value\": \"on\"}", "10feffff0108", "00", "j"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": \"on\", \"unit\": \"ja/nein\", \"comment\": \"Wahrheitswert\"}", "10feffff0108", "00", "vvvj"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": 1}", "10feffff0108", "00", "vnj"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"x\": {\"value\": 1, \"unit\": \"ja/nein\", \"comment\": \"Wahrheitswert\"}", "10feffff0108", "00", "vvvnj"}, + {"x,,bi3:2,0=off;1=on,ja/nein,Wahrheitswert", "\n \"0\": {\"name\": \"x\", \"value\": 1}", "10feffff0108", "00", "nj"}, + {"x,,uch,1=test;2=high;3=off;0x10=on", "on", "10feffff0110", "00", ""}, + {"x,s,uch", "3", "1050ffff00", "0103", ""}, + {"x,,d2b,,°C,Aussentemperatur", "x=18.004 °C [Aussentemperatur]", "10fe0700090112", "00", "vvv"}, + {"x,,bti,,,,y,,bda,,,,z,,bdy", "21:04:58;26.10.2014;Sun", "10fe0700085804212610061406", "00", ""}, // combination + {"x,,bi3,,,,y,,bi5", "1;0", "10feffff0108", "00", ""}, // bit combination + {"x,,bi3,,,,y,,bi5", "1;1", "10feffff0128", "00", ""}, // bit combination + {"x,,bi3,,,,y,,bi5", "0;1", "10feffff0120", "00", ""}, // bit combination + {"x,,bi3,,,,y,,bi5", "0;0", "10feffff0100", "00", ""}, // bit combination + {"x,,bi3,,,,y,,bi7,,,,t,,uch", "0;0;9", "10feffff020009", "00", ""}, // bit combination + {"x,,bi6:2,,,,y,,bi0:2,,,,t,,uch", "2;1;9", "10feffff03800109", "00", ""}, // bit combination + {"x,,BI0;BI1;BI2;BI3;BI4;BI5;BI6;BI7", "0;0;1;0;0;0;0;0", "ff75b50900", "0104", ""}, // bits + {"temp,d2b,,°C,Aussentemperatur", "", "", "", "t"}, // template with relative pos + {"x,,temp", "18.004", "10fe0700020112", "00", ""}, // reference to template + {"x,,temp,10", "1.8004", "10fe0700020112", "00", ""}, // reference to template, valid divider product + {"x,,temp,-10", "", "", "", "c"}, // reference to template, invalid divider product + {"relrel,d2b,,,,y,d1c", "", "", "", "t"}, // template struct with relative pos + {"x,,relrel", "18.004;9.5", "10fe070003011213", "00", ""}, // reference to template struct + {"trelrel,temp;temp", "", "", "", "t"}, // template struct with relative pos and ref to templates + {"x,,trelrel", "18.004;19.008", "10fe07000401120213", "00", ""}, // reference to template struct + {"x,,temp,,,,y,,d1c", "18.004;9.5", "10fe070003011213", "00", ""}, // reference to template, normal def + {"x,,temp;HEX:2", "18.004;13 14", "10fe07000401121314", "00", ""}, // reference to template and base type + {"x,,temp;HEX:2", "temp=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type + {"x,,temp:degrees;HEX:2", "degrees=18.004;=13 14", "10fe07000401121314", "00", "v"}, // reference to template and base type + }; + DataFieldTemplates* templates = new DataFieldTemplates(); + DataField* fields = NULL; + for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { + string check[5] = checks[i]; + istringstream isstr(check[0]); + string expectStr = check[1]; + SymbolString mstr(false); + result_t result = mstr.parseHex(check[2]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; + error = true; + continue; + } + SymbolString sstr(false); + result = sstr.parseHex(check[3]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; + error = true; + continue; + } + string flags = check[4]; + bool isSet = flags.find('s') != string::npos; + bool failedCreate = flags.find('c') != string::npos; + bool failedRead = flags.find('r') != string::npos; + bool failedReadMatch = flags.find('R') != string::npos; + bool failedWrite = flags.find('w') != string::npos; + bool failedWriteMatch = flags.find('W') != string::npos; + OutputFormat verbosity = 0; + if (flags.find("v") != string::npos) { + verbosity |= OF_NAMES; + } + if (flags.find("vv") != string::npos) { + verbosity |= OF_UNITS; + } + if (flags.find("vvv") != string::npos) { + verbosity |= OF_COMMENTS; + } + if (flags.find('j') != string::npos) { + verbosity |= OF_JSON; + } + bool numeric = flags.find('n') != string::npos; + bool isTemplate = flags.find('t') != string::npos; + string item; + vector entries; - while (getline(isstr, item, FIELD_SEPARATOR)) - entries.push_back(item); + while (getline(isstr, item, FIELD_SEPARATOR)) + entries.push_back(item); - if (fields != NULL) { - delete fields; - fields = NULL; - } - vector::iterator it = entries.begin(); - result = DataField::create(it, entries.end(), templates, fields, isSet, isTemplate, !isTemplate && (mstr[1] == BROADCAST || isMaster(mstr[1]))); - if (failedCreate) { - if (result == RESULT_OK) { - cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << "\"" << check[0] << "\": failed create OK" << endl; - } - continue; - } - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; - error = true; - continue; - } - if (fields == NULL) { - cout << "\"" << check[0] << "\": create error: NULL" << endl; - error = true; - continue; - } - if (it != entries.end()) { - cout << "\"" << check[0] << "\": create error: trailing input" << endl; - error = true; - continue; - } - cout << "\"" << check[0] << "\"=\""; - fields->dump(cout); - cout << "\": create OK" << endl; - if (isTemplate) { - // store new template - result = templates->add(fields, "", true); - if (result == RESULT_OK) { - fields = NULL; - cout << " store template OK" << endl; - } else { - cout << " store template error: " << getResultCode(result) << endl; - error = true; - } - continue; - } + if (fields != NULL) { + delete fields; + fields = NULL; + } + vector::iterator it = entries.begin(); + result = DataField::create(it, entries.end(), templates, fields, isSet, isTemplate, !isTemplate && (mstr[1] == BROADCAST || isMaster(mstr[1]))); + if (failedCreate) { + if (result == RESULT_OK) { + cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << "\"" << check[0] << "\": failed create OK" << endl; + } + continue; + } + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; + error = true; + continue; + } + if (fields == NULL) { + cout << "\"" << check[0] << "\": create error: NULL" << endl; + error = true; + continue; + } + if (it != entries.end()) { + cout << "\"" << check[0] << "\": create error: trailing input" << endl; + error = true; + continue; + } + cout << "\"" << check[0] << "\"=\""; + fields->dump(cout); + cout << "\": create OK" << endl; + if (isTemplate) { + // store new template + result = templates->add(fields, "", true); + if (result == RESULT_OK) { + fields = NULL; + cout << " store template OK" << endl; + } else { + cout << " store template error: " << getResultCode(result) << endl; + error = true; + } + continue; + } - ostringstream output; - SymbolString writeMstr(false); - result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10)); - if (result != RESULT_OK) { - cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl; - error = true; - } - SymbolString writeSstr(false); - result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2)); - if (result != RESULT_OK) { - cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl; - error = true; - } - result = fields->read(pt_masterData, mstr, 0, output, verbosity|(numeric?OF_NUMERIC:0), -1, false); - if (result >= RESULT_OK) { - result = fields->read(pt_slaveData, sstr, 0, output, verbosity|(numeric?OF_NUMERIC:0), -1, !output.str().empty()); - } - if (failedRead) { - if (result >= RESULT_OK) { - cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< OK" << endl; - } - } else if (result < RESULT_OK) { - cout << " read " << fields->getName() << " >" << check[2] << " " << check[3] - << "< error: " << getResultCode(result) << endl; - error = true; - } else { - bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; - verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); - } + ostringstream output; + SymbolString writeMstr(false); + result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10)); + if (result != RESULT_OK) { + cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl; + error = true; + } + SymbolString writeSstr(false); + result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2)); + if (result != RESULT_OK) { + cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl; + error = true; + } + result = fields->read(pt_masterData, mstr, 0, output, verbosity|(numeric?OF_NUMERIC:0), -1, false); + if (result >= RESULT_OK) { + result = fields->read(pt_slaveData, sstr, 0, output, verbosity|(numeric?OF_NUMERIC:0), -1, !output.str().empty()); + } + if (failedRead) { + if (result >= RESULT_OK) { + cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< OK" << endl; + } + } else if (result < RESULT_OK) { + cout << " read " << fields->getName() << " >" << check[2] << " " << check[3] + << "< error: " << getResultCode(result) << endl; + error = true; + } else { + bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; + verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); + } - if (verbosity == 0) { - istringstream input(expectStr); - result = fields->write(input, pt_masterData, writeMstr, 0); - if (result >= RESULT_OK) { - result = fields->write(input, pt_slaveData, writeSstr, 0); - } - if (failedWrite) { - if (result >= RESULT_OK) { - cout << " failed write " << fields->getName() << " >" - << expectStr << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed write " << fields->getName() << " >" - << expectStr << "< OK" << endl; - } - } else if (result < RESULT_OK) { - cout << " write " << fields->getName() << " >" << expectStr - << "< error: " << getResultCode(result) << endl; - error = true; - } else { - bool match = mstr == writeMstr && sstr == writeSstr; - verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false)); - } - } - delete fields; - fields = NULL; - } + if (verbosity == 0) { + istringstream input(expectStr); + result = fields->write(input, pt_masterData, writeMstr, 0); + if (result >= RESULT_OK) { + result = fields->write(input, pt_slaveData, writeSstr, 0); + } + if (failedWrite) { + if (result >= RESULT_OK) { + cout << " failed write " << fields->getName() << " >" + << expectStr << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed write " << fields->getName() << " >" + << expectStr << "< OK" << endl; + } + } else if (result < RESULT_OK) { + cout << " write " << fields->getName() << " >" << expectStr + << "< error: " << getResultCode(result) << endl; + error = true; + } else { + bool match = mstr == writeMstr && sstr == writeSstr; + verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false)); + } + } + delete fields; + fields = NULL; + } - delete templates; + delete templates; - return error ? 1 : 0; + return error ? 1 : 0; } diff --git a/src/lib/ebus/test/test_device.cpp b/src/lib/ebus/test/test_device.cpp index b32e0005..e1fe2276 100644 --- a/src/lib/ebus/test/test_device.cpp +++ b/src/lib/ebus/test/test_device.cpp @@ -24,39 +24,39 @@ using namespace std; using namespace ebusd; int main() { - Device* device = Device::create("/dev/ttyUSB20", true, false, false); - if (device == NULL) { - cout << "unable to create device" << endl; - return -1; - } - result_t result = device->open(); - if (result != RESULT_OK) { - cout << "open failed: " << getResultCode(result) << endl; - } else { - if (!device->isValid()) { - cout << "device not available." << endl; - } - int count = 0; + Device* device = Device::create("/dev/ttyUSB20", true, false, false); + if (device == NULL) { + cout << "unable to create device" << endl; + return -1; + } + result_t result = device->open(); + if (result != RESULT_OK) { + cout << "open failed: " << getResultCode(result) << endl; + } else { + if (!device->isValid()) { + cout << "device not available." << endl; + } + int count = 0; - while (1) { - unsigned char byte = 0; - result = device->recv(0, byte); + while (1) { + unsigned char byte = 0; + result = device->recv(0, byte); - if (result == RESULT_OK) { - cout << hex << setw(2) << setfill('0') - << static_cast(byte) << endl; - } - count++; - } + if (result == RESULT_OK) { + cout << hex << setw(2) << setfill('0') + << static_cast(byte) << endl; + } + count++; + } - device->close(); + device->close(); - if (!device->isValid()) { - cout << "close successful." << endl; - } - } + if (!device->isValid()) { + cout << "close successful." << endl; + } + } - delete device; + delete device; - return 0; + return 0; } diff --git a/src/lib/ebus/test/test_filereader.cpp b/src/lib/ebus/test/test_filereader.cpp index 6f722611..54ebcf6e 100644 --- a/src/lib/ebus/test/test_filereader.cpp +++ b/src/lib/ebus/test/test_filereader.cpp @@ -28,70 +28,70 @@ using namespace ebusd; static bool error = false; void verify(bool expectFailMatch, string type, string input, - bool match, string expectStr, string gotStr) { - match = match && expectStr == gotStr; - if (expectFailMatch) { - if (match) { - cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed " << type << " match >" << input << "< OK" << endl; - } - } else if (match) { - cout << " " << type << " match >" << input << "< OK" << endl; - } else { - cout << " " << type << " match >" << input << "< error: got >" - << gotStr << "<, expected >" << expectStr << "<" << endl; - error = true; - } + bool match, string expectStr, string gotStr) { + match = match && expectStr == gotStr; + if (expectFailMatch) { + if (match) { + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + } else if (match) { + cout << " " << type << " match >" << input << "< OK" << endl; + } else { + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; + error = true; + } } int main() { - istringstream ifs( - "line 1 col 1,line 1 col 2,line 1 col 3\n" - "line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n" - "line 4 col 1,\"line 4 col 2 part 1\n" - "line 4 col 2 part 2\",line 4 col 3\n" - ",,,\n" - "line 6 col 1,,line 6 col 3\n" - "line 8 col 1,\"line 8 col 2 part 1;\n" - "line 8 col 2 part 2\",line 8 col 3\n" - ); - string resultlines[][3] = { - {"line 1 col 1", "line 1 col 2", "line 1 col 3"}, - {"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""}, - {"", "", ""}, - {"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"}, - {"", "", ""}, - {"line 6 col 1", "", "line 6 col 3"}, - {"", "", ""}, - {"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"}, - }; - unsigned int lineNo = 0; - vector row; + istringstream ifs( + "line 1 col 1,line 1 col 2,line 1 col 3\n" + "line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n" + "line 4 col 1,\"line 4 col 2 part 1\n" + "line 4 col 2 part 2\",line 4 col 3\n" + ",,,\n" + "line 6 col 1,,line 6 col 3\n" + "line 8 col 1,\"line 8 col 2 part 1;\n" + "line 8 col 2 part 2\",line 8 col 3\n" + ); + string resultlines[][3] = { + {"line 1 col 1", "line 1 col 2", "line 1 col 3"}, + {"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""}, + {"", "", ""}, + {"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"}, + {"", "", ""}, + {"line 6 col 1", "", "line 6 col 3"}, + {"", "", ""}, + {"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"}, + }; + unsigned int lineNo = 0; + vector row; - while (FileReader::splitFields(ifs, row, lineNo)) { - cout << "line " << static_cast(lineNo) << ": split OK" << endl; - string resultline[3] = resultlines[lineNo-1]; - if (row.empty()) { - cout << " result empty"; - if (resultline[0] == "") { - cout << ": OK" << endl; - } else { - cout << ": error" << endl; - error = true; - } - continue; - } - for (vector::iterator it = row.begin(); it != row.end(); it++) { - string got = *it; - string expect = resultline[distance(row.begin(), it)]; - ostringstream type; - type << "line " << static_cast(lineNo) << " col " << static_cast(distance(row.begin(), it)+1); - verify(false, type.str(), expect, got == expect, expect, got); - } - } + while (FileReader::splitFields(ifs, row, lineNo)) { + cout << "line " << static_cast(lineNo) << ": split OK" << endl; + string resultline[3] = resultlines[lineNo-1]; + if (row.empty()) { + cout << " result empty"; + if (resultline[0] == "") { + cout << ": OK" << endl; + } else { + cout << ": error" << endl; + error = true; + } + continue; + } + for (vector::iterator it = row.begin(); it != row.end(); it++) { + string got = *it; + string expect = resultline[distance(row.begin(), it)]; + ostringstream type; + type << "line " << static_cast(lineNo) << " col " << static_cast(distance(row.begin(), it)+1); + verify(false, type.str(), expect, got == expect, expect, got); + } + } - return error ? 1 : 0; + return error ? 1 : 0; } diff --git a/src/lib/ebus/test/test_message.cpp b/src/lib/ebus/test/test_message.cpp index 880b1c21..f904b062 100644 --- a/src/lib/ebus/test/test_message.cpp +++ b/src/lib/ebus/test/test_message.cpp @@ -27,397 +27,397 @@ using namespace ebusd; void verify(bool expectFailMatch, string type, string input, - bool match, string expectStr, string gotStr) { - if (expectFailMatch) { - if (match) { - cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << endl; - } else { - cout << " failed " << type << " match >" << input << "< OK" << endl; - } - } else if (match) { - cout << " " << type << " match >" << input << "< OK" << endl; - } else { - cout << " " << type << " match >" << input << "< error: got >" - << gotStr << "<, expected >" << expectStr << "<" << endl; - } + bool match, string expectStr, string gotStr) { + if (expectFailMatch) { + if (match) { + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + } else { + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + } else if (match) { + cout << " " << type << " match >" << input << "< OK" << endl; + } else { + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; + } } DataFieldTemplates* templates = NULL; namespace ebusd { DataFieldTemplates* getTemplates(const string filename) { - if (filename == "") { // avoid compiler warning - return templates; - } - return templates; + if (filename == "") { // avoid compiler warning + return templates; + } + return templates; } } int main() { - // message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... - // field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] - // template: name,type[:len][,[divisor|values][,[unit][,[comment]]]] - // condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values - string checks[][5] = { - // "message", "decoded", "master", "slave", "flags" - {"date,HDA:3,,,Datum", "", "", "", "template"}, - {"time,VTI,,,", "", "", "", "template"}, - {"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"}, - {"temp,D2C,,°C,Temperatur", "", "", "", "template"}, - {"temp1,D1C,,°C,Temperatur", "", "", "", "template"}, - {"temp2,D2B,,°C,Temperatur", "", "", "", "template"}, - {"power,UCH,,kW", "", "", "", "template"}, - {"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"}, - {"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"}, - {"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"}, - {"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"}, - {"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"}, - {"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, - {"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, - {"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"}, - {"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"}, - {"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"}, - {"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""}, - {"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"}, - {"w,,first,,,15,b509", "", "ff15b50900", "00", "m"}, - {"w,,,,,,b505,2d", "", "", "", "defaults"}, - {"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"}, - {"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"}, - {"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"}, - {"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"}, - {"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"}, - {"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, - {"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, - {"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, - {"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, - {"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"}, - {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"}, - {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"}, - {"u,ehp,ActualEnvironmentPower,Energiebezug,,08,B509,29BA00,,s,IGN:2,,,,,s,power", "8", "1008b5090329ba00", "03ba0008", "pm"}, - {"uw,ehp,test,Test,,08,B5de,ab,,,power,,,,,s,hex:1", "8;39", "1008b5de02ab08", "0139", "pm"}, - {"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"}, - {"", "55.50;ok", "1025b50903290000", "050000780300", "d"}, - {"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" }, - {"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, - {"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, - {"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" }, - {"r,ehp,,,,08,b509,0d", "", "", "", "defaults" }, - {"w,ehp,,,,08,b509,0e", "", "", "", "defaults" }, - {"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" }, - {"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" }, - {"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" }, - {"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" }, - {"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" }, - {"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" }, - {"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" }, - {"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" }, - {"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" }, - {"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" }, - {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" }, - {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" }, - {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" }, - {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" }, - {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" }, - {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" }, - {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" }, - {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" }, - {"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" }, - {"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"}, - }; - templates = new DataFieldTemplates(); - MessageMap* messages = new MessageMap(); - vector< vector > defaultsRows; - map &conditions = messages->getConditions(); - Message* message = NULL; - vector deleteMessages; - vector mstrs; - vector sstrs; - mstrs.resize(1); - sstrs.resize(1); - for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { - string check[5] = checks[i]; - string inputStr = check[1]; - string flags = check[4]; - bool isTemplate = flags == "template"; - bool isCondition = flags == "condition"; - bool isDefaults = isCondition || flags == "defaults"; - bool dontMap = flags.find('m') != string::npos; - bool onlyMap = flags.find('M') != string::npos; - bool failedCreate = flags.find('c') != string::npos; - bool isChain = flags.find('C') != string::npos; - bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos; - bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos; - bool withMessageDump = flags.find('N') != string::npos; - bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos); - bool failedPrepare = flags.find('p') != string::npos; - bool failedPrepareMatch = flags.find('P') != string::npos; - bool multi = flags.find('*') != string::npos; - bool withInput = flags.find('i') != string::npos; - result_t result = RESULT_EMPTY; - vector entries; - istringstream ifs(check[0]); - unsigned int lineNo = 0; - if (!FileReader::splitFields(ifs, entries, lineNo)) { - entries.clear(); - } - if (isTemplate) { - // store new template - DataField* fields = NULL; - vector::iterator it = entries.begin(); - result = DataField::create(it, entries.end(), templates, fields, false, true, false); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl; - } else if (it != entries.end()) { - cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast(entries.end()-it) << endl; - } else { - cout << "\"" << check[0] << "\": create template OK" << endl; - result = templates->add(fields, "", true); - if (result == RESULT_OK) { - cout << " store template OK" << endl; - } else { - cout << " store template error: " << getResultCode(result) << endl; - delete fields; - } - } - continue; - } - if (isDefaults) { - // store defaults or condition - vector::iterator it = entries.begin(); - size_t oldSize = conditions.size(); - result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl; - } else if (it != entries.end()) { - cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast(entries.end()-it) << endl; - } else { - cout << "\"" << check[0] << "\": read defaults OK" << endl; - if (isCondition) { - if (conditions.size() == oldSize) { - cout << " create condition error" << endl; - } else { - result = messages->resolveConditions(); - if (result != RESULT_OK) { - cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl; - } else { - cout << " resolve conditions OK" << endl; - } - } - } - } - continue; - } - if (isChain) { - size_t pos = 0; - string token; - istringstream stream(check[2]); - while (getline(stream, token, VALUE_SEPARATOR)) { - if (pos >= mstrs.size()) { - mstrs.resize(pos+1); - } else if (mstrs[pos] != NULL) { - delete mstrs[pos]; - } - mstrs[pos] = new SymbolString(false); - result = mstrs[pos]->parseHex(token); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; - break; - } - pos++; - } - pos = 0; - stream.str(check[3]); - stream.clear(); - while (getline(stream, token, VALUE_SEPARATOR)) { - if (pos >= sstrs.size()) { - sstrs.resize(pos+1); - } else if (sstrs[pos] != NULL) { - delete sstrs[pos]; - } - sstrs[pos] = new SymbolString(false); - result = sstrs[pos]->parseHex(token); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; - break; - } - pos++; - } - if (result != RESULT_OK) { - continue; - } - } else { - if (mstrs[0] != NULL) { - delete mstrs[0]; - } - mstrs[0] = new SymbolString(false); - result = mstrs[0]->parseHex(check[2]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; - continue; - } - if (sstrs[0] != NULL) { - delete sstrs[0]; - } - sstrs[0] = new SymbolString(false); - result = sstrs[0]->parseHex(check[3]); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; - continue; - } - } + // message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... + // field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] + // template: name,type[:len][,[divisor|values][,[unit][,[comment]]]] + // condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values + string checks[][5] = { + // "message", "decoded", "master", "slave", "flags" + {"date,HDA:3,,,Datum", "", "", "", "template"}, + {"time,VTI,,,", "", "", "", "template"}, + {"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"}, + {"temp,D2C,,°C,Temperatur", "", "", "", "template"}, + {"temp1,D1C,,°C,Temperatur", "", "", "", "template"}, + {"temp2,D2B,,°C,Temperatur", "", "", "", "template"}, + {"power,UCH,,kW", "", "", "", "template"}, + {"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"}, + {"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"}, + {"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"}, + {"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"}, + {"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"}, + {"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, + {"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, + {"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"}, + {"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"}, + {"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"}, + {"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""}, + {"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"}, + {"w,,first,,,15,b509", "", "ff15b50900", "00", "m"}, + {"w,,,,,,b505,2d", "", "", "", "defaults"}, + {"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"}, + {"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"}, + {"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"}, + {"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"}, + {"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"}, + {"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, + {"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, + {"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, + {"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, + {"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"}, + {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"}, + {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"}, + {"u,ehp,ActualEnvironmentPower,Energiebezug,,08,B509,29BA00,,s,IGN:2,,,,,s,power", "8", "1008b5090329ba00", "03ba0008", "pm"}, + {"uw,ehp,test,Test,,08,B5de,ab,,,power,,,,,s,hex:1", "8;39", "1008b5de02ab08", "0139", "pm"}, + {"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"}, + {"", "55.50;ok", "1025b50903290000", "050000780300", "d"}, + {"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" }, + {"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, + {"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, + {"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" }, + {"r,ehp,,,,08,b509,0d", "", "", "", "defaults" }, + {"w,ehp,,,,08,b509,0e", "", "", "", "defaults" }, + {"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" }, + {"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" }, + {"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" }, + {"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" }, + {"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" }, + {"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" }, + {"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" }, + {"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" }, + {"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" }, + {"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" }, + {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" }, + {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" }, + {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" }, + {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" }, + {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" }, + {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" }, + {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" }, + {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" }, + {"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" }, + {"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"}, + }; + templates = new DataFieldTemplates(); + MessageMap* messages = new MessageMap(); + vector< vector > defaultsRows; + map &conditions = messages->getConditions(); + Message* message = NULL; + vector deleteMessages; + vector mstrs; + vector sstrs; + mstrs.resize(1); + sstrs.resize(1); + for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { + string check[5] = checks[i]; + string inputStr = check[1]; + string flags = check[4]; + bool isTemplate = flags == "template"; + bool isCondition = flags == "condition"; + bool isDefaults = isCondition || flags == "defaults"; + bool dontMap = flags.find('m') != string::npos; + bool onlyMap = flags.find('M') != string::npos; + bool failedCreate = flags.find('c') != string::npos; + bool isChain = flags.find('C') != string::npos; + bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos; + bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos; + bool withMessageDump = flags.find('N') != string::npos; + bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos); + bool failedPrepare = flags.find('p') != string::npos; + bool failedPrepareMatch = flags.find('P') != string::npos; + bool multi = flags.find('*') != string::npos; + bool withInput = flags.find('i') != string::npos; + result_t result = RESULT_EMPTY; + vector entries; + istringstream ifs(check[0]); + unsigned int lineNo = 0; + if (!FileReader::splitFields(ifs, entries, lineNo)) { + entries.clear(); + } + if (isTemplate) { + // store new template + DataField* fields = NULL; + vector::iterator it = entries.begin(); + result = DataField::create(it, entries.end(), templates, fields, false, true, false); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl; + } else if (it != entries.end()) { + cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast(entries.end()-it) << endl; + } else { + cout << "\"" << check[0] << "\": create template OK" << endl; + result = templates->add(fields, "", true); + if (result == RESULT_OK) { + cout << " store template OK" << endl; + } else { + cout << " store template error: " << getResultCode(result) << endl; + delete fields; + } + } + continue; + } + if (isDefaults) { + // store defaults or condition + vector::iterator it = entries.begin(); + size_t oldSize = conditions.size(); + result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl; + } else if (it != entries.end()) { + cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast(entries.end()-it) << endl; + } else { + cout << "\"" << check[0] << "\": read defaults OK" << endl; + if (isCondition) { + if (conditions.size() == oldSize) { + cout << " create condition error" << endl; + } else { + result = messages->resolveConditions(); + if (result != RESULT_OK) { + cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl; + } else { + cout << " resolve conditions OK" << endl; + } + } + } + } + continue; + } + if (isChain) { + size_t pos = 0; + string token; + istringstream stream(check[2]); + while (getline(stream, token, VALUE_SEPARATOR)) { + if (pos >= mstrs.size()) { + mstrs.resize(pos+1); + } else if (mstrs[pos] != NULL) { + delete mstrs[pos]; + } + mstrs[pos] = new SymbolString(false); + result = mstrs[pos]->parseHex(token); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; + break; + } + pos++; + } + pos = 0; + stream.str(check[3]); + stream.clear(); + while (getline(stream, token, VALUE_SEPARATOR)) { + if (pos >= sstrs.size()) { + sstrs.resize(pos+1); + } else if (sstrs[pos] != NULL) { + delete sstrs[pos]; + } + sstrs[pos] = new SymbolString(false); + result = sstrs[pos]->parseHex(token); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; + break; + } + pos++; + } + if (result != RESULT_OK) { + continue; + } + } else { + if (mstrs[0] != NULL) { + delete mstrs[0]; + } + mstrs[0] = new SymbolString(false); + result = mstrs[0]->parseHex(check[2]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; + continue; + } + if (sstrs[0] != NULL) { + delete sstrs[0]; + } + sstrs[0] = new SymbolString(false); + result = sstrs[0]->parseHex(check[3]); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; + continue; + } + } - if (deleteMessages.size() > 0) { - for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { - Message* deleteMessage = *it; - delete deleteMessage; - } - deleteMessages.clear(); - } - if (entries.size() == 0) { - message = messages->find(*mstrs[0]); - if (message == NULL) { - cout << "\"" << check[2] << "\": find error: NULL" << endl; - continue; - } - cout << "\"" << check[2] << "\": find OK" << endl; - } else { - vector::iterator it = entries.begin(); - string types = *it; - Condition* condition = NULL; - result = messages->readConditions(types, "no file", condition); - if (result == RESULT_OK) { - *it = types; - result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages); - } - if (failedCreate) { - if (result == RESULT_OK) { - cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; - } else { - cout << "\"" << check[0] << "\": failed create OK" << endl; - } - continue; - } - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": create error: " - << getResultCode(result) << endl; - printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result); - continue; - } - if (deleteMessages.size() == 0) { - cout << "\"" << check[0] << "\": create error: NULL" << endl; - continue; - } - if (it != entries.end()) { - cout << "\"" << check[0] << "\": create error: trailing input " << static_cast(entries.end()-it) << endl; - continue; - } - if (multi && deleteMessages.size() == 1) { - cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl; - continue; - } - if (!multi && deleteMessages.size() > 1) { - cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl; - continue; - } - cout << "\"" << check[0] << "\": create OK" << endl; - if (!dontMap) { - result_t result = RESULT_OK; - for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { - Message* deleteMessage = *it; - result_t result = messages->add(deleteMessage); - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": add error: " - << getResultCode(result) << endl; - break; - } - } - if (result != RESULT_OK) { - continue; - } - cout << " map OK" << endl; - message = deleteMessages.front(); - deleteMessages.clear(); - if (onlyMap) { - continue; - } - Message* foundMessage = messages->find(*mstrs[0]); - if (foundMessage == message) { - cout << " find OK" << endl; - } else if (foundMessage == NULL) { - cout << " find error: NULL" << endl; - } else { - cout << " find error: different" << endl; - } - } else { - message = deleteMessages.front(); - } - } + if (deleteMessages.size() > 0) { + for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { + Message* deleteMessage = *it; + delete deleteMessage; + } + deleteMessages.clear(); + } + if (entries.size() == 0) { + message = messages->find(*mstrs[0]); + if (message == NULL) { + cout << "\"" << check[2] << "\": find error: NULL" << endl; + continue; + } + cout << "\"" << check[2] << "\": find OK" << endl; + } else { + vector::iterator it = entries.begin(); + string types = *it; + Condition* condition = NULL; + result = messages->readConditions(types, "no file", condition); + if (result == RESULT_OK) { + *it = types; + result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages); + } + if (failedCreate) { + if (result == RESULT_OK) { + cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; + } else { + cout << "\"" << check[0] << "\": failed create OK" << endl; + } + continue; + } + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": create error: " + << getResultCode(result) << endl; + printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result); + continue; + } + if (deleteMessages.size() == 0) { + cout << "\"" << check[0] << "\": create error: NULL" << endl; + continue; + } + if (it != entries.end()) { + cout << "\"" << check[0] << "\": create error: trailing input " << static_cast(entries.end()-it) << endl; + continue; + } + if (multi && deleteMessages.size() == 1) { + cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl; + continue; + } + if (!multi && deleteMessages.size() > 1) { + cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl; + continue; + } + cout << "\"" << check[0] << "\": create OK" << endl; + if (!dontMap) { + result_t result = RESULT_OK; + for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { + Message* deleteMessage = *it; + result_t result = messages->add(deleteMessage); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": add error: " + << getResultCode(result) << endl; + break; + } + } + if (result != RESULT_OK) { + continue; + } + cout << " map OK" << endl; + message = deleteMessages.front(); + deleteMessages.clear(); + if (onlyMap) { + continue; + } + Message* foundMessage = messages->find(*mstrs[0]); + if (foundMessage == message) { + cout << " find OK" << endl; + } else if (foundMessage == NULL) { + cout << " find error: NULL" << endl; + } else { + cout << " find error: different" << endl; + } + } else { + message = deleteMessages.front(); + } + } - if (message->isPassive() || decode) { - ostringstream output; - for (unsigned char index = 0; index < message->getCount(); index++) { - message->storeLastData(*mstrs[index], *sstrs[index]); - } - if (withMessageDump && !decodeJson) { - message->dump(output, NULL, true); - output << ": "; - } - result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false); - if (result != RESULT_OK) { - cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: " - << getResultCode(result) << endl; - continue; - } - cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl; - bool match = inputStr == output.str(); - verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str()); - } - if (!message->isPassive() && (withInput || !decode)) { - istringstream input(inputStr); - SymbolString writeMstr(false); - result = message->prepareMaster(0xff, writeMstr, input); - if (failedPrepare) { - if (result == RESULT_OK) { - cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl; - } else { - cout << " \"" << inputStr << "\": failed prepare OK" << endl; - } - continue; - } + if (message->isPassive() || decode) { + ostringstream output; + for (unsigned char index = 0; index < message->getCount(); index++) { + message->storeLastData(*mstrs[index], *sstrs[index]); + } + if (withMessageDump && !decodeJson) { + message->dump(output, NULL, true); + output << ": "; + } + result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false); + if (result != RESULT_OK) { + cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: " + << getResultCode(result) << endl; + continue; + } + cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl; + bool match = inputStr == output.str(); + verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str()); + } + if (!message->isPassive() && (withInput || !decode)) { + istringstream input(inputStr); + SymbolString writeMstr(false); + result = message->prepareMaster(0xff, writeMstr, input); + if (failedPrepare) { + if (result == RESULT_OK) { + cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl; + } else { + cout << " \"" << inputStr << "\": failed prepare OK" << endl; + } + continue; + } - if (result != RESULT_OK) { - cout << " \"" << inputStr << "\": prepare error: " - << getResultCode(result) << endl; - continue; - } - cout << " \"" << inputStr << "\": prepare OK" << endl; + if (result != RESULT_OK) { + cout << " \"" << inputStr << "\": prepare error: " + << getResultCode(result) << endl; + continue; + } + cout << " \"" << inputStr << "\": prepare OK" << endl; - bool match = writeMstr == *mstrs[0]; - verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false)); - } - } + bool match = writeMstr == *mstrs[0]; + verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false)); + } + } - if (deleteMessages.size() > 0) { - for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { - Message* deleteMessage = *it; - delete deleteMessage; - } - deleteMessages.clear(); - } + if (deleteMessages.size() > 0) { + for (vector::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { + Message* deleteMessage = *it; + delete deleteMessage; + } + deleteMessages.clear(); + } - delete templates; - delete messages; - for (vector::iterator it = mstrs.begin(); it != mstrs.end(); it++) { - delete *it; - } - for (vector::iterator it = sstrs.begin(); it != sstrs.end(); it++) { - delete *it; - } - return 0; + delete templates; + delete messages; + for (vector::iterator it = mstrs.begin(); it != mstrs.end(); it++) { + delete *it; + } + for (vector::iterator it = sstrs.begin(); it != sstrs.end(); it++) { + delete *it; + } + return 0; } diff --git a/src/lib/ebus/test/test_symbol.cpp b/src/lib/ebus/test/test_symbol.cpp index d168c62f..a1d7f2b0 100644 --- a/src/lib/ebus/test/test_symbol.cpp +++ b/src/lib/ebus/test/test_symbol.cpp @@ -27,70 +27,70 @@ using namespace ebusd; static bool error = false; void verify(bool expectFailMatch, string type, string input, - bool match, string expectStr, string gotStr) { - match = match && expectStr == gotStr; - if (expectFailMatch) { - if (match) { - cout << " failed " << type << " match >" << input - << "< error: unexpectedly succeeded" << endl; - error = true; - } else { - cout << " failed " << type << " match >" << input << "< OK" << endl; - } - } else if (match) { - cout << " " << type << " match >" << input << "< OK" << endl; - } else { - cout << " " << type << " match >" << input << "< error: got >" - << gotStr << "<, expected >" << expectStr << "<" << endl; - error = true; - } + bool match, string expectStr, string gotStr) { + match = match && expectStr == gotStr; + if (expectFailMatch) { + if (match) { + cout << " failed " << type << " match >" << input + << "< error: unexpectedly succeeded" << endl; + error = true; + } else { + cout << " failed " << type << " match >" << input << "< OK" << endl; + } + } else if (match) { + cout << " " << type << " match >" << input << "< OK" << endl; + } else { + cout << " " << type << " match >" << input << "< error: got >" + << gotStr << "<, expected >" << expectStr << "<" << endl; + error = true; + } } int main(int argc, char** argv) { - SymbolString sstr(true); + SymbolString sstr(true); - if (argc > 1) { - result_t result = sstr.parseHex(argv[1], true); - if (result != RESULT_OK) { - cout << "parse escaped error: " << getResultCode(result) << endl; - } else { - unsigned char gotCrc = sstr.getCRC(); - cout << "calculated CRC: 0x" - << nouppercase << setw(2) << hex << setfill('0') - << static_cast(gotCrc) << endl; - } - return 0; - } - string gotStr, expectStr; - result_t result = sstr.parseHex("10feb5050427a915aa", false); - if (result != RESULT_OK) { - cout << "parse escaped error: " << getResultCode(result) << endl; - error = true; - } else { - gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177"; - verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr); - unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; - ostringstream ostr; - ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast(expectCrc); - expectStr = ostr.str(); - ostr.str(""); - ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast(gotCrc); - gotStr = ostr.str(); - verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr); + if (argc > 1) { + result_t result = sstr.parseHex(argv[1], true); + if (result != RESULT_OK) { + cout << "parse escaped error: " << getResultCode(result) << endl; + } else { + unsigned char gotCrc = sstr.getCRC(); + cout << "calculated CRC: 0x" + << nouppercase << setw(2) << hex << setfill('0') + << static_cast(gotCrc) << endl; + } + return 0; + } + string gotStr, expectStr; + result_t result = sstr.parseHex("10feb5050427a915aa", false); + if (result != RESULT_OK) { + cout << "parse escaped error: " << getResultCode(result) << endl; + error = true; + } else { + gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177"; + verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr); + unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; + ostringstream ostr; + ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast(expectCrc); + expectStr = ostr.str(); + ostr.str(""); + ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast(gotCrc); + gotStr = ostr.str(); + verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr); - gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; - verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr); - } + gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; + verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr); + } - sstr = SymbolString(false); - result = sstr.parseHex("10feb5050427a90015a90177", true); - if (result != RESULT_OK) { - cout << "parse unescaped error: " << getResultCode(result) << endl; - error = true; - } else { - gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; - verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr); - } + sstr = SymbolString(false); + result = sstr.parseHex("10feb5050427a90015a90177", true); + if (result != RESULT_OK) { + cout << "parse unescaped error: " << getResultCode(result) << endl; + error = true; + } else { + gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; + verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr); + } - return error ? 1 : 0; + return error ? 1 : 0; } diff --git a/src/lib/utils/clock.cpp b/src/lib/utils/clock.cpp index 61a25889..82e25de1 100644 --- a/src/lib/utils/clock.cpp +++ b/src/lib/utils/clock.cpp @@ -18,8 +18,8 @@ #include "clock.h" #ifdef __MACH__ -# include -# include +# include +# include #endif #ifdef __MACH__ @@ -29,15 +29,15 @@ static clock_serv_t clockServ; void clockGettime(struct timespec* t) { #ifdef __MACH__ - if (!clockInitialized) { - clockInitialized = true; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ); - } - mach_timespec_t mts; - clock_get_time(clockServ, &mts); - t->tv_sec = mts.tv_sec; - t->tv_nsec = mts.tv_nsec; + if (!clockInitialized) { + clockInitialized = true; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ); + } + mach_timespec_t mts; + clock_get_time(clockServ, &mts); + t->tv_sec = mts.tv_sec; + t->tv_nsec = mts.tv_nsec; #else - clock_gettime(CLOCK_REALTIME, t); + clock_gettime(CLOCK_REALTIME, t); #endif } diff --git a/src/lib/utils/log.cpp b/src/lib/utils/log.cpp index 7fa7de45..beb40eb5 100644 --- a/src/lib/utils/log.cpp +++ b/src/lib/utils/log.cpp @@ -27,23 +27,23 @@ /** the name of each @a LogFacility. */ static const char *facilityNames[] = { - "main", - "network", - "bus", - "update", - "other", - "all", - NULL + "main", + "network", + "bus", + "update", + "other", + "all", + NULL }; /** the name of each @a LogLevel. */ static const char* levelNames[] = { - "none", - "error", - "notice", - "info", - "debug", - NULL + "none", + "error", + "notice", + "info", + "debug", + NULL }; /** the bit combination of currently active log facilities (1 << @a LogFacility). */ @@ -56,119 +56,119 @@ static LogLevel s_logLevel = ll_notice; static FILE* s_logFile = stdout; bool setLogFacilities(const char* facilities) { - char *input = strdup(facilities); - char *opt = reinterpret_cast(input), *value = NULL; - int newFacilites = 0; - while (*opt) { - int val = getsubopt(&opt, (char *const *)facilityNames, &value); - if (val < 0 || val > lf_COUNT || value) { - free(input); - return false; - } - if (val == lf_COUNT) { - newFacilites = LF_ALL; - } else { - newFacilites |= 1 << val; - } - } - //s_lastFacilities = newFacilites; - s_logFacilites = newFacilites; - free(input); - return true; + char *input = strdup(facilities); + char *opt = reinterpret_cast(input), *value = NULL; + int newFacilites = 0; + while (*opt) { + int val = getsubopt(&opt, (char *const *)facilityNames, &value); + if (val < 0 || val > lf_COUNT || value) { + free(input); + return false; + } + if (val == lf_COUNT) { + newFacilites = LF_ALL; + } else { + newFacilites |= 1 << val; + } + } + //s_lastFacilities = newFacilites; + s_logFacilites = newFacilites; + free(input); + return true; } bool getLogFacilities(char* buffer) { - if (s_logFacilites == LF_ALL) { - return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0; - } - *buffer = 0; // for strcat to work - bool found = false; - size_t len = 0; - for (int val = 0; val < lf_COUNT; val++) { - if (s_logFacilites&(1 << val)) { - if (found) { - len += snprintf(buffer+len, 48-len, ","); - } - found = true; - len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]); - } - } - return true; + if (s_logFacilites == LF_ALL) { + return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0; + } + *buffer = 0; // for strcat to work + bool found = false; + size_t len = 0; + for (int val = 0; val < lf_COUNT; val++) { + if (s_logFacilites&(1 << val)) { + if (found) { + len += snprintf(buffer+len, 48-len, ","); + } + found = true; + len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]); + } + } + return true; } bool setLogLevel(const char* level) { - char *input = strdup(level); - char *opt = reinterpret_cast(input), *value = NULL; - int newLevel = 0; - if (*opt) { - int val = getsubopt(&opt, (char *const *)levelNames, &value); - if (val < 0 || val >= ll_COUNT || value || *opt) { - free(input); - return false; - } - newLevel = val; - } - s_logLevel = (LogLevel)newLevel; - free(input); - return true; + char *input = strdup(level); + char *opt = reinterpret_cast(input), *value = NULL; + int newLevel = 0; + if (*opt) { + int val = getsubopt(&opt, (char *const *)levelNames, &value); + if (val < 0 || val >= ll_COUNT || value || *opt) { + free(input); + return false; + } + newLevel = val; + } + s_logLevel = (LogLevel)newLevel; + free(input); + return true; } const char* getLogLevel() { - return levelNames[s_logLevel]; + return levelNames[s_logLevel]; } bool setLogFile(const char* filename) { - FILE* newFile = fopen(filename, "a"); - if (newFile == NULL) { - return false; - } - closeLogFile(); - s_logFile = newFile; - return true; + FILE* newFile = fopen(filename, "a"); + if (newFile == NULL) { + return false; + } + closeLogFile(); + s_logFile = newFile; + return true; } void closeLogFile() { - if (s_logFile != NULL) { - if (s_logFile != stdout) { - fclose(s_logFile); - } - s_logFile = NULL; - } + if (s_logFile != NULL) { + if (s_logFile != stdout) { + fclose(s_logFile); + } + s_logFile = NULL; + } } bool needsLog(const LogFacility facility, const LogLevel level) { - return ((s_logFacilites & (1 << facility)) != 0) - && (s_logLevel >= level); + return ((s_logFacilites & (1 << facility)) != 0) + && (s_logLevel >= level); } void logWrite(const char* facility, const char* level, const char* message, va_list ap) { - struct timespec ts; - struct tm td; - clockGettime(&ts); - localtime_r(&ts.tv_sec, &td); - char* buf; - if (vasprintf(&buf, message, ap) >= 0 && buf) { - fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n", - td.tm_year+1900, td.tm_mon+1, td.tm_mday, - td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, - facility, level, buf); - fflush(s_logFile); - } - if (buf) { - free(buf); - } + struct timespec ts; + struct tm td; + clockGettime(&ts); + localtime_r(&ts.tv_sec, &td); + char* buf; + if (vasprintf(&buf, message, ap) >= 0 && buf) { + fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n", + td.tm_year+1900, td.tm_mon+1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, + facility, level, buf); + fflush(s_logFile); + } + if (buf) { + free(buf); + } } void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) { - va_list ap; - va_start(ap, message); - logWrite(facilityNames[facility], levelNames[level], message, ap); - va_end(ap); + va_list ap; + va_start(ap, message); + logWrite(facilityNames[facility], levelNames[level], message, ap); + va_end(ap); } void logWrite(const char* facility, const LogLevel level, const char* message, ...) { - va_list ap; - va_start(ap, message); - logWrite(facility, levelNames[level], message, ap); - va_end(ap); + va_list ap; + va_start(ap, message); + logWrite(facility, levelNames[level], message, ap); + va_end(ap); } diff --git a/src/lib/utils/log.h b/src/lib/utils/log.h index 024be767..ce16dfda 100644 --- a/src/lib/utils/log.h +++ b/src/lib/utils/log.h @@ -23,12 +23,12 @@ /** the available log facilities. */ enum LogFacility { - lf_main = 0, //!< main loop - lf_network, //!< network related - lf_bus, //!< eBUS related - lf_update, //!< updates found while listening to the bus - lf_other, //!< all other log facilities - lf_COUNT = 5 //!< number of available log facilities + lf_main = 0, //!< main loop + lf_network, //!< network related + lf_bus, //!< eBUS related + lf_update, //!< updates found while listening to the bus + lf_other, //!< all other log facilities + lf_COUNT = 5 //!< number of available log facilities }; /** macro for enabling all log facilities. */ @@ -36,12 +36,12 @@ enum LogFacility { /** the available log levels. */ enum LogLevel { - ll_none = 0, //!< no level at all - ll_error, //!< error message - ll_notice, //!< important message - ll_info, //!< informational message - ll_debug, //!< debugging message (normally suppressed) - ll_COUNT = 5 //!< number of available log levels + ll_none = 0, //!< no level at all + ll_error, //!< error message + ll_notice, //!< important message + ll_info, //!< informational message + ll_debug, //!< debugging message (normally suppressed) + ll_COUNT = 5 //!< number of available log levels }; /** diff --git a/src/lib/utils/notify.h b/src/lib/utils/notify.h index bca64ddc..27d0d353 100644 --- a/src/lib/utils/notify.h +++ b/src/lib/utils/notify.h @@ -28,45 +28,45 @@ * class to notify other thread per pipe. */ class Notify { - public: - /** - * constructs a new instance and do notifying. - */ - Notify() { - int pipefd[2]; - int ret = pipe(pipefd); + public: + /** + * constructs a new instance and do notifying. + */ + Notify() { + int pipefd[2]; + int ret = pipe(pipefd); - if (ret == 0) { - m_recvfd = pipefd[0]; - m_sendfd = pipefd[1]; + if (ret == 0) { + m_recvfd = pipefd[0]; + m_sendfd = pipefd[1]; - fcntl(m_sendfd, F_SETFL, O_NONBLOCK); - } - } + fcntl(m_sendfd, F_SETFL, O_NONBLOCK); + } + } - /** - * destructor. - */ - ~Notify() { close(m_sendfd); close(m_recvfd); } + /** + * destructor. + */ + ~Notify() { close(m_sendfd); close(m_recvfd); } - /** - * file descriptor to watch for notify event. - * @return the notification value. - */ - int notifyFD() { return m_recvfd; } + /** + * file descriptor to watch for notify event. + * @return the notification value. + */ + int notifyFD() { return m_recvfd; } - /** - * write notify event to file descriptor. - * @return result of writing notification. - */ - int notify() const { return write(m_sendfd, "1", 1); } + /** + * write notify event to file descriptor. + * @return result of writing notification. + */ + int notify() const { return write(m_sendfd, "1", 1); } - private: - /** file descriptor to watch */ - int m_recvfd; + private: + /** file descriptor to watch */ + int m_recvfd; - /** file descriptor to notify */ - int m_sendfd; + /** file descriptor to notify */ + int m_sendfd; }; #endif // LIB_UTILS_NOTIFY_H_ diff --git a/src/lib/utils/queue.h b/src/lib/utils/queue.h index 36d66f0c..166dc67b 100644 --- a/src/lib/utils/queue.h +++ b/src/lib/utils/queue.h @@ -34,122 +34,122 @@ using std::list; */ template class Queue { - public: - /** - * Constructor. - */ - Queue() { - pthread_mutex_init(&m_mutex, NULL); - pthread_cond_init(&m_cond, NULL); - } + public: + /** + * Constructor. + */ + Queue() { + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); + } - /** - * Destructor. - */ - ~Queue() { - pthread_mutex_destroy(&m_mutex); - pthread_cond_destroy(&m_cond); - } + /** + * Destructor. + */ + ~Queue() { + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_cond); + } - private: - /** - * Hidden copy constructor. - * @param src the object to copy from. - */ - Queue(const Queue& src); + private: + /** + * Hidden copy constructor. + * @param src the object to copy from. + */ + Queue(const Queue& src); - public: - /** - * Add an item to the end of queue. - * @param item the item to add. - */ - void push(T item) { - pthread_mutex_lock(&m_mutex); - m_queue.push_back(item); - pthread_cond_broadcast(&m_cond); - pthread_mutex_unlock(&m_mutex); - } + public: + /** + * Add an item to the end of queue. + * @param item the item to add. + */ + void push(T item) { + pthread_mutex_lock(&m_mutex); + m_queue.push_back(item); + pthread_cond_broadcast(&m_cond); + pthread_mutex_unlock(&m_mutex); + } - /** - * Remove the first item from the queue optionally waiting for the queue being non-empty. - * @param timeout the maximum time in seconds to wait for the queue being filled, or 0 for no wait. - * @return the item, or NULL if no item is available within the specified time. - */ - T pop(int timeout = 0) { - T item; - pthread_mutex_lock(&m_mutex); - if (timeout > 0) { - struct timespec t; - clockGettime(&t); - t.tv_sec += timeout; - while (m_queue.empty()) { - if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) { - break; - } - } - } - if (m_queue.empty()) { - item = NULL; - } else { - item = m_queue.front(); - m_queue.pop_front(); - } - pthread_mutex_unlock(&m_mutex); - return item; - } + /** + * Remove the first item from the queue optionally waiting for the queue being non-empty. + * @param timeout the maximum time in seconds to wait for the queue being filled, or 0 for no wait. + * @return the item, or NULL if no item is available within the specified time. + */ + T pop(int timeout = 0) { + T item; + pthread_mutex_lock(&m_mutex); + if (timeout > 0) { + struct timespec t; + clockGettime(&t); + t.tv_sec += timeout; + while (m_queue.empty()) { + if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) { + break; + } + } + } + if (m_queue.empty()) { + item = NULL; + } else { + item = m_queue.front(); + m_queue.pop_front(); + } + pthread_mutex_unlock(&m_mutex); + return item; + } - /** - * Remove the specified item from the queue optionally waiting for it to appear in the queue. - * @param item the item to remove and optionally wait for. - * @param wait true to wait for the item to appear in the queue. - * @return whether the item was removed. - */ - bool remove(T item, bool wait = false) { - bool ret = false; - pthread_mutex_lock(&m_mutex); - do { - size_t oldSize = m_queue.size(); - if (oldSize > 0) { - m_queue.remove(item); - if (m_queue.size() != oldSize) { - ret = true; - break; - } - } - pthread_cond_wait(&m_cond, &m_mutex); - } while (wait); - pthread_mutex_unlock(&m_mutex); - return ret; - } + /** + * Remove the specified item from the queue optionally waiting for it to appear in the queue. + * @param item the item to remove and optionally wait for. + * @param wait true to wait for the item to appear in the queue. + * @return whether the item was removed. + */ + bool remove(T item, bool wait = false) { + bool ret = false; + pthread_mutex_lock(&m_mutex); + do { + size_t oldSize = m_queue.size(); + if (oldSize > 0) { + m_queue.remove(item); + if (m_queue.size() != oldSize) { + ret = true; + break; + } + } + pthread_cond_wait(&m_cond, &m_mutex); + } while (wait); + pthread_mutex_unlock(&m_mutex); + return ret; + } - /** - * Return the first item in the queue without removing it. - * @return the item, or NULL if no item is available. - */ - T peek() { - T item; - pthread_mutex_lock(&m_mutex); - if (m_queue.empty()) { - item = NULL; - } else { - item = m_queue.front(); - } - pthread_mutex_unlock(&m_mutex); - return item; - } + /** + * Return the first item in the queue without removing it. + * @return the item, or NULL if no item is available. + */ + T peek() { + T item; + pthread_mutex_lock(&m_mutex); + if (m_queue.empty()) { + item = NULL; + } else { + item = m_queue.front(); + } + pthread_mutex_unlock(&m_mutex); + return item; + } - private: - /** the queue itself */ - list m_queue; + private: + /** the queue itself */ + list m_queue; - /** mutex variable for exclusive lock */ - pthread_mutex_t m_mutex; + /** mutex variable for exclusive lock */ + pthread_mutex_t m_mutex; - /** condition variable for exclusive lock */ - pthread_cond_t m_cond; + /** condition variable for exclusive lock */ + pthread_cond_t m_cond; }; #endif // LIB_UTILS_QUEUE_H_ diff --git a/src/lib/utils/rotatefile.cpp b/src/lib/utils/rotatefile.cpp index 923e5c82..a67f8588 100644 --- a/src/lib/utils/rotatefile.cpp +++ b/src/lib/utils/rotatefile.cpp @@ -30,59 +30,59 @@ using std::streamsize; RotateFile::~RotateFile() { - if (m_stream) { - fclose(m_stream); - m_stream = NULL; - } + if (m_stream) { + fclose(m_stream); + m_stream = NULL; + } } bool RotateFile::setEnabled(bool enabled) { - if (enabled == m_enabled) { - return false; - } - m_enabled = enabled; - if (m_stream) { - fclose(m_stream); - m_stream = NULL; - } - if (enabled) { - m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); - m_fileSize = 0; - } - return true; + if (enabled == m_enabled) { + return false; + } + m_enabled = enabled; + if (m_stream) { + fclose(m_stream); + m_stream = NULL; + } + if (enabled) { + m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); + m_fileSize = 0; + } + return true; } void RotateFile::write(unsigned char* value, unsigned int size, bool received) { - if (!m_enabled || !m_stream) { - return; - } - if (m_textMode) { - struct timespec ts; - struct tm td; - clockGettime(&ts); - localtime_r(&ts.tv_sec, &td); - fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c", - td.tm_year+1900, td.tm_mon+1, td.tm_mday, - td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, - received ? '<' : '>'); - for (unsigned int pos = 0; pos < size; pos++) { - fprintf(m_stream, "%2.2x ", value[pos]); - } - fprintf(m_stream, "\n"); - m_fileSize += 25+3*size+1; - } else { - fwrite(value, (streamsize)size, 1, m_stream); - m_fileSize += size; - } - if ((m_fileSize%1024) == 0) { - fflush(m_stream); - } - if (m_fileSize >= m_maxSize * 1024LL) { - string oldfile = string(m_fileName)+".old"; - if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) { - fclose(m_stream); - m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); - m_fileSize = 0; - } - } + if (!m_enabled || !m_stream) { + return; + } + if (m_textMode) { + struct timespec ts; + struct tm td; + clockGettime(&ts); + localtime_r(&ts.tv_sec, &td); + fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c", + td.tm_year+1900, td.tm_mon+1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, + received ? '<' : '>'); + for (unsigned int pos = 0; pos < size; pos++) { + fprintf(m_stream, "%2.2x ", value[pos]); + } + fprintf(m_stream, "\n"); + m_fileSize += 25+3*size+1; + } else { + fwrite(value, (streamsize)size, 1, m_stream); + m_fileSize += size; + } + if ((m_fileSize%1024) == 0) { + fflush(m_stream); + } + if (m_fileSize >= m_maxSize * 1024LL) { + string oldfile = string(m_fileName)+".old"; + if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) { + fclose(m_stream); + m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); + m_fileSize = 0; + } + } } diff --git a/src/lib/utils/rotatefile.h b/src/lib/utils/rotatefile.h index 75d75eb2..aaa8066b 100644 --- a/src/lib/utils/rotatefile.h +++ b/src/lib/utils/rotatefile.h @@ -35,61 +35,61 @@ using std::string; * Helper class for writing to a rotating file with maximum size. */ class RotateFile { - public: - /** - * Construct a new instance. - * @param fileName the name of the file write to. - * @param maxSize the maximum size of the file to write to. - * @param textMode whether to write each byte with prefixed timestamp and direction as text. - */ - RotateFile(const string fileName, const unsigned int maxSize, const bool textMode = false) - : m_enabled(false), m_fileName(fileName), m_maxSize(maxSize), m_textMode(textMode), m_stream(), m_fileSize(0) {} + public: + /** + * Construct a new instance. + * @param fileName the name of the file write to. + * @param maxSize the maximum size of the file to write to. + * @param textMode whether to write each byte with prefixed timestamp and direction as text. + */ + RotateFile(const string fileName, const unsigned int maxSize, const bool textMode = false) + : m_enabled(false), m_fileName(fileName), m_maxSize(maxSize), m_textMode(textMode), m_stream(), m_fileSize(0) {} - /** - * Destructor. - */ - virtual ~RotateFile(); + /** + * Destructor. + */ + virtual ~RotateFile(); - /** - * Enable or disable writing to the file. - * @param enabled @p true to enable writing to the file, @p false to disable it. - * @return @p true when the state was changed, @p false otherwise. - */ - bool setEnabled(bool enabled = true); + /** + * Enable or disable writing to the file. + * @param enabled @p true to enable writing to the file, @p false to disable it. + * @return @p true when the state was changed, @p false otherwise. + */ + bool setEnabled(bool enabled = true); - /** - * Return whether writing to the file is enabled. - * @return whether writing to the file is enabled. - */ - bool isEnabled() { return m_enabled; } + /** + * Return whether writing to the file is enabled. + * @return whether writing to the file is enabled. + */ + bool isEnabled() { return m_enabled; } - /** - * Write a number of bytes to the stream. - * @param value the pointer to the bytes to write. - * @param size the number of bytes to write. - * @param received @a true on reception, @a false on sending (only relevant in text mode). - */ - void write(unsigned char* value, unsigned int size, bool received = true); + /** + * Write a number of bytes to the stream. + * @param value the pointer to the bytes to write. + * @param size the number of bytes to write. + * @param received @a true on reception, @a false on sending (only relevant in text mode). + */ + void write(unsigned char* value, unsigned int size, bool received = true); - private: - /** whether writing to the file is enabled. */ - bool m_enabled; + private: + /** whether writing to the file is enabled. */ + bool m_enabled; - /** the name of the file write to. */ - const string m_fileName; + /** the name of the file write to. */ + const string m_fileName; - /** the maximum size of @a m_file, or 0 for infinite. */ - const unsigned int m_maxSize; + /** the maximum size of @a m_file, or 0 for infinite. */ + const unsigned int m_maxSize; - /** whether to write each byte with prefixed timestamp and direction as text. */ - const bool m_textMode; + /** whether to write each byte with prefixed timestamp and direction as text. */ + const bool m_textMode; - /** the @a FILE to writing to. */ - FILE* m_stream; + /** the @a FILE to writing to. */ + FILE* m_stream; - /** the number of bytes already written to the @a m_file. */ - uint64_t m_fileSize; + /** the number of bytes already written to the @a m_file. */ + uint64_t m_fileSize; }; #endif // LIB_UTILS_ROTATEFILE_H_ diff --git a/src/lib/utils/tcpsocket.cpp b/src/lib/utils/tcpsocket.cpp index 47b1cf80..e28461a5 100644 --- a/src/lib/utils/tcpsocket.cpp +++ b/src/lib/utils/tcpsocket.cpp @@ -24,98 +24,98 @@ #include TCPSocket::TCPSocket(int sfd, struct sockaddr_in* address) : m_sfd(sfd) { - char ip[17]; - inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1); - m_ip = ip; - m_port = (uint16_t)ntohs(address->sin_port); + char ip[17]; + inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1); + m_ip = ip; + m_port = (uint16_t)ntohs(address->sin_port); } bool TCPSocket::isValid() { - return fcntl(m_sfd, F_GETFL) != -1; + return fcntl(m_sfd, F_GETFL) != -1; } TCPSocket* TCPClient::connect(const string& server, const uint16_t& port) { - struct sockaddr_in address; - int ret; + struct sockaddr_in address; + int ret; - memset(reinterpret_cast(&address), 0, sizeof(address)); + memset(reinterpret_cast(&address), 0, sizeof(address)); - if (inet_addr(server.c_str()) == INADDR_NONE) { - struct hostent* he; + if (inet_addr(server.c_str()) == INADDR_NONE) { + struct hostent* he; - he = gethostbyname(server.c_str()); - if (he == NULL) { - return NULL; - } - memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length); - } else { - ret = inet_aton(server.c_str(), &address.sin_addr); - if (ret == 0) { - return NULL; - } - } + he = gethostbyname(server.c_str()); + if (he == NULL) { + return NULL; + } + memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length); + } else { + ret = inet_aton(server.c_str(), &address.sin_addr); + if (ret == 0) { + return NULL; + } + } - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons(port); + address.sin_family = AF_INET; + address.sin_port = (in_port_t)htons(port); - int sfd = socket(AF_INET, SOCK_STREAM, 0); - if (sfd < 0) { - return NULL; - } - ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address)); - if (ret < 0) { - return NULL; - } - return new TCPSocket(sfd, &address); + int sfd = socket(AF_INET, SOCK_STREAM, 0); + if (sfd < 0) { + return NULL; + } + ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address)); + if (ret < 0) { + return NULL; + } + return new TCPSocket(sfd, &address); } int TCPServer::start() { - if (m_listening) { - return 0; - } - m_lfd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in address; + if (m_listening) { + return 0; + } + m_lfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in address; - memset(&address, 0, sizeof(address)); + memset(&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_port = (in_port_t)htons(m_port); + address.sin_family = AF_INET; + address.sin_port = (in_port_t)htons(m_port); - if (m_address.size() > 0) { - inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr)); - } else { - address.sin_addr.s_addr = INADDR_ANY; - } - int optval = 1; - setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if (m_address.size() > 0) { + inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr)); + } else { + address.sin_addr.s_addr = INADDR_ANY; + } + int optval = 1; + setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address)); - if (result != 0) { - return result; - } - result = listen(m_lfd, 5); - if (result != 0) { - return result; - } - m_listening = true; - return result; + int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address)); + if (result != 0) { + return result; + } + result = listen(m_lfd, 5); + if (result != 0) { + return result; + } + m_listening = true; + return result; } TCPSocket* TCPServer::newSocket() { - if (!m_listening) { - return NULL; - } - struct sockaddr_in address; - socklen_t len = sizeof(address); + if (!m_listening) { + return NULL; + } + struct sockaddr_in address; + socklen_t len = sizeof(address); - memset(&address, 0, sizeof(address)); + memset(&address, 0, sizeof(address)); - int sfd = accept(m_lfd, (struct sockaddr*) &address, &len); - if (sfd < 0) { - return NULL; - } - return new TCPSocket(sfd, &address); + int sfd = accept(m_lfd, (struct sockaddr*) &address, &len); + if (sfd < 0) { + return NULL; + } + return new TCPSocket(sfd, &address); } diff --git a/src/lib/utils/tcpsocket.h b/src/lib/utils/tcpsocket.h index b0dc1879..9d8148bd 100644 --- a/src/lib/utils/tcpsocket.h +++ b/src/lib/utils/tcpsocket.h @@ -38,140 +38,140 @@ using std::string; * class for low level tcp socket operations. (open, close, send, receive). */ class TCPSocket { - public: - /** grant access for friend class TCPClient */ - friend class TCPClient; + public: + /** grant access for friend class TCPClient */ + friend class TCPClient; - /** grant access for friend class TCPServer */ - friend class TCPServer; + /** grant access for friend class TCPServer */ + friend class TCPServer; - /** - * destructor. - */ - ~TCPSocket() { close(m_sfd); } + /** + * destructor. + */ + ~TCPSocket() { close(m_sfd); } - /** - * write bytes to opened file descriptor. - * @param buffer data to send. - * @param len number of bytes to send. - * @return number of written bytes or -1 if an error has occured. - */ - ssize_t send(const char* buffer, size_t len) { return ::send(m_sfd, buffer, len, MSG_NOSIGNAL); } + /** + * write bytes to opened file descriptor. + * @param buffer data to send. + * @param len number of bytes to send. + * @return number of written bytes or -1 if an error has occured. + */ + ssize_t send(const char* buffer, size_t len) { return ::send(m_sfd, buffer, len, MSG_NOSIGNAL); } - /** - * read bytes from opened file descriptor. - * @param buffer for received bytes. - * @param len size of the receive buffer. - * @return number of read bytes or -1 if an error has occured. - */ - ssize_t recv(char* buffer, size_t len) { return ::recv(m_sfd, buffer, len, 0); } + /** + * read bytes from opened file descriptor. + * @param buffer for received bytes. + * @param len size of the receive buffer. + * @return number of read bytes or -1 if an error has occured. + */ + ssize_t recv(char* buffer, size_t len) { return ::recv(m_sfd, buffer, len, 0); } - /** - * returns the tcp port. - * @return the tcp port. - */ - uint16_t getPort() const { return m_port; } + /** + * returns the tcp port. + * @return the tcp port. + */ + uint16_t getPort() const { return m_port; } - /** - * returns the ip address. - * @return the ip address. - */ - string getIP() const { return m_ip; } + /** + * returns the ip address. + * @return the ip address. + */ + string getIP() const { return m_ip; } - /** - * returns the file descriptor. - * @return the file descriptor. - */ - int getFD() const { return m_sfd; } + /** + * returns the file descriptor. + * @return the file descriptor. + */ + int getFD() const { return m_sfd; } - /** - * returns status of file descriptor. - * @return true if file descriptor is valid. - */ - bool isValid(); + /** + * returns status of file descriptor. + * @return true if file descriptor is valid. + */ + bool isValid(); - private: - /** file descriptor from tcp socket */ - int m_sfd; + private: + /** file descriptor from tcp socket */ + int m_sfd; - /** port of tcp socket */ - uint16_t m_port; + /** port of tcp socket */ + uint16_t m_port; - /** ip address of tcp socket */ - string m_ip; + /** ip address of tcp socket */ + string m_ip; - /** - * private constructor, limited access only for friend classes. - * @param sfd the file desctriptor of tcp socket. - * @param address struct which holds the ip address. - */ - TCPSocket(int sfd, struct sockaddr_in* address); + /** + * private constructor, limited access only for friend classes. + * @param sfd the file desctriptor of tcp socket. + * @param address struct which holds the ip address. + */ + TCPSocket(int sfd, struct sockaddr_in* address); }; /** * class to initiate a tcp socket connection to a listening server. */ class TCPClient { - public: - /** - * initiate a tcp socket connection to a listening server. - * @param server the server name or ip address to connect. - * @param port the tcp port. - * @return pointer to an opened tcp socket. - */ - TCPSocket* connect(const string& server, const uint16_t& port); + public: + /** + * initiate a tcp socket connection to a listening server. + * @param server the server name or ip address to connect. + * @param port the tcp port. + * @return pointer to an opened tcp socket. + */ + TCPSocket* connect(const string& server, const uint16_t& port); }; /** * class for a tcp based network server. */ class TCPServer { - public: - /** - * creates a new instance of a listening tcp server. - * @param port the tcp port. - * @param address the ip address. - */ - TCPServer(const uint16_t port, const string address) - : m_lfd(0), m_port(port), m_address(address), m_listening(false) {} + public: + /** + * creates a new instance of a listening tcp server. + * @param port the tcp port. + * @param address the ip address. + */ + TCPServer(const uint16_t port, const string address) + : m_lfd(0), m_port(port), m_address(address), m_listening(false) {} - /** - * destructor. - */ - ~TCPServer() { if (m_lfd > 0) {close(m_lfd);} } + /** + * destructor. + */ + ~TCPServer() { if (m_lfd > 0) {close(m_lfd);} } - /** - * start listening of tcp socket. - * @return result of low level functions. - */ - int start(); + /** + * start listening of tcp socket. + * @return result of low level functions. + */ + int start(); - /** - * accept an incoming tcp connection and create a local tcp socket for communication. - * @return pointer to an opened tcp socket. - */ - TCPSocket* newSocket(); + /** + * accept an incoming tcp connection and create a local tcp socket for communication. + * @return pointer to an opened tcp socket. + */ + TCPSocket* newSocket(); - /** - * returns the file descriptor. - * @return the file descriptor. - */ - int getFD() const { return m_lfd; } + /** + * returns the file descriptor. + * @return the file descriptor. + */ + int getFD() const { return m_lfd; } - private: - /** file descriptor from listening tcp socket */ - int m_lfd; + private: + /** file descriptor from listening tcp socket */ + int m_lfd; - /** listening tcp port */ - uint16_t m_port; + /** listening tcp port */ + uint16_t m_port; - /** listening tcp socket ip address */ - string m_address; + /** listening tcp socket ip address */ + string m_address; - /** true if object is already listening */ - bool m_listening; + /** true if object is already listening */ + bool m_listening; }; #endif // LIB_UTILS_TCPSOCKET_H_ diff --git a/src/lib/utils/thread.cpp b/src/lib/utils/thread.cpp index 197d3014..47f1d0fd 100644 --- a/src/lib/utils/thread.cpp +++ b/src/lib/utils/thread.cpp @@ -17,88 +17,88 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include "thread.h" #include "clock.h" void* Thread::runThread(void* arg) { - reinterpret_cast(arg)->enter(); - return NULL; + reinterpret_cast(arg)->enter(); + return NULL; } Thread::~Thread() { - if (m_started) { - pthread_cancel(m_threadid); - pthread_detach(m_threadid); - } + if (m_started) { + pthread_cancel(m_threadid); + pthread_detach(m_threadid); + } } bool Thread::start(const char* name) { - int result = pthread_create(&m_threadid, NULL, runThread, this); - if (result == 0) { + int result = pthread_create(&m_threadid, NULL, runThread, this); + if (result == 0) { #ifdef HAVE_PTHREAD_SETNAME_NP #ifndef __MACH__ - pthread_setname_np(m_threadid, name); + pthread_setname_np(m_threadid, name); #endif #endif - m_started = true; - return true; - } - return false; + m_started = true; + return true; + } + return false; } bool Thread::join() { - int result = -1; - if (m_started) { - m_stopped = true; - result = pthread_join(m_threadid, NULL); - if (result == 0) { - m_started = false; - } - } - return result == 0; + int result = -1; + if (m_started) { + m_stopped = true; + result = pthread_join(m_threadid, NULL); + if (result == 0) { + m_started = false; + } + } + return result == 0; } void Thread::enter() { - m_running = true; - run(); - m_running = false; + m_running = true; + run(); + m_running = false; } WaitThread::WaitThread() - : Thread() { - pthread_mutex_init(&m_mutex, NULL); - pthread_cond_init(&m_cond, NULL); + : Thread() { + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); } WaitThread::~WaitThread() { - pthread_mutex_destroy(&m_mutex); - pthread_cond_destroy(&m_cond); + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_cond); } void WaitThread::stop() { - pthread_mutex_lock(&m_mutex); - pthread_cond_signal(&m_cond); - pthread_mutex_unlock(&m_mutex); - Thread::stop(); + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + Thread::stop(); } bool WaitThread::join() { - pthread_mutex_lock(&m_mutex); - pthread_cond_signal(&m_cond); - pthread_mutex_unlock(&m_mutex); - return Thread::join(); + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + return Thread::join(); } bool WaitThread::Wait(int seconds) { - struct timespec t; - clockGettime(&t); - t.tv_sec += seconds; - pthread_mutex_lock(&m_mutex); - pthread_cond_timedwait(&m_cond, &m_mutex, &t); - pthread_mutex_unlock(&m_mutex); - return isRunning(); + struct timespec t; + clockGettime(&t); + t.tv_sec += seconds; + pthread_mutex_lock(&m_mutex); + pthread_cond_timedwait(&m_cond, &m_mutex, &t); + pthread_mutex_unlock(&m_mutex); + return isRunning(); } diff --git a/src/lib/utils/thread.h b/src/lib/utils/thread.h index a056033c..46de8cc7 100644 --- a/src/lib/utils/thread.h +++ b/src/lib/utils/thread.h @@ -27,79 +27,79 @@ * wrapper class for pthread. */ class Thread { - public: - /** - * constructor. - */ - Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {} + public: + /** + * constructor. + */ + Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {} - /** - * virtual destructor. - */ - virtual ~Thread(); + /** + * virtual destructor. + */ + virtual ~Thread(); - /** - * Thread entry helper for pthread_create. - * @param arg pointer to the @a Thread. - * @return NULL. - */ - static void* runThread(void* arg); + /** + * Thread entry helper for pthread_create. + * @param arg pointer to the @a Thread. + * @return NULL. + */ + static void* runThread(void* arg); - /** - * Return whether this @a Thread is still running and not yet stopped. - * @return true if this @a Thread is till running and not yet stopped. - */ - virtual bool isRunning() { return m_running && !m_stopped; } + /** + * Return whether this @a Thread is still running and not yet stopped. + * @return true if this @a Thread is till running and not yet stopped. + */ + virtual bool isRunning() { return m_running && !m_stopped; } - /** - * Create the native thread and set its name. - * @param name the thread name to show in the process list. - * @return whether the thread was started. - */ - virtual bool start(const char* name); + /** + * Create the native thread and set its name. + * @param name the thread name to show in the process list. + * @return whether the thread was started. + */ + virtual bool start(const char* name); - /** - * Notify the thread that it shall stop. - */ - virtual void stop() { m_stopped = true; } + /** + * Notify the thread that it shall stop. + */ + virtual void stop() { m_stopped = true; } - /** - * Join the thread. - * @return whether the thread was joined. - */ - virtual bool join(); + /** + * Join the thread. + * @return whether the thread was joined. + */ + virtual bool join(); - /** - * Get the thread id. - * @return the thread id. - */ - pthread_t self() { return m_threadid; } + /** + * Get the thread id. + * @return the thread id. + */ + pthread_t self() { return m_threadid; } - protected: - /** - * Thread entry method to be overridden by derived class. - */ - virtual void run() = 0; + protected: + /** + * Thread entry method to be overridden by derived class. + */ + virtual void run() = 0; - private: - /** - * Enter the Thread loop by calling run(). - */ - void enter(); + private: + /** + * Enter the Thread loop by calling run(). + */ + void enter(); - /** own thread id */ - pthread_t m_threadid; + /** own thread id */ + pthread_t m_threadid; - /** Whether the thread was started. */ - bool m_started; + /** Whether the thread was started. */ + bool m_started; - /** Whether the thread is still running (i.e. in @a run() ). */ - bool m_running; + /** Whether the thread is still running (i.e. in @a run() ). */ + bool m_running; - /** Whether the thread was stopped by @a stop() or @a join(). */ - bool m_stopped; + /** Whether the thread was stopped by @a stop() or @a join(). */ + bool m_stopped; }; @@ -107,37 +107,37 @@ class Thread { * A @a Thread that can be waited on. */ class WaitThread : public Thread { - public: - /** - * Constructor. - */ - WaitThread(); + public: + /** + * Constructor. + */ + WaitThread(); - /** - * Destructor. - */ - virtual ~WaitThread(); + /** + * Destructor. + */ + virtual ~WaitThread(); - // @copydoc - virtual void stop(); + // @copydoc + virtual void stop(); - // @copydoc - virtual bool join(); + // @copydoc + virtual bool join(); - /** - * Wait for the specified amount of time. - * @param seconds the number of seconds to wait. - * @return true if this @a WaitThread is still running and not yet stopped. - */ - bool Wait(int seconds); + /** + * Wait for the specified amount of time. + * @param seconds the number of seconds to wait. + * @return true if this @a WaitThread is still running and not yet stopped. + */ + bool Wait(int seconds); - private: - /** the mutex for waiting. */ - pthread_mutex_t m_mutex; + private: + /** the mutex for waiting. */ + pthread_mutex_t m_mutex; - /** the condition for waiting. */ - pthread_cond_t m_cond; + /** the condition for waiting. */ + pthread_cond_t m_cond; }; #endif // LIB_UTILS_THREAD_H_ diff --git a/src/tools/ebusctl.cpp b/src/tools/ebusctl.cpp index ebbd5b9c..99e1cff0 100644 --- a/src/tools/ebusctl.cpp +++ b/src/tools/ebusctl.cpp @@ -17,13 +17,13 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include #include #ifdef HAVE_PPOLL -# include +# include #endif #include #include @@ -39,20 +39,20 @@ using std::endl; /** A structure holding all program options. */ struct options { - const char* server; //!< ebusd server host (name or ip) [localhost] - uint16_t port; //!< ebusd server port [8888] + const char* server; //!< ebusd server host (name or ip) [localhost] + uint16_t port; //!< ebusd server port [8888] - char* const *args; //!< arguments to pass to ebusd - unsigned int argCount; //!< number of arguments to pass to ebusd + char* const *args; //!< arguments to pass to ebusd + unsigned int argCount; //!< number of arguments to pass to ebusd }; /** the program options. */ static struct options opt = { - "localhost", // server - 8888, // port + "localhost", // server + 8888, // port - NULL, // args - 0 // argCount + NULL, // args + 0 // argCount }; /** the version string of the program. */ @@ -63,21 +63,21 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT""; /** the documentation of the program. */ static const char argpdoc[] = - "Client for acessing "PACKAGE" via TCP.\n" - "\v" - "If given, send COMMAND together with CMDOPT options to "PACKAGE".\n" - "Use 'help' as COMMAND for help on available "PACKAGE" commands."; + "Client for acessing "PACKAGE" via TCP.\n" + "\v" + "If given, send COMMAND together with CMDOPT options to "PACKAGE".\n" + "Use 'help' as COMMAND for help on available "PACKAGE" commands."; /** the description of the accepted arguments. */ static char argpargsdoc[] = "\nCOMMAND [CMDOPT...]"; /** the definition of the known program arguments. */ static const struct argp_option argpoptions[] = { - {NULL, 0, NULL, 0, "Options:", 1 }, - {"server", 's', "HOST", 0, "Connect to HOST running "PACKAGE" (name or IP) [localhost]", 0 }, - {"port", 'p', "PORT", 0, "Connect to PORT on HOST [8888]", 0 }, + {NULL, 0, NULL, 0, "Options:", 1 }, + {"server", 's', "HOST", 0, "Connect to HOST running "PACKAGE" (name or IP) [localhost]", 0 }, + {"port", 'p', "PORT", 0, "Connect to PORT on HOST [8888]", 0 }, - {NULL, 0, NULL, 0, NULL, 0 }, + {NULL, 0, NULL, 0, NULL, 0 }, }; /** @@ -87,216 +87,216 @@ static const struct argp_option argpoptions[] = { * @param state the parsing state. */ error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct options *opt = (struct options*)state->input; - char* strEnd = NULL; - unsigned int port; - switch (key) { - // Device settings: - case 's': // --server=localhost - if (arg == NULL || arg[0] == 0) { - argp_error(state, "invalid server"); - return EINVAL; - } - opt->server = arg; - break; - case 'p': // --port=8888 - port = strtoul(arg, &strEnd, 10); - if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) { - argp_error(state, "invalid port"); - return EINVAL; - } - opt->port = (uint16_t)port; - break; - case ARGP_KEY_ARGS: - opt->args = state->argv + state->next; - opt->argCount = state->argc - state->next; - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + struct options *opt = (struct options*)state->input; + char* strEnd = NULL; + unsigned int port; + switch (key) { + // Device settings: + case 's': // --server=localhost + if (arg == NULL || arg[0] == 0) { + argp_error(state, "invalid server"); + return EINVAL; + } + opt->server = arg; + break; + case 'p': // --port=8888 + port = strtoul(arg, &strEnd, 10); + if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) { + argp_error(state, "invalid port"); + return EINVAL; + } + opt->port = (uint16_t)port; + break; + case ARGP_KEY_ARGS: + opt->args = state->argv + state->next; + opt->argCount = state->argc - state->next; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } string fetchData(TCPSocket* socket, bool& listening) { - char data[1024]; - ssize_t datalen; - ostringstream ostream; - string message, sendmessage; + char data[1024]; + ssize_t datalen; + ostringstream ostream; + string message, sendmessage; - int ret; - struct timespec tdiff; + int ret; + struct timespec tdiff; - // set timeout - tdiff.tv_sec = 0; - tdiff.tv_nsec = 1E8; + // set timeout + tdiff.tv_sec = 0; + tdiff.tv_nsec = 1E8; #ifdef HAVE_PPOLL - int nfds = 2; - struct pollfd fds[nfds]; + int nfds = 2; + struct pollfd fds[nfds]; - memset(fds, 0, sizeof(fds)); + memset(fds, 0, sizeof(fds)); - fds[0].fd = STDIN_FILENO; - fds[0].events = POLLIN; + fds[0].fd = STDIN_FILENO; + fds[0].events = POLLIN; - fds[1].fd = socket->getFD(); - fds[1].events = POLLIN; + fds[1].fd = socket->getFD(); + fds[1].events = POLLIN; #else #ifdef HAVE_PSELECT - int maxfd; - fd_set checkfds; + int maxfd; + fd_set checkfds; - FD_ZERO(&checkfds); - FD_SET(STDIN_FILENO, &checkfds); - FD_SET(socket->getFD(), &checkfds); - maxfd = STDIN_FILENO; - if (socket->getFD() > maxfd) { - maxfd = socket->getFD(); - } + FD_ZERO(&checkfds); + FD_SET(STDIN_FILENO, &checkfds); + FD_SET(socket->getFD(), &checkfds); + maxfd = STDIN_FILENO; + if (socket->getFD() > maxfd) { + maxfd = socket->getFD(); + } #endif #endif - while(true) { + while(true) { #ifdef HAVE_PPOLL - // wait for new fd event - ret = ppoll(fds, nfds, &tdiff, NULL); + // wait for new fd event + ret = ppoll(fds, nfds, &tdiff, NULL); #else #ifdef HAVE_PSELECT - // set readfds to inital checkfds - fd_set readfds = checkfds; - // wait for new fd event - ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); + // set readfds to inital checkfds + fd_set readfds = checkfds; + // wait for new fd event + ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); #endif #endif - bool newData = false; - bool newInput = false; - if (ret != 0) { + bool newData = false; + bool newInput = false; + if (ret != 0) { #ifdef HAVE_PPOLL - // new data from notify - newInput = fds[0].revents & POLLIN; + // new data from notify + newInput = fds[0].revents & POLLIN; - // new data from socket - newData = fds[1].revents & POLLIN; + // new data from socket + newData = fds[1].revents & POLLIN; #else #ifdef HAVE_PSELECT - // new data from notify - newInput = FD_ISSET(STDIN_FILENO, &readfds); + // new data from notify + newInput = FD_ISSET(STDIN_FILENO, &readfds); - // new data from socket - newData = FD_ISSET(socket->getFD(), &readfds); + // new data from socket + newData = FD_ISSET(socket->getFD(), &readfds); #endif #endif - } + } - if (newData) { - if (socket->isValid()) { - datalen = socket->recv(data, sizeof(data)); + if (newData) { + if (socket->isValid()) { + datalen = socket->recv(data, sizeof(data)); - if (datalen < 0) { - perror("recv"); - break; - } + if (datalen < 0) { + perror("recv"); + break; + } - for (int i = 0; i < datalen; i++) { - ostream << data[i]; - } - string str = ostream.str(); - if (listening) { - return str; - } - if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') { - return str; - } - } else { - break; - } - } else if (newInput) { - getline(cin, message); - sendmessage = message+'\n'; - socket->send(sendmessage.c_str(), sendmessage.size()); + for (int i = 0; i < datalen; i++) { + ostream << data[i]; + } + string str = ostream.str(); + if (listening) { + return str; + } + if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') { + return str; + } + } else { + break; + } + } else if (newInput) { + getline(cin, message); + sendmessage = message+'\n'; + socket->send(sendmessage.c_str(), sendmessage.size()); - if (strcasecmp(message.c_str(), "Q") == 0 - || strcasecmp(message.c_str(), "QUIT") == 0 - || strcasecmp(message.c_str(), "STOP") == 0) { - exit(EXIT_SUCCESS); - return ""; - } - message.clear(); - } - } + if (strcasecmp(message.c_str(), "Q") == 0 + || strcasecmp(message.c_str(), "QUIT") == 0 + || strcasecmp(message.c_str(), "STOP") == 0) { + exit(EXIT_SUCCESS); + return ""; + } + message.clear(); + } + } - return ostream.str(); + return ostream.str(); } void connect(const char* host, uint16_t port, char* const *args, int argCount) { - TCPClient* client = new TCPClient(); - TCPSocket* socket = client->connect(host, port); + TCPClient* client = new TCPClient(); + TCPSocket* socket = client->connect(host, port); - bool once = args != NULL && argCount > 0; - if (socket != NULL) { - string message, sendmessage; - do { - bool listening = false; + bool once = args != NULL && argCount > 0; + if (socket != NULL) { + string message, sendmessage; + do { + bool listening = false; - if (!once) { - cout << host << ": "; - getline(cin, message); - } else { - for (int i = 0; i < argCount; i++) { - if (i > 0) { - message += " "; - } - bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL; - if (quote) { - message += "\""; - } - message += args[i]; - if (quote) { - message += "\""; - } - } - } + if (!once) { + cout << host << ": "; + getline(cin, message); + } else { + for (int i = 0; i < argCount; i++) { + if (i > 0) { + message += " "; + } + bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL; + if (quote) { + message += "\""; + } + message += args[i]; + if (quote) { + message += "\""; + } + } + } - sendmessage = message+'\n'; - socket->send(sendmessage.c_str(), sendmessage.size()); + sendmessage = message+'\n'; + socket->send(sendmessage.c_str(), sendmessage.size()); - if (strcasecmp(message.c_str(), "Q") == 0 - || strcasecmp(message.c_str(), "QUIT") == 0 - || strcasecmp(message.c_str(), "STOP") == 0) - break; + if (strcasecmp(message.c_str(), "Q") == 0 + || strcasecmp(message.c_str(), "QUIT") == 0 + || strcasecmp(message.c_str(), "STOP") == 0) + break; - if (message.length() > 0) { - if (strcasecmp(message.c_str(), "L") == 0 - || strcasecmp(message.c_str(), "LISTEN") == 0) { - listening = true; - while (listening && !cin.eof()) { - string result(fetchData(socket, listening)); - cout << result; - if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) { - break; - } - } - } else { - cout << fetchData(socket, listening); - } - } - } while (!once && !cin.eof()); - delete socket; - } else { - cout << "error connecting to " << host << ":" << port << endl; - } - delete client; + if (message.length() > 0) { + if (strcasecmp(message.c_str(), "L") == 0 + || strcasecmp(message.c_str(), "LISTEN") == 0) { + listening = true; + while (listening && !cin.eof()) { + string result(fetchData(socket, listening)); + cout << result; + if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) { + break; + } + } + } else { + cout << fetchData(socket, listening); + } + } + } while (!once && !cin.eof()); + delete socket; + } else { + cout << "error connecting to " << host << ":" << port << endl; + } + delete client; } int main(int argc, char* argv[]) { - struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; - setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); - if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { - return EINVAL; - } - connect(opt.server, opt.port, opt.args, opt.argCount); + struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; + setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); + if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { + return EINVAL; + } + connect(opt.server, opt.port, opt.args, opt.argCount); - exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); } diff --git a/src/tools/ebusfeed.cpp b/src/tools/ebusfeed.cpp index 4d68e07f..e450890f 100644 --- a/src/tools/ebusfeed.cpp +++ b/src/tools/ebusfeed.cpp @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include +# include #endif #include @@ -42,18 +42,18 @@ using ebusd::Device; /** A structure holding all program options. */ struct options { - const char* device; //!< device to write to [/dev/ttyUSB60] - unsigned int time; //!< delay between bytes in us [10000] + const char* device; //!< device to write to [/dev/ttyUSB60] + unsigned int time; //!< delay between bytes in us [10000] - const char* dumpFile; //!< dump file to read + const char* dumpFile; //!< dump file to read }; /** the program options. */ static struct options opt = { - "/dev/ttyUSB60", // device - 10000, // time + "/dev/ttyUSB60", // device + 10000, // time - "/tmp/ebus_dump.bin", // dumpFile + "/tmp/ebus_dump.bin", // dumpFile }; /** the version string of the program. */ @@ -64,27 +64,27 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT""; /** the documentation of the program. */ static const char argpdoc[] = - "Feed data from an "PACKAGE" DUMPFILE to a serial device.\n" - "\v" - "With no DUMPFILE, /tmp/ebus_dump.bin is used.\n" - "\n" - "Example for setting up two pseudo terminals with 'socat':\n" - " 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n" - " 2. create symbol links to appropriate devices, e.g.\n" - " 'ln -s /dev/pts/2 /dev/ttyUSB60'\n" - " 'ln -s /dev/pts/3 /dev/ttyUSB20'\n" - " 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n" - " 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n"; + "Feed data from an "PACKAGE" DUMPFILE to a serial device.\n" + "\v" + "With no DUMPFILE, /tmp/ebus_dump.bin is used.\n" + "\n" + "Example for setting up two pseudo terminals with 'socat':\n" + " 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n" + " 2. create symbol links to appropriate devices, e.g.\n" + " 'ln -s /dev/pts/2 /dev/ttyUSB60'\n" + " 'ln -s /dev/pts/3 /dev/ttyUSB20'\n" + " 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n" + " 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n"; /** the description of the accepted arguments. */ static char argpargsdoc[] = "[DUMPFILE]"; /** the definition of the known program arguments. */ static const struct argp_option argpoptions[] = { - {"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 }, - {"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 0 }, + {"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 }, + {"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 0 }, - {NULL, 0, NULL, 0, NULL, 0 }, + {NULL, 0, NULL, 0, NULL, 0 }, }; /** @@ -94,80 +94,80 @@ static const struct argp_option argpoptions[] = { * @param state the parsing state. */ error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct options *opt = (struct options*)state->input; - char* strEnd = NULL; - switch (key) { - // Device settings: - case 'd': // --device=/dev/ttyUSB60 - if (arg == NULL || arg[0] == 0) { - argp_error(state, "invalid device"); - return EINVAL; - } - opt->device = arg; - break; - case 't': // --time=10000 - opt->time = (unsigned int)strtoul(arg, &strEnd, 10); - if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) { - argp_error(state, "invalid time"); - return EINVAL; - } - break; - case ARGP_KEY_ARG: - if (state->arg_num == 0) { - if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { - argp_error(state, "invalid dumpfile"); - return EINVAL; - } - opt->dumpFile = arg; - } else { - return ARGP_ERR_UNKNOWN; - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + struct options *opt = (struct options*)state->input; + char* strEnd = NULL; + switch (key) { + // Device settings: + case 'd': // --device=/dev/ttyUSB60 + if (arg == NULL || arg[0] == 0) { + argp_error(state, "invalid device"); + return EINVAL; + } + opt->device = arg; + break; + case 't': // --time=10000 + opt->time = (unsigned int)strtoul(arg, &strEnd, 10); + if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) { + argp_error(state, "invalid time"); + return EINVAL; + } + break; + case ARGP_KEY_ARG: + if (state->arg_num == 0) { + if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { + argp_error(state, "invalid dumpfile"); + return EINVAL; + } + opt->dumpFile = arg; + } else { + return ARGP_ERR_UNKNOWN; + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } int main(int argc, char* argv[]) { - struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; - setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); - if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { - return EINVAL; - } - Device* device = Device::create(opt.device, false, false, NULL); - if (device == NULL) { - cout << "unable to create device " << opt.device << endl; - return EINVAL; - } - result_t result = device->open(); - if (result != ebusd::RESULT_OK) { - cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl; - } - if (!device->isValid()) { - cout << "device " << opt.device << " not available" << endl; - } else { - cout << "device opened" << endl; - fstream file(opt.dumpFile, ios::in | ios::binary); - if (file.is_open()) { - while (true) { - unsigned char byte = (unsigned char)file.get(); - if (file.eof()) { - break; - } - cout << hex << setw(2) << setfill('0') - << static_cast(byte) << endl; - device->send(byte); - usleep(opt.time); - } - file.close(); - } else { - cout << "error opening file " << opt.dumpFile << endl; - } - } + struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; + setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); + if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { + return EINVAL; + } + Device* device = Device::create(opt.device, false, false, NULL); + if (device == NULL) { + cout << "unable to create device " << opt.device << endl; + return EINVAL; + } + result_t result = device->open(); + if (result != ebusd::RESULT_OK) { + cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl; + } + if (!device->isValid()) { + cout << "device " << opt.device << " not available" << endl; + } else { + cout << "device opened" << endl; + fstream file(opt.dumpFile, ios::in | ios::binary); + if (file.is_open()) { + while (true) { + unsigned char byte = (unsigned char)file.get(); + if (file.eof()) { + break; + } + cout << hex << setw(2) << setfill('0') + << static_cast(byte) << endl; + device->send(byte); + usleep(opt.time); + } + file.close(); + } else { + cout << "error opening file " << opt.dumpFile << endl; + } + } - delete device; - exit(EXIT_SUCCESS); + delete device; + exit(EXIT_SUCCESS); }