From 3b3a5f2e0775d2abc78d199e5cd3a911b99e1ad7 Mon Sep 17 00:00:00 2001 From: Roland Jax Date: Sun, 7 Dec 2014 12:00:03 +0100 Subject: [PATCH] Merge pull request #18 from john30/master; first version supporting get, set, and cyc again. --- src/ebusd/Makefile.am | 6 +- src/ebusd/baseloop.cpp | 297 +++++++---- src/ebusd/baseloop.h | 42 +- src/ebusd/bushandler.cpp | 488 +++++++++++++++++ src/ebusd/bushandler.h | 271 ++++++++++ src/ebusd/busloop.cpp | 728 -------------------------- src/ebusd/busloop.h | 370 ------------- src/ebusd/ebusd.cpp | 9 +- src/ebusd/network.cpp | 27 +- src/ebusd/network.h | 22 +- src/lib/ebus/Makefile.am | 15 +- src/lib/ebus/command.cpp | 338 ------------ src/lib/ebus/command.h | 64 --- src/lib/ebus/commands.cpp | 260 --------- src/lib/ebus/commands.h | 81 --- src/lib/ebus/configfile.cpp | 121 ----- src/lib/ebus/configfile.h | 132 ----- src/lib/ebus/data.cpp | 425 ++++++++++----- src/lib/ebus/data.h | 238 +++++++-- src/lib/ebus/decode.cpp | 347 ------------ src/lib/ebus/decode.h | 286 ---------- src/lib/ebus/encode.cpp | 364 ------------- src/lib/ebus/encode.h | 286 ---------- src/lib/ebus/message.cpp | 409 ++++++++++++--- src/lib/ebus/message.h | 171 ++++-- src/lib/ebus/port.cpp | 105 +++- src/lib/ebus/port.h | 104 +++- src/lib/ebus/result.cpp | 43 +- src/lib/ebus/result.h | 43 +- src/lib/ebus/symbol.cpp | 60 ++- src/lib/ebus/symbol.h | 41 +- src/lib/ebus/test/Makefile.am | 21 +- src/lib/ebus/test/test_commands.cpp | 86 --- src/lib/ebus/test/test_configfile.cpp | 47 -- src/lib/ebus/test/test_data.cpp | 38 +- src/lib/ebus/test/test_decode.cpp | 308 ----------- src/lib/ebus/test/test_encode.cpp | 309 ----------- src/lib/ebus/test/test_message.cpp | 194 ++++--- src/lib/ebus/test/test_port.cpp | 2 +- src/lib/ebus/test/test_symbol.cpp | 16 +- src/lib/utils/Makefile.am | 0 src/lib/utils/appl.cpp | 3 + src/lib/utils/appl.h | 3 +- src/lib/utils/logger.cpp | 22 +- src/lib/utils/logger.h | 11 +- src/lib/utils/thread.cpp | 44 +- src/lib/utils/thread.h | 65 ++- src/lib/utils/wqueue.h | 58 +- src/tools/Makefile.am | 0 49 files changed, 2536 insertions(+), 4884 deletions(-) create mode 100644 src/ebusd/bushandler.cpp create mode 100644 src/ebusd/bushandler.h delete mode 100644 src/ebusd/busloop.cpp delete mode 100644 src/ebusd/busloop.h mode change 100644 => 100755 src/lib/ebus/Makefile.am delete mode 100644 src/lib/ebus/command.cpp delete mode 100644 src/lib/ebus/command.h delete mode 100644 src/lib/ebus/commands.cpp delete mode 100644 src/lib/ebus/commands.h delete mode 100644 src/lib/ebus/configfile.cpp delete mode 100644 src/lib/ebus/configfile.h delete mode 100644 src/lib/ebus/decode.cpp delete mode 100644 src/lib/ebus/decode.h delete mode 100644 src/lib/ebus/encode.cpp delete mode 100644 src/lib/ebus/encode.h mode change 100644 => 100755 src/lib/ebus/test/Makefile.am delete mode 100644 src/lib/ebus/test/test_commands.cpp delete mode 100644 src/lib/ebus/test/test_configfile.cpp delete mode 100644 src/lib/ebus/test/test_decode.cpp delete mode 100644 src/lib/ebus/test/test_encode.cpp mode change 100644 => 100755 src/lib/utils/Makefile.am mode change 100644 => 100755 src/tools/Makefile.am diff --git a/src/ebusd/Makefile.am b/src/ebusd/Makefile.am index b99b0495..5d060fb6 100644 --- a/src/ebusd/Makefile.am +++ b/src/ebusd/Makefile.am @@ -6,8 +6,8 @@ AM_CXXFLAGS = -fpic \ bin_PROGRAMS = ebusd -ebusd_SOURCES = busloop.cpp \ - busloop.h \ +ebusd_SOURCES = bushandler.cpp \ + bushandler.h \ network.cpp \ network.h \ baseloop.cpp \ @@ -16,7 +16,7 @@ ebusd_SOURCES = busloop.cpp \ ebusd_LDADD = $(top_srcdir)/src/lib/utils/libutils.a \ $(top_srcdir)/src/lib/ebus/libebus.a \ - -lpthread + -lpthread -lrt distclean-local: -rm -f Makefile.in diff --git a/src/ebusd/baseloop.cpp b/src/ebusd/baseloop.cpp index 6bcf1958..42bd1ce0 100644 --- a/src/ebusd/baseloop.cpp +++ b/src/ebusd/baseloop.cpp @@ -18,9 +18,10 @@ */ #include "baseloop.h" -#include "configfile.h" #include "logger.h" #include "appl.h" +#include +#include using namespace std; @@ -30,15 +31,55 @@ extern Appl& A; BaseLoop::BaseLoop() { // create commands DB - m_commands = ConfigCommands(A.getOptVal("ebusconfdir"), ft_csv).getCommands(); - L.log(bas, trace, "ebus configuration dir: %s", A.getOptVal("ebusconfdir")); - L.log(bas, event, "commands DB: %d ", m_commands->sizeCmdDB()); - L.log(bas, event, " cycle DB: %d ", m_commands->sizeCycDB()); - L.log(bas, event, " polling DB: %d ", m_commands->sizePollDB()); + m_templates = new DataFieldTemplates(); + m_messages = new MessageMap(); - // create busloop - m_busloop = new BusLoop(m_commands); - m_busloop->start("busloop"); + string confdir = A.getOptVal("ebusconfdir"); + L.log(bas, trace, "ebus configuration dir: %s", confdir.c_str()); + result_t result = m_templates->readFromFile(confdir+"/_types.csv"); + if (result == RESULT_OK) + L.log(bas, trace, "read templates"); + else + L.log(bas, error, "error reading templates: %s", getResultCode(result)); + result = readConfigFiles(confdir, ".csv"); + if (result == RESULT_OK) + L.log(bas, trace, "read config files"); + else + L.log(bas, error, "error reading config files: %s", getResultCode(result)); + + /*L.log(bas, event, "commands DB: %d ", m_commands->sizeCmdDB()); + L.log(bas, event, " cycle DB: %d ", m_commands->sizeCycDB()); + L.log(bas, event, " polling DB: %d ", m_commands->sizePollDB());*/ + + m_ownAddress = A.getOptVal("address") & 0xff; + const bool answer = A.getOptVal("answer"); + + const bool logRaw = A.getOptVal("lograwdata"); + + const bool dumpRaw = A.getOptVal("dump"); + const char* dumpRawFile = A.getOptVal("dumpfile"); + const long dumpRawMaxSize = A.getOptVal("dumpsize"); + + const unsigned int busLostRetries = A.getOptVal("lockretries"); + const unsigned int failedSendRetries = A.getOptVal("sendretries"); + const unsigned int busAcquireWaitTime = A.getOptVal("acquiretimeout"); + const unsigned int slaveRecvTimeout = A.getOptVal("recvtimeout"); + const unsigned int lockCount = A.getOptVal("lockcounter"); + + // create Port + m_port = new Port(A.getOptVal("device"), A.getOptVal("nodevicecheck"), logRaw, &BaseLoop::logRaw, dumpRaw, dumpRawFile, dumpRawMaxSize); + m_port->open(); + + if (m_port->isOpen() == false) + L.log(bus, error, "can't open %s", A.getOptVal("device")); + + // create BusHandler + m_busHandler = new BusHandler(m_port, m_messages, + answer ? m_ownAddress : SYN, answer ? (m_ownAddress+5)&0xff : SYN, + busLostRetries, failedSendRetries, + busAcquireWaitTime, slaveRecvTimeout, + lockCount); + m_busHandler->start("bushandler"); // create network m_network = new Network(A.getOptVal("localhost"), &m_netQueue); @@ -47,22 +88,63 @@ BaseLoop::BaseLoop() BaseLoop::~BaseLoop() { - // free network if (m_network != NULL) delete m_network; - // free busloop - if (m_busloop != NULL) { - m_busloop->stop(); - m_busloop->join(); - delete m_busloop; + if (m_busHandler != NULL) { + m_busHandler->stop(); + m_busHandler->join(); + delete m_busHandler; } - // free commands DB - if (m_commands != NULL) - delete m_commands; + if (m_port != NULL) + delete m_port; + + if (m_messages != NULL) + delete m_messages; + + if (m_templates != NULL) + delete m_templates; } +result_t BaseLoop::readConfigFiles(const string path, const string extension) +{ + DIR* dir = opendir(path.c_str()); + + if (dir == NULL) + return RESULT_ERR_NOTFOUND; + + dirent* d = readdir(dir); + + while (d != NULL) { + if (d->d_type == DT_DIR) { + string fn = d->d_name; + + if (fn != "." && fn != "..") { + const string p = path + "/" + d->d_name; + result_t result = readConfigFiles(p, extension); + if (result != RESULT_OK) + return result; + } + } else if (d->d_type == DT_REG) { + string fn = d->d_name; + + if (fn.find(extension, (fn.length() - extension.length())) != string::npos + && fn != "_types" + extension) { + const string p = path + "/" + d->d_name; + result_t result = m_messages->readFromFile(p, m_templates); + if (result != RESULT_OK) + return result; + } + } + + d = readdir(dir); + } + closedir(dir); + + return RESULT_OK; +}; + void BaseLoop::start() { for (;;) { @@ -96,16 +178,24 @@ void BaseLoop::start() } } +void BaseLoop::logRaw(const unsigned char byte, bool received) { + if (received == true) { + L.log(bus, event, "<%02x", byte); + } else { + L.log(bus, event, ">%02x", byte); + } +} + string BaseLoop::decodeMessage(const string& data) { ostringstream result; string cycdata, polldata; - int index; // prepare data string token; istringstream stream(data); vector cmd; + Message* message; while (getline(stream, token, ' ') != 0) cmd.push_back(token); @@ -119,17 +209,19 @@ string BaseLoop::decodeMessage(const string& data) break; case ct_get: - if (cmd.size() < 3 || cmd.size() > 4) { - result << "usage: 'get class cmd (sub)'"; + if (cmd.size() < 2 || cmd.size() > 4) { + result << "usage: 'get [class] cmd' or 'get class cmd sub'"; break; } - index = m_commands->findCommand(data); + if (cmd.size() == 2) + message = m_messages->find("", cmd[1], false); + else + message = m_messages->find(cmd[1], cmd[2], false); - if (index >= 0) { + if (message != NULL) { - // polling data - if (strcasecmp(m_commands->getCmdType(index).c_str(), "P") == 0) { + /*if (message->getPollPriority() > 0) // get polldata polldata = m_commands->getPollData(index); if (polldata != "") { @@ -144,38 +236,35 @@ string BaseLoop::decodeMessage(const string& data) result << "no data stored"; } + break; + }*/ + + SymbolString master; + istringstream input; + result_t ret = message->prepareMaster(m_ownAddress, master, input); + if (ret != RESULT_OK) { + L.log(bas, error, " prepare message: %s", getResultCode(ret)); + result << getResultCode(ret); break; } + L.log(bas, event, " msg: %s", master.getDataStr().c_str()); - string busCommand(A.getOptVal("address")); - busCommand += m_commands->getBusCommand(index); - transform(busCommand.begin(), busCommand.end(), busCommand.begin(), ::tolower); - - BusMessage* message = new BusMessage(busCommand, false, false); - L.log(bas, trace, " msg: %s", busCommand.c_str()); // send message - m_busloop->addMessage(message); - message->waitSignal(); + SymbolString slave; + ret = m_busHandler->sendAndWait(master, slave); - if (!message->isErrorResult()) { - // decode data - Command* command = new Command(index, (*m_commands)[index], message->getMessageStr()); // TODO use getCommand()+getResult() - - // return result - result << command->calcResult(cmd); - - delete command; - } else { - L.log(bas, error, " %s", message->getResultCodeCStr()); - result << message->getResultCodeCStr(); + if (ret == RESULT_OK) { + // TODO reduce to requested variable only + ret = message->decode(pt_slaveData, slave, result); // decode data + } + if (ret != RESULT_OK) { + L.log(bas, error, " %s", getResultCode(ret)); + result << getResultCode(ret); } - - delete message; } else { result << "ebus command not found"; } - break; case ct_set: @@ -184,48 +273,34 @@ string BaseLoop::decodeMessage(const string& data) break; } - index = m_commands->findCommand(data.substr(0, data.find(cmd[3])-1)); + message = m_messages->find(cmd[1], cmd[2], true); - if (index >= 0) { + if (message != NULL) { - string busCommand(A.getOptVal("address")); - busCommand += m_commands->getBusCommand(index); - - // encode data - Command* command = new Command(index, (*m_commands)[index], cmd[3]); - string value = command->calcData(); - if (value[0] != '-') { - busCommand += value; - } else { - L.log(bas, error, " %s", value.c_str()); - delete command; + SymbolString master; + istringstream input(cmd[3]); + result_t ret = message->prepareMaster(m_ownAddress, master, input); + if (ret != RESULT_OK) { + L.log(bas, error, " prepare message: %s", getResultCode(ret)); + result << getResultCode(ret); break; } + L.log(bas, event, " msg: %s", master.getDataStr().c_str()); - transform(busCommand.begin(), busCommand.end(), busCommand.begin(), ::tolower); - - BusMessage* message = new BusMessage(busCommand, false, false); - L.log(bas, event, " msg: %s", busCommand.c_str()); // send message - m_busloop->addMessage(message); - message->waitSignal(); + SymbolString slave; + ret = m_busHandler->sendAndWait(master, slave); - if (!message->isErrorResult()) { - // decode result - if (message->getType()==broadcast) - result << "done"; - else if (message->getMessageStr().substr(message->getMessageStr().length()-8) == "00000000") // TODO use getResult() + if (ret == RESULT_OK) { + if (master[1] == BROADCAST || isMaster(master[1])) result << "done"; else - result << "error"; - - } else { - L.log(bas, error, " %s", message->getResultCodeCStr()); - result << message->getResultCodeCStr(); + ret = message->decode(pt_slaveData, slave, result); // decode data + } + if (ret != RESULT_OK) { + L.log(bas, error, " %s", getResultCode(ret)); + result << getResultCode(ret); } - - delete message; - delete command; } else { result << "ebus command not found"; @@ -234,24 +309,20 @@ string BaseLoop::decodeMessage(const string& data) break; case ct_cyc: - if (cmd.size() < 3 || cmd.size() > 4) { - result << "usage: 'cyc class cmd (sub)'"; + if (cmd.size() < 2 || cmd.size() > 3) { + result << "usage: 'cyc [class] cmd'"; break; } - index = m_commands->findCommand(data); + if (cmd.size() == 2) + message = m_messages->find("", cmd[1], false, true); + else + message = m_messages->find(cmd[1], cmd[2], false, true); - if (index >= 0) { - // get cycdata - cycdata = m_commands->getCycData(index); - if (cycdata != "") { - // decode data - Command* command = new Command(index, (*m_commands)[index], cycdata); - - // return result - result << command->calcResult(cmd); - - delete command; + if (message != NULL) { + token = message->getLastValue(); + if (token.empty() == false) { + result << token; } else { result << "no data stored"; } @@ -268,30 +339,32 @@ string BaseLoop::decodeMessage(const string& data) } { - string busCommand(A.getOptVal("address")); cmd[1].erase(remove_if(cmd[1].begin(), cmd[1].end(), ::isspace), cmd[1].end()); - busCommand += cmd[1]; - transform(busCommand.begin(), busCommand.end(), busCommand.begin(), ::tolower); + string src; + ostringstream msg; + msg << hex << setw(2) << setfill('0') << static_cast(m_ownAddress); + msg << cmd[1]; + SymbolString master(msg.str()); + L.log(bas, event, " msg: %s", master.getDataStr().c_str()); - BusMessage* message = new BusMessage(busCommand, false, false); - L.log(bas, trace, " msg: %s", busCommand.c_str()); // send message - m_busloop->addMessage(message); - message->waitSignal(); + SymbolString slave; + result_t ret = m_busHandler->sendAndWait(master, slave); - if (message->isErrorResult()) { - L.log(bas, error, " %s", message->getResultCodeCStr()); - result << message->getResultCodeCStr(); - } else { - result << message->getMessageStr(); // TODO use getCommand()+getResult() + if (ret == RESULT_OK) + // decode data + result << slave.getDataStr(); + + if (ret != RESULT_OK) { + L.log(bas, error, " %s", getResultCode(ret)); + result << getResultCode(ret); } - delete message; } break; - case ct_scan: + /*case ct_scan: if (cmd.size() == 1) { m_busloop->scan(); result << "done"; @@ -315,7 +388,7 @@ string BaseLoop::decodeMessage(const string& data) result << "usage: 'scan'" << endl << " 'scan full'" << endl << " 'scan result'"; - break; + break;*/ case ct_log: if (cmd.size() != 3 ) { @@ -348,7 +421,7 @@ string BaseLoop::decodeMessage(const string& data) break; } - m_busloop->raw(); + m_port->setLogRaw(!m_port->getLogRaw()); result << "done"; break; @@ -358,11 +431,11 @@ string BaseLoop::decodeMessage(const string& data) break; } - m_busloop->dump(); + m_port->setDumpRaw(!m_port->getDumpRaw()); result << "done"; break; - case ct_reload: + /*case ct_reload: if (cmd.size() != 1) { result << "usage: 'reload'"; break; @@ -382,7 +455,7 @@ string BaseLoop::decodeMessage(const string& data) result << "done"; break; - } + }*/ case ct_help: result << "commands:" << endl diff --git a/src/ebusd/baseloop.h b/src/ebusd/baseloop.h index d45556ba..73f6407b 100644 --- a/src/ebusd/baseloop.h +++ b/src/ebusd/baseloop.h @@ -20,9 +20,9 @@ #ifndef BASELOOP_H_ #define BASELOOP_H_ -#include "commands.h" +#include "message.h" #include "network.h" -#include "busloop.h" +#include "bushandler.h" using namespace std; @@ -51,15 +51,22 @@ class BaseLoop public: /** - * @brief construct the baseloop and creates commads, network and busloop subsystems. + * @brief Construct the base loop and create messaging, network and bus handling subsystems. */ BaseLoop(); /** - * @brief destructor. + * @brief Destructor. */ ~BaseLoop(); + /** + * @brief Read the configuration files from the specified path. + * @param path the path from which to read the files. + * @param extension the filename extension of the files to read. + */ + result_t readConfigFiles(const string path, const string extension); + /** * @brief start baseloop instance. */ @@ -71,14 +78,31 @@ public: */ void addMessage(NetMessage* message) { m_netQueue.add(message); } + /** + * @brief Create a log message for a received/sent raw data byte. + * @param param byte the raw data byte. + * @param received true if the byte was received, false if it was sent. + */ + static void logRaw(const unsigned char byte, bool received); + private: - /** the commands instance */ - Commands* m_commands; - /** the busloop instance */ - BusLoop* m_busloop; + /** the @a DataFieldTemplates instance. */ + DataFieldTemplates* m_templates; - /** the network instance */ + /** the @a MessageMap instance. */ + MessageMap* m_messages; + + /** the own master address for sending on the bus. */ + unsigned char m_ownAddress; + + /** the @a Port instance. */ + Port* m_port; + + /** the @a BusHandler instance. */ + BusHandler* m_busHandler; + + /** the @a Network instance. */ Network* m_network; /** queue for network messages */ diff --git a/src/ebusd/bushandler.cpp b/src/ebusd/bushandler.cpp new file mode 100644 index 00000000..30d43e08 --- /dev/null +++ b/src/ebusd/bushandler.cpp @@ -0,0 +1,488 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#include "bushandler.h" +#include "message.h" +#include "data.h" +#include "result.h" +#include "symbol.h" +#include "logger.h" +#include "appl.h" +#include +#include +#include +#include + +using namespace std; + +extern Logger& L; +extern Appl& A; + +/** + * @brief Return the string corresponding to the @a BusState. + * @param state the @a BusState. + * @return the string corresponding to the @a BusState. + */ +const char* getStateCode(BusState state) { + switch (state) + { + 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_sendRes: return "send response"; +// case bs_sendCmdAck: return "send command ACK"; + case bs_sendSyn: return "send SYN"; + default: return "unknown"; + } +} + + +BusRequest::BusRequest(SymbolString& master, SymbolString& slave) + : m_master(master), m_slave(slave), m_finished(false), m_result(RESULT_SYN) +{ + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); +} + +BusRequest::~BusRequest() +{ + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_cond); +} + +bool BusRequest::wait(int timeout) +{ + m_finished = false; + m_result = RESULT_SYN; + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + t.tv_sec += timeout; + int result = 0; + + pthread_mutex_lock(&m_mutex); + + while (m_finished == false && result == 0) + result = pthread_cond_timedwait(&m_cond, &m_mutex, &t); + + if (result == 0 && m_finished == false) + result = 1; + + pthread_mutex_unlock(&m_mutex); + + return result == 0; +} + +void BusRequest::notify(result_t result) +{ + pthread_mutex_lock(&m_mutex); + + m_result = result; + m_finished = true; + pthread_cond_signal(&m_cond); + + pthread_mutex_unlock(&m_mutex); +} + + +result_t BusHandler::sendAndWait(SymbolString& master, SymbolString& slave) +{ + result_t result = RESULT_SYN; + BusRequest* request = new BusRequest(master, slave); + + for (int sendRetries=m_failedSendRetries+1, lostRetries=m_busLostRetries+1; sendRetries>=0; sendRetries--) { + m_requests.add(request); + bool success = request->wait(1); // 1 second is still 3 times the theoretical worst-case request duration + if (success == false) + m_requests.remove(request); + result = success == true ? request->m_result : RESULT_ERR_TIMEOUT; + + if (result == RESULT_OK) + break; + + if (result == RESULT_ERR_BUS_LOST) { + if (--lostRetries > 0) { + sendRetries++; // try to get lock again, do not decrement send retries + L.log(bus, error, " %s, retry bus loss", getResultCode(result)); + continue; + } + lostRetries = m_busLostRetries+1; // send retry: reset lock retries + } + L.log(bus, error, " %s, %s", getResultCode(result), sendRetries>0 ? "retry send" : "give up"); + } + + delete request; + + return result; +} + +void BusHandler::run() +{ + do { + if (m_port->isOpen() == true) + handleSymbol(); + else { + // TODO: define max reopen + sleep(10); + result_t result = m_port->open(); + + if (result != RESULT_OK) + L.log(bus, error, "can't open %s", A.getOptVal("device")); + + } + + } while (isRunning() == true); +} + +result_t BusHandler::handleSymbol() +{ + long timeout = SYN_TIMEOUT; + unsigned char sendSymbol = ESC; + bool sending = false; + + // check if another symbol has to be sent and determine timeout for receive + switch (m_state) + { + case bs_skip: + timeout = 0; // endless + break; + + case bs_ready: + if (m_request != NULL) + setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up + if (m_remainLockCount == 0) { + m_request = m_requests.next(false); + if (m_request != NULL) { // initiate arbitration + sendSymbol = m_request->m_master[0]; + sending = true; + } + } + break; + + case bs_recvCmd: + case bs_recvCmdAck: + case bs_recvRes: + case bs_recvResAck: + timeout = m_slaveRecvTimeout; + break; + + case bs_sendCmd: + if (m_request != NULL) { + sendSymbol = m_request->m_master[m_nextSendPos]; + sending = true; + } + break; + + case bs_sendResAck: + if (m_request != NULL) { + sendSymbol = m_responseCrcValid ? ACK : NAK; + sending = true; + } + break; + + case bs_sendSyn: + sendSymbol = SYN; + sending = true; + break; + } + + // send symbol if necessary + if (sending == true) { + if (m_port->send(&sendSymbol, 1) == 1) + if (m_state == bs_ready) + timeout = m_busAcquireTimeout; + else + timeout = SEND_TIMEOUT; + else { + sending = false; + timeout = 0; + setState(bs_skip, RESULT_ERR_SEND); + } + } + + // receive next symbol (optionally check reception of sent symbol) + unsigned char recvSymbol; + ssize_t count = m_port->recv(timeout, 1, &recvSymbol); + + if (count < 0) // count < 0 is a RESULT_ERR_ code + return setState(bs_skip, count); // TODO keep "no signal" within auto-syn state + + //unsigned char recvSymbol = m_port->byte(); // TODO remove me + if (recvSymbol == SYN) { + if (sending == false && m_remainLockCount > 0) + m_remainLockCount--; + return setState(bs_ready, RESULT_SYN); + } + + unsigned char headerLen, crcPos; + result_t result; + + switch (m_state) + { + case bs_skip: + return RESULT_OK; + + case bs_ready: + if (m_request != NULL && sending == true) { + if (m_requests.remove(m_request) == false) { + // request already timed out + return setState(bs_skip, RESULT_ERR_TIMEOUT); + } + // 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; + 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; + 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]; + //if (isValidAddress(dstAddress) == false || isMaster(m_command[0]) == false) + // return setState(bs_skip, RESULT_ERR_INVALID_ADDR); + + m_commandCrcValid = m_command[headerLen + 1 + m_command[headerLen]] == m_command.getCRC(); + if (m_commandCrcValid) { + if (dstAddress == BROADCAST) { + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } + //if (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_OK); + + //if (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress) + // return setState(bs_sendCmdAck, RESULT_ERR_CRC); + if (m_repeat == true) + 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 == false) + return setState(bs_skip, RESULT_ERR_ACK); + + if (m_request != NULL) { + if (isMaster(m_request->m_master[1]) == true) { + return setState(bs_sendSyn, RESULT_OK); + } + } else if (isMaster(m_command[1]) == true) { + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } + + m_repeat = false; + return setState(bs_recvRes, RESULT_OK); + } + if (recvSymbol == NAK) { + if (m_repeat == false) { + m_repeat = true; + m_nextSendPos = 0; + m_command.clear(); + if (m_request != NULL) + return setState(bs_sendCmd, RESULT_ERR_NAK, true); + + return setState(bs_recvCmd, RESULT_ERR_NAK); + } + if (m_request != NULL) + return setState(bs_skip, RESULT_ERR_NAK); + + return setState(bs_skip, RESULT_ERR_NAK); + } + if (m_request != NULL) + return setState(bs_skip, RESULT_ERR_ACK); + + 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) { + if (m_request != NULL) + return setState(bs_skip, result); + + 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_request != NULL) + return setState(bs_sendResAck, RESULT_OK); + + return setState(bs_recvResAck, RESULT_OK); + } + if (m_repeat == true) { + if (m_request != NULL) + return setState(bs_skip, RESULT_ERR_CRC); + + return setState(bs_skip, RESULT_ERR_CRC); + } + if (m_request != 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 == false) + return setState(bs_skip, RESULT_ERR_ACK); + + receiveCompleted(); + return setState(bs_skip, RESULT_OK); + } + if (recvSymbol == NAK) { + if (m_repeat == false) { + 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_request != NULL && sending == true) { + if (recvSymbol == sendSymbol) { + // successfully sent + m_nextSendPos++; + if (m_nextSendPos >= m_request->m_master.size()) { + // master data completely sent + if (m_request->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_request != NULL && sending == true) { + if (recvSymbol == sendSymbol) { + // successfully sent + return setState(bs_sendSyn, RESULT_OK); + } + } + return setState(bs_skip, RESULT_ERR_INVALID_ARG); + + case bs_sendSyn: + if (sending == true) { + if (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_request != NULL) { + if (state == bs_sendSyn || (result != RESULT_OK && firstRepetition == false)) { + L.log(bus, debug, "notify request: %s", getResultCode(result)); + m_request->m_slave = m_response; // TODO nicer + m_request->notify(result); + m_request = NULL; + } + } + + if (state == m_state) + return result; + + if (result < RESULT_OK || (result != RESULT_OK && state == bs_skip)) + L.log(bus, debug, " %s during %s, switching to %s", getResultCode(result), getStateCode(m_state), getStateCode(state)); + else if (m_request != NULL || state == bs_sendCmd || state==bs_sendResAck || state==bs_sendSyn) + L.log(bus, debug, " switching from %s to %s", getStateCode(m_state), getStateCode(state)); + m_state = state; + + if (state == bs_ready || state == bs_skip) { + m_command.clear(); + m_commandCrcValid = false; + m_response.clear(); + m_responseCrcValid = false; + m_nextSendPos = 0; + } + + return result; +} + +void BusHandler::receiveCompleted() +{ + Message* message = m_messages->find(m_command); + if (message != NULL) { + string clazz = message->getClass(); + string name = message->getName(); + ostringstream output; + result_t result = message->decode(pt_masterData, m_command, output); + if (result == RESULT_OK) + result = message->decode(pt_slaveData, m_response, output, output.str().empty() == false); + if (result != RESULT_OK) + L.log(bus, error, "unable to parse %s %s from %s / %s: %s", clazz.c_str(), name.c_str(), m_command.getDataStr().c_str(), m_response.getDataStr().c_str(), getResultCode(result)); + else { + string data = output.str(); + L.log(bus, trace, "%s %s: %s", clazz.c_str(), name.c_str(), data.c_str()); + } + return; + } + if (m_command[1] == BROADCAST) + L.log(bus, trace, "received broadcast %s", m_command.getDataStr().c_str()); + else if (isMaster(m_command[1]) == true) + L.log(bus, trace, "received master-master %s", m_command.getDataStr().c_str()); + else + L.log(bus, trace, "received master-slave %s / %s", m_command.getDataStr().c_str(), m_response.getDataStr().c_str()); +} diff --git a/src/ebusd/bushandler.h b/src/ebusd/bushandler.h new file mode 100644 index 00000000..8ad6e474 --- /dev/null +++ b/src/ebusd/bushandler.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of ebusd. + * + * ebusd is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ebusd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ebusd. If not, see http://www.gnu.org/licenses/. + */ + +#ifndef BUSHANDLER_H_ +#define BUSHANDLER_H_ + +#include "message.h" +#include "data.h" +#include "symbol.h" +#include "result.h" +#include "port.h" +#include "wqueue.h" +#include "thread.h" +#include +#include +#include +#include + +using namespace std; + +/** the maximum allowed time [us] for retrieving a symbol from an addressed slave. */ +//#define SLAVE_RECV_TIMEOUT 10000 +/** the maximum allowed time [us] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ +#define SYN_TIMEOUT 50800 +/** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */ +#define SYMBOL_DURATION 4700 +/** the maximum allowed time [us] for retrieving back a sent symbol (2x symbol duration). */ +#define SEND_TIMEOUT (2*SYMBOL_DURATION) + +/** the possible bus states. */ +enum BusState { + 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_sendRes, // send response (slave data) [passive get] // TODO implement +// bs_sendCmdAck, // send command ACK/NACK [passive get] // TODO implement + bs_sendSyn, // send SYN for completed transfer [active set+get] +}; + +/** the possible combinations of participants in a single message exchange. */ +enum MessageDirection { + md_thisToAll, // message from us to all (broadcast) + md_thisToMaster, // message from us to another master + md_thisToSlave, // message from us to another slave + md_otherToAll, // message from a master (other than us) to all (broadcast) + md_otherToMaster, // message from a master (other than us) to another master (other than us) + md_otherToSlave, // message from a master (other than us) to another slave (other than us) + md_otherToThisMaster, // message from a master (other than us) to us (as master) + md_otherToThisSlave, // message from a master (other than us) to us (as slave) + md_undefined, +}; + +class BusHandler; + +/** + * @brief Handles input from and output to the bus with respect to the ebus protocol. + */ +class BusRequest +{ + friend class BusHandler; +public: + + /** + * @brief Constructor. + * @param master the master data @a SymbolString to send. + * @param slave the slave data @a SymbolString received. + */ + BusRequest(SymbolString& master, SymbolString& slave); + + /** + * @brief Destructor. + */ + virtual ~BusRequest(); + + /** + * @brief Wait for notification. + * @param timeout the maximum time to wait in seconds. + * @return the result code. + */ + bool wait(int timeout); + + /** + * @brief Notify all waiting threads. + */ + void notify(result_t result); + +private: + + /** the master data @a SymbolString to send. */ + SymbolString& m_master; + + /** the slave data @a SymbolString received. */ + SymbolString& m_slave; + + /** true once the request is finished. */ + bool m_finished; + + /** the result of handling the request. */ + result_t m_result; + + /** a mutex for wait/notify. */ + pthread_mutex_t m_mutex; + + /** a mutex condition for wait/notify. */ + pthread_cond_t m_cond; + +}; + + +/** + * @brief Handles input from and output to the bus with respect to the ebus protocol. + */ +class BusHandler : public Thread +{ +public: + + /** + * @brief Construct a new instance. + * @param port the @a Port instance for accessing the bus. + * @param messages the @a MessageMap instance with all known @a Message instances. + * @param ownMasterAddress the own master address to react on master-master messages, or @a SYN to ignore. + * @param ownSlaveAddress the own slave address to react on master-slave messages, or @a SYN to ignore. + * @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 slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge. + * @param busAcquireTimeout the maximum time in microseconds for bus acquisition. + * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration. + */ + BusHandler(Port* port, MessageMap* messages, + const unsigned char ownMasterAddress, const unsigned char ownSlaveAddress, + const unsigned int busLostRetries, const unsigned int failedSendRetries, + const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout, + const unsigned int lockCount) + : m_port(port), m_messages(messages), + m_ownMasterAddress(ownMasterAddress), m_ownSlaveAddress(ownSlaveAddress), + m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), + m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), + m_lockCount(lockCount), m_remainLockCount(lockCount), + m_request(NULL), m_nextSendPos(0), + m_state(bs_skip), m_repeat(false), + m_commandCrcValid(false), m_responseCrcValid(false) {} + + /** + * @brief Destructor. + */ + virtual ~BusHandler() {} + + /** + * @brief Send a message on the bus and wait for the answer. + * @param master the @a SymbolString with the master data to send. + * @param slave the @a SymbolString that will be filled with retrieved slave data. + */ + result_t sendAndWait(SymbolString& master, SymbolString& slave); + + /** + * @brief Main thread entry. + */ + virtual void run(); + + /** + * @brief Get the last received data for the @a Message. + * @param message the @a Message instance. + * @return the last received data for the @a Message, or the empty string if not available. + */ + string getReceivedData(Message* message); + +private: + + /** + * @brief Handle the next symbol on the bus. + * @return RESULT_OK on success, or an error code. + */ + result_t handleSymbol(); + + /** + * @brief 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); + + /** + * @brief Called when a passive reception was successfully completed. + */ + void receiveCompleted(); + + /** the @a Port instance for accessing the bus. */ + Port* m_port; + + /** the @a MessageMap instance with all known @a Message instances. */ + MessageMap* m_messages; + + /** the own master address to react on master-master messages, or @a SYN to ignore. */ + const unsigned char m_ownMasterAddress; + + /** the own slave address to react on master-slave messages, or @a SYN to ignore. */ + const unsigned char m_ownSlaveAddress; + + /** 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 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 number of AUTO-SYN symbols before sending is allowed after lost arbitration. */ + const unsigned int m_lockCount; + + /** the remaining number of AUTO-SYN symbols before sending is allowed again. */ + unsigned int m_remainLockCount; + + /** the queue of @a BusRequests that shall be handled. */ + WQueue m_requests; + + /** the currently handled BusRequest, or NULL. */ + BusRequest* m_request; + + /** 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 bs_command or bs_response). */ + unsigned char m_nextSendPos; + + /** the current @a BusState. */ + BusState m_state; + + /** whether the current message part is being repeated. */ + bool m_repeat; + + /** the received/sent command. */ + SymbolString m_command; + + /** whether the command CRC is valid. */ + bool m_commandCrcValid; + + /** the received/sent response. */ + SymbolString m_response; + + /** whether the response CRC is valid. */ + bool m_responseCrcValid; + +}; + + +#endif // BUSHANDLER_H_ diff --git a/src/ebusd/busloop.cpp b/src/ebusd/busloop.cpp deleted file mode 100644 index 541c0cac..00000000 --- a/src/ebusd/busloop.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "busloop.h" -#include "logger.h" -#include "appl.h" -#include -#include - -using namespace std; - -extern Logger& L; -extern Appl& A; - -BusMessage::BusMessage(const string command, const bool poll, const bool scan) - : m_poll(poll), m_scan(scan), m_command(command), m_result(), m_resultCode(RESULT_OK) -{ - unsigned char dstAddress = m_command[1]; - - if (dstAddress == BROADCAST) - m_type = broadcast; - else if (isMaster(dstAddress) == true) - m_type = masterMaster; - else - m_type = masterSlave; - - pthread_mutex_init(&m_mutex, NULL); - pthread_cond_init(&m_cond, NULL); -} - -const string BusMessage::getMessageStr() -{ - string result; - - if (m_resultCode >= 0) { - if (m_type == masterSlave) { - result = m_command.getDataStr(); - result += "00"; - result += m_result.getDataStr(); - result += "00"; - } - else - result = "success"; - } - else - result = "error: "+string(getResultCodeCStr()); - - return result; -} - - -BusLoop::BusLoop(Commands* commands) - : m_commands(commands), m_running(true), m_lockCounter(0), - m_priorRetry(false), m_scan(false), m_scanFull(false), m_scanIndex(0) -{ - m_port = new Port(A.getOptVal("device"), A.getOptVal("nodevicecheck")); - m_port->open(); - - if (m_port->isOpen() == false) - L.log(bus, error, "can't open %s", A.getOptVal("device")); - - m_dumpFile = A.getOptVal("dumpfile"); - m_dumpSize = A.getOptVal("dumpsize"); - m_dumping = A.getOptVal("dump"); - - m_logRawData = A.getOptVal("lograwdata"); - - m_pollInterval = A.getOptVal("pollinterval"); - - m_recvTimeout = A.getOptVal("recvtimeout"); - - m_sendRetries = A.getOptVal("sendretries"); - - m_lockRetries = A.getOptVal("lockretries"); - - m_acquireTime = A.getOptVal("acquiretime"); -} - -BusLoop::~BusLoop() -{ - if (m_port->isOpen() == true) - m_port->close(); - - delete m_port; -} - -void* BusLoop::run() -{ - int sendRetries = 0; - int lockRetries = 0; - - // polling - time_t pollStart, pollEnd; - time(&pollStart); - double pollDelta; - - for (;;) { - if (m_port->isOpen() == true) { - ssize_t numBytes; - - // add poll or scan command - if (m_commands->sizePollDB() > 0 || m_scan == true) { - // check polling delta - time(&pollEnd); - pollDelta = difftime(pollEnd, pollStart); - - // add new polling command to send - if (pollDelta >= m_pollInterval) { - if (m_scan == true) - addScanMessage(); - else - addPollMessage(); - - time(&pollStart); - } - } - - // read device - no timeout needed (AUTO-SYN) - numBytes = m_port->recv(0); - - if (numBytes < 0) { - L.log(bus, error, " ERR_DEVICE: generic device error"); - continue; - } - - // cycle bytes - collectCycData(numBytes); - - // send command - if (m_sstr.size() == 0 && m_lockCounter == 0 && m_busQueue.size() > 0) { - // acquire Bus - int busResult = acquireBus(); - - // send bus command - if (busResult == RESULT_BUS_ACQUIRED) { - BusMessage* message = sendCommand(); - L.log(bus, trace, " %s", message->getMessageStr().c_str()); - - if (message->isErrorResult() == true) { - if (sendRetries < m_sendRetries) { - sendRetries++; - L.log(bus, trace, " send retry %d", sendRetries); - message->setResult(string(), RESULT_OK); - } - else { - sendRetries = 0; - L.log(bus, event, " send retry failed", sendRetries); - - if (message->isPoll() == true) - delete m_busQueue.remove(); - else - message->sendSignal(); - } - } - else { - sendRetries = 0; - - if (message->isPoll() == true) { - if (message->isScan() == true) - m_commands->storeScanData(message->getMessageStr().c_str()); - else - m_commands->storePollData(message->getMessageStr().c_str()); // TODO use getResult() - delete message; - } - else - message->sendSignal(); - } - - lockRetries = 0; - m_lockCounter = A.getOptVal("lockcounter"); - } - else if (busResult == RESULT_ERR_BUS_LOST) { - L.log(bus, trace, " acquire bus failed"); - - if (lockRetries >= m_lockRetries) { - lockRetries = 0; - L.log(bus, event, " lock bus failed"); - - BusMessage* message = m_busQueue.remove(); - if (message->isPoll() == true) - delete message; - else - message->sendSignal(); - } - else { - lockRetries++; - L.log(bus, trace, " lock retry %d", lockRetries); - } - - m_lockCounter = A.getOptVal("lockcounter"); - } - - } - - } - else { - // TODO: define max reopen - sleep(10); - m_port->open(); - - if (m_port->isOpen() == false) - L.log(bus, error, "can't open %s", A.getOptVal("device")); - - } - - if (m_running == false) { - if (m_port->isOpen() == true) - m_port->close(); - - return NULL; - } - - } - - return NULL; -} - -int BusLoop::writeDumpFile(const char* byte) -{ - int ret = 0; - - ofstream fs(m_dumpFile.c_str(), ios::out | ios::binary | ios::app); - - if (fs == 0) - return -1; - - fs.write(byte, 1); - - if (fs.tellp() >= m_dumpSize * 1024) { - string oldfile; - oldfile += m_dumpFile; - oldfile += ".old"; - ret = rename(m_dumpFile.c_str(), oldfile.c_str()); - } - - fs.close(); - - return ret; -} - -unsigned char BusLoop::fetchByte() -{ - unsigned char byte; - - // fetch byte - byte = m_port->byte(); - - if (m_dumping == true) - writeDumpFile((const char*) &byte); - - if (m_logRawData == true) - L.log(bus, event, "%02x", byte); - - return byte; -} - -void BusLoop::collectCycData(const int numRecv) -{ - // cycle bytes - for (int i = 0; i < numRecv; i++) { - - // fetch byte - unsigned char byte = fetchByte(); - - if (byte == SYN) { - - // analyse cycle data - if (m_sstr.size() > 0) { - - L.log(cyc, trace, "%s", m_sstr.getDataStr().c_str()); - - analyseCycData(); - - if (m_sstr.size() == 1 && m_lockCounter == 0 && m_priorRetry == false) - m_lockCounter++; - - else if (m_lockCounter > 0) - m_lockCounter--; - - m_sstr.clear(); - } - - else if (m_lockCounter > 0) - m_lockCounter--; - - } - - // collect cycle data - else - m_sstr.push_back(byte, true, false); - } -} - -void BusLoop::analyseCycData() -{ - // check minimum length - if (m_sstr.size() < 6) { - L.log(cyc, trace, "ERR_CYC_LEN: message too short"); - return; - } - - // check master crc - int lenMaster = m_sstr[4]; - SymbolString master; - - for (int i = 0; i < 5+lenMaster; i++) - master.push_back(m_sstr[i], false , true); - - if (m_sstr[5+lenMaster] != master.getCRC()) { - L.log(cyc, trace, "ERR_CYC_CRC_M: %s - %02x %02x", master.getDataStr().c_str(), m_sstr[5+lenMaster], master.getCRC()); - return; - } - - // check slave crc - if (m_sstr[1] != BROADCAST || isMaster(m_sstr[1]) == false) { - int lenSlave = m_sstr[5+lenMaster+2]; - SymbolString slave; - - for (int i = 5+lenMaster+2; i < 5+lenMaster+3+lenSlave; i++) - slave.push_back(m_sstr[i], false , true); - - if (m_sstr[5+lenMaster+3+lenSlave] != slave.getCRC()) { - L.log(cyc, trace, "ERR_CYC_CRC_S: %s - %02x %02x", slave.getDataStr().c_str(), m_sstr[5+lenMaster+3+lenSlave], slave.getCRC()); - return; - } - } - - // store valid data - int index = m_commands->storeCycData(m_sstr.getDataStr()); - - if (index == -1) { - L.log(cyc, debug, " command not found"); - } - else if (index == -2) { - L.log(cyc, debug, " no commands defined"); - } - else if (index == -3) { - L.log(cyc, debug, " search skipped - string too short"); - } - else { - string tmp; - tmp += (*m_commands)[index][1]; - tmp += " "; - tmp += (*m_commands)[index][2]; - L.log(cyc, event, " cycle [%4d] %s", index, tmp.c_str()); - } - - // collect Slave address - if (index != -3) - collectSlave(); - -} - -void BusLoop::collectSlave() -{ - vector::iterator it; - - for (int i = 0; i < 2; i++) { - bool found = false; - unsigned char mm = m_sstr[i]; - - if (i == 0) { - if (mm == 0xFF) - mm = 0x04; - else - mm += 0x05; - } - - for (it = m_slave.begin(); it != m_slave.end(); it++) - if ((*it) == mm) - found = true; - - if (found == false && isMaster(mm) == false && mm != BROADCAST) { - m_slave.push_back(mm); - L.log(bus, event, " new slave: %d %02x", m_slave.size(), m_slave.back()); - } - } -} - -int BusLoop::acquireBus() -{ - unsigned char recvByte, sendByte; - ssize_t numRecv, numSend; - - sendByte = m_busQueue.next()->getCommand()[0]; - - // send QQ - numSend = m_port->send(&sendByte); - if (numSend <= 0) { - L.log(bus, error, " ERR_SEND: send error"); - return RESULT_ERR_SEND; - } - - // wait ~4200 usec for receive - usleep(m_acquireTime); - - // receive 1 byte - must be QQ - numRecv = m_port->recv(0); - - if (numRecv < 0) { - L.log(bus, error, " ERR_DEVICE: generic device error"); - return RESULT_ERR_DEVICE; - } - - if (numRecv == 1) { - // fetch byte - recvByte = fetchByte(); - - // compare sent and received byte - if (sendByte == recvByte) { - L.log(bus, trace, " bus acquired"); - return RESULT_BUS_ACQUIRED; - } - - // collect cycle data - if (recvByte != SYN) - m_sstr.push_back(recvByte, true, false); - - // compare prior nibble for retry - if ((sendByte & 0x0F) == (recvByte & 0x0F)) { - m_priorRetry = true; - L.log(bus, trace, " bus prior retry"); - return RESULT_BUS_PRIOR_RETRY; - } - - L.log(bus, error, " ERR_BUS_LOST: lost bus arbitration"); - return RESULT_ERR_BUS_LOST; - } - - // cycle bytes - collectCycData(numRecv); - - L.log(bus, error, " ERR_EXTRA_DATA: received bytes > sent bytes"); - return RESULT_ERR_EXTRA_DATA; -} - -BusMessage* BusLoop::sendCommand() -{ - unsigned char recvByte; - string result; - SymbolString slaveData; - int retval = RESULT_OK; - - BusMessage* message = m_busQueue.next(); - - // send ZZ PB SB NN Dx CRC - SymbolString command = message->getCommand(); - for (size_t i = 1; i < command.size(); i++) { - retval = sendByte(command[i]); - if (retval < 0) - goto on_exit; - } - - // BC -> send SYN - if (message->getType() == broadcast) { - sendByte(SYN); - goto on_exit; - } - - // receive ACK - retval = recvSlaveAck(recvByte); - if (retval < 0) - goto on_exit; - - // is slave ACK negative? - if (recvByte == NAK) { - - // send QQ ZZ PB SB NN Dx CRC again - for (size_t i = 0; i < command.size(); i++) { - retval = sendByte(command[i]); - if (retval < 0) - goto on_exit; - } - - // receive ACK - retval = recvSlaveAck(recvByte); - if (retval < 0) - goto on_exit; - - // is slave ACK negative? - if (recvByte == NAK) { - sendByte(SYN); - L.log(bus, error, " ERR_NAK: NAK received"); - retval = RESULT_ERR_NAK; - goto on_exit; - } - } - - // MM -> send SYN - if (message->getType() == masterMaster) { - sendByte(SYN); - goto on_exit; - } - - // receive NN, Dx, CRC - retval = recvSlaveData(slaveData); - - // are calculated and received CRC equal? - if (retval == RESULT_ERR_CRC) { - - // send NAK - retval = sendByte(NAK); - if (retval < 0) - goto on_exit; - - // receive NN, Dx, CRC - slaveData.clear(); - retval = recvSlaveData(slaveData); - - // are calculated and received CRC equal? - if (retval == RESULT_ERR_CRC) { - - // send NAK - retval = sendByte(NAK); - if (retval >= 0) - retval = RESULT_ERR_CRC; - } - } - - if (retval < 0) - goto on_exit; - - // send ACK - retval = sendByte(ACK); - if (retval == -1) { - L.log(bus, error, " ERR_ACK: ACK error"); - retval = RESULT_ERR_ACK; - goto on_exit; - } - - // MS -> send SYN - sendByte(SYN); - -on_exit: - - // empty receive buffer - while (m_port->size() != 0) - recvByte = fetchByte(); - - message->setResult(slaveData, retval); - - if (retval == RESULT_OK) - return m_busQueue.remove(); - else - return message; - -} - -int BusLoop::sendByte(const unsigned char sendByte) -{ - unsigned char recvByte; - ssize_t numRecv, numSend; - - numSend = m_port->send(&sendByte); - - // receive 1 byte - must be equal - numRecv = m_port->recv(RECV_TIMEOUT); - - if (numSend != numRecv) { - L.log(bus, error, " ERR_EXTRA_DATA: received bytes > sent bytes"); - return RESULT_ERR_EXTRA_DATA; - } - - recvByte = fetchByte(); - - if (sendByte != recvByte) { - L.log(bus, error, " ERR_SEND: send error"); - return RESULT_ERR_SEND; - } - - return RESULT_OK; -} - -int BusLoop::recvSlaveAck(unsigned char& recvByte) -{ - ssize_t numRecv; - - // receive ACK - numRecv = m_port->recv(m_recvTimeout); - - if (numRecv > 1) { - L.log(bus, error, " ERR_EXTRA_DATA: received bytes > sent bytes"); - return RESULT_ERR_EXTRA_DATA; - } - else if (numRecv < 0) { - L.log(bus, error, " ERR_TIMEOUT: read timeout"); - return RESULT_ERR_TIMEOUT; - } - - recvByte = fetchByte(); - - // is received byte SYN? - if (recvByte == SYN) { - L.log(bus, error, " ERR_SYN: SYN received"); - return RESULT_ERR_SYN; - } - - return RESULT_OK; -} - -int BusLoop::recvSlaveData(SymbolString& result) -{ - unsigned char recvByte, calcCrc = 0; - ssize_t numRecv; - size_t NN = 0; - bool updateCrc = true; - int retval = 0; - - for (size_t i = 0, needed = 1; i < needed; i++) { - numRecv = m_port->recv(RECV_TIMEOUT); - if (numRecv < 0) { - L.log(bus, error, " ERR_TIMEOUT: read timeout"); - return RESULT_ERR_TIMEOUT; - } - - recvByte = fetchByte(); - retval = result.push_back(recvByte, true, updateCrc); - if (retval < 0) - return retval; - - if (retval == RESULT_IN_ESC) - needed++; - else if (result.size() == 1) { // NN received - NN = result[0]; - needed += NN; - } - else if (NN > 0 && result.size() == 1+NN) {// all data received - updateCrc = false; - calcCrc = result.getCRC(); - needed++; - } - } - - if (retval == RESULT_IN_ESC) { - L.log(bus, error, " ERR_ESC: invalid escape sequence received"); - return RESULT_ERR_ESC; - } - - if (updateCrc == true || calcCrc != result[result.size()-1]) { - L.log(bus, error, " ERR_CRC: CRC error"); - return RESULT_ERR_CRC; - } - - return RESULT_OK; -} - -void BusLoop::addPollMessage() -{ - int index = m_commands->nextPollCommand(); - if (index < 0) { - L.log(bus, error, "polling index out of range"); - } - else { - // TODO: implement as methode from class commands? - string tmp; - tmp += (*m_commands)[index][1]; - tmp += " "; - tmp += (*m_commands)[index][2]; - L.log(bus, event, " polling [%4d] %s", index, tmp.c_str()); - - string busCommand(A.getOptVal("address")); - busCommand += m_commands->getBusCommand(index); - transform(busCommand.begin(), busCommand.end(), busCommand.begin(), ::tolower); - - BusMessage* message = new BusMessage(busCommand, true, false); - L.log(bus, trace, " msg: %s", busCommand.c_str()); - - addMessage(message); - } -} - -void BusLoop::addScanMessage() -{ - string busCommand(A.getOptVal("address")); - stringstream sstr; - - if (m_scanFull == true) { - for (; m_scanIndex <= 0xFF; m_scanIndex++) { - if (isMaster(m_scanIndex) == false && m_scanIndex != SYN - && m_scanIndex != ESC && m_scanIndex != BROADCAST) { - sstr << nouppercase << setw(2) << setfill('0') - << hex << m_scanIndex; - break; - } - } - } - else { - sstr << nouppercase << setw(2) << setfill('0') - << hex << static_cast(m_slave[m_scanIndex]); - - if (m_scanIndex+1 >= m_slave.size()) - m_scan = false; - } - - if (m_scanIndex > 0xFF) - m_scan = false; - else { - m_scanIndex++; - - busCommand += sstr.str(); - busCommand += "070400"; - transform(busCommand.begin(), busCommand.end(), busCommand.begin(), ::tolower); - - L.log(bus, event, " scanning address %s", sstr.str().c_str()); - - - BusMessage* message = new BusMessage(busCommand, true, true); - L.log(bus, trace, " msg: %s", busCommand.c_str()); - - addMessage(message); - } -} diff --git a/src/ebusd/busloop.h b/src/ebusd/busloop.h deleted file mode 100644 index ec18ebc3..00000000 --- a/src/ebusd/busloop.h +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef BUSLOOP_H_ -#define BUSLOOP_H_ - -#include "commands.h" -#include "port.h" -#include "wqueue.h" -#include "thread.h" -#include "symbol.h" -#include "result.h" - -using namespace std; - -/** \file busloop.h */ - -/** the maximum time [us] allowed for retrieving a byte from an addressed slave */ -#define RECV_TIMEOUT 10000 - -/** possible bus command types */ -enum BusCommandType { - invalid, /*!< invalid command type */ - broadcast, /*!< broadcast */ - masterMaster, /*!< master - master */ - masterSlave /*!< master - slave */ -}; - -/** - * @brief class for data/message transfer between baseloop and busloop. - */ -class BusMessage -{ - -public: - /** - * @brief construct a new bus message instance and determine command type. - * @param command the command data to write on bus. - * @param poll true if message type is polling. - * @param scan true if message type is scanning. - */ - BusMessage(const string command, const bool poll, const bool scan); - - /** - * @brief destructor. - */ - ~BusMessage() - { - pthread_mutex_destroy(&m_mutex); - pthread_cond_destroy(&m_cond); - } - - /** - * @brief get the bus command type. - * @return the bus command type. - */ - BusCommandType getType() const { return m_type; } - - /** - * @brief get the command string. - * @return the command string. - */ - SymbolString getCommand() const { return m_command; } - - /** - * @brief get the result string. - * @return the result string. - */ - SymbolString getResult() const { return m_result; } - - /** - * @brief set the result string and result code. - * @param result the result string. - * @param resultCode the result code. - */ - void setResult(const SymbolString result, const int resultCode) - { m_result = result; m_resultCode = resultCode; } - - /** - * @brief return status of result code. - * @return true if result code is negativ. - */ - bool isErrorResult() const { return m_resultCode < 0; } - - /** - * @brief return output string of result code. - * @return the output string of result code. - */ - const char* getResultCodeCStr() const { return getResultCode(m_resultCode); } - - /** - * @brief return the message string or error result string. - * @return the message string or error result string. - */ - const string getMessageStr(); - - /** - * @brief return polling flag of message type. - * @return true if message type is polling. - */ - bool isPoll() const { return m_poll; } - - /** - * @brief return scanning flag of message type. - * @return true if message type is scanning. - */ - bool isScan() const { return m_scan; } - - /** - * @brief wait on notification. - */ - void waitSignal() { pthread_cond_wait(&m_cond, &m_mutex); } // TODO timeout - - /** - * @brief send notification. - */ - void sendSignal() { pthread_cond_signal(&m_cond); } - -private: - /** the bus command type */ - BusCommandType m_type; - - /** true if message is of type polling */ - bool m_poll; - - /** true if message is of type scanning */ - bool m_scan; - - /** the command string (master data) */ - SymbolString m_command; - - /** the result string (slave data) */ - SymbolString m_result; - - /** the result code of result string */ - int m_resultCode; - - /** mutex variable for exclusive lock */ - pthread_mutex_t m_mutex; - - /** condition variable for exclusive lock */ - pthread_cond_t m_cond; - -}; - -/** - * @brief class busloop which handle all bus activities. - */ -class BusLoop : public Thread -{ - -public: - /** - * @brief create a busloop instance and set the commands instance. - * @param commands the commands instance. - */ - BusLoop(Commands* commands); - - /** - * @brief destructor. - */ - ~BusLoop(); - - /** - * @brief endless loop for busloop instance. - * @return void pointer. - */ - void* run(); - - /** - * @brief shut down busloop. - */ - void stop() { m_running = false; } - - /** - * @brief add a new bus message to internal message queue. - * @param message the bus message. - */ - void addMessage(BusMessage* message) { m_busQueue.add(message); } - - /** - * @brief switch to new commands instance. - * @param commands reference of new loaded commands instance. - */ - void reload(Commands* commands) { m_commands = commands; } - - /** - * @brief scanning ebus do determine bus members. - * @param full if true a scan of all slave addresses will be done. - */ - void scan(const bool full=false) { m_scan = true; m_scanFull = full; m_scanIndex = 0; } - - /** - * @brief toggle (on/off) logging of raw data to logging system. - */ - void raw() { m_logRawData == true ? m_logRawData = false : m_logRawData = true ; } - - /** - * @brief set the name of dump file. - * @param dumpFile the file name of dump file. - */ - void setDumpFile(const string& dumpFile) { m_dumpFile = dumpFile; } - - /** - * @brief set the max size of dump file. - * @param dumpSize the max. size of the dump file, before switching. - */ - void setDumpSize(const long dumpSize) { m_dumpSize = dumpSize; } - - /** - * @brief toggle (on/off) dumping of raw bytes to a dump file. - */ - void dump() { m_dumping == true ? m_dumping = false : m_dumping = true ; } - -private: - /** the commands instance */ - Commands* m_commands; - - /** the port instance which control the ebus device */ - Port* m_port; - - /** the name of dump file*/ - string m_dumpFile; - - /** max. size of dump file */ - long m_dumpSize; - - /** true if dumping of raw bytes to file is enabled */ - bool m_dumping; - - /** true if logging of raw bytes is enabled */ - bool m_logRawData; - - /** true if this instance is running */ - bool m_running; - - /** bus access is not allowed if counter is greater than 0 */ - int m_lockCounter; - - /** if true, we lost bus acquire but same priority class. - * after next SYN sign we are allowed to try again to aquire bus. - */ - bool m_priorRetry; - - /** queue for bus messages */ - WQueue m_busQueue; - - /** string for cycle bus data */ - SymbolString m_sstr; - - /** number of send retries for one bus command */ - int m_sendRetries; - - /** number of lock retries (acquire bus) for one bus command */ - int m_lockRetries; - - /** time for receiving answer from slave [us] */ - long m_recvTimeout; - - /** waiting time for bus acquire [us] */ - long m_acquireTime; - - /** time between to polling commands [s] */ - double m_pollInterval; - - /** vector with collected slave addresses */ - vector m_slave; - - /** true if bus scanning for collected slave addresses is active */ - bool m_scan; - - /** true if bus scanning for all slave addresses is active */ - bool m_scanFull; - - /** internal index do get next scan command */ - size_t m_scanIndex; - - /** - * @brief write byte to dump file. - * @param byte to write - * @return -1 if dump file cannot opened or renaming of dump file failed. - */ - int writeDumpFile(const char* byte); - - /** - * @brief fetch next byte of device input buffer (dumping and raw logging). - * @return next byte of device. - */ - unsigned char fetchByte(); - - /** - * @brief collect cycle bytes. the analysis of collected bytes will be triggered after next SYN sign. - * @param numRecv the number of bytes to analyze. - */ - void collectCycData(const int numRecv); - - /** - * @brief the analyzing of collected bytes. collecting of slave address will be triggered. - */ - void analyseCycData(); - - /** - * @brief determine and collect slave addresses. - */ - void collectSlave(); - - /** - * @brief try to acquire bus for sending purpose. - * @return result code of bus acquiring. - */ - int acquireBus(); - - /** - * @brief handle sending of a bus command. - * @return a reference to sent bus message. - */ - BusMessage* sendCommand(); - - /** - * @brief send 1 byte to bus device. - * @param sendByte the byte to send. - * @return result code of byte sending. - */ - int sendByte(const unsigned char sendByte); - - /** - * @brief receive ACK from slave. - * @param reference for receive byte. - * @return result code of receiving byte. - */ - int recvSlaveAck(unsigned char& recvByte); - - /** - * @brief receive slave data block. - * @param reference for result string. - * @return result code of receiving slave data. - */ - int recvSlaveData(SymbolString& result); - - /** - * @brief add a polling bus message to internal message queue. - * @param message the bus message. - */ - void addPollMessage(); - - /** - * @brief add a scanning bus message to internal message queue. - * @param message the bus message. - */ - void addScanMessage(); - -}; - -#endif // BUSLOOP_H_ diff --git a/src/ebusd/ebusd.cpp b/src/ebusd/ebusd.cpp index fbc85345..61749241 100644 --- a/src/ebusd/ebusd.cpp +++ b/src/ebusd/ebusd.cpp @@ -43,9 +43,12 @@ void define_args() A.addText("Options:\n"); - A.addOption("address", "a", OptVal("FF"), dt_string, ot_mandatory, + A.addOption("address", "a", OptVal(0xff), dt_hex, ot_mandatory, "\tebus device address (FF)"); + A.addOption("answer", "", OptVal(false), dt_bool, ot_none, + "\tanswers to requests from other devices"); + A.addOption("device", "d", OptVal("/dev/ttyUSB0"), dt_string, ot_mandatory, "\tebus device (serial or network) (/dev/ttyUSB0)"); @@ -64,8 +67,8 @@ void define_args() A.addOption("recvtimeout", "", OptVal(15000), dt_long, ot_mandatory, "receive timeout in 'us' (15000)"); - A.addOption("acquiretime", "", OptVal(4200), dt_long, ot_mandatory, - "waiting time for bus acquire in 'us' (4200)\n"); + A.addOption("acquiretimeout", "", OptVal(9400), dt_long, ot_mandatory, + "bus acquisition timeout in 'us' (9400)\n"); A.addOption("pollinterval", "", OptVal(5), dt_int, ot_mandatory, "polling interval in 's' (5)\n"); diff --git a/src/ebusd/network.cpp b/src/ebusd/network.cpp index 67b86462..4b36ad78 100644 --- a/src/ebusd/network.cpp +++ b/src/ebusd/network.cpp @@ -37,10 +37,8 @@ extern Appl& A; int Connection::m_ids = 0; -void* Connection::run() +void Connection::run() { - m_running = true; - int ret; struct timespec tdiff; @@ -142,15 +140,12 @@ void* Connection::run() } delete m_socket; - m_running = false; L.log(net, trace, "[%05d] connection closed", getID()); - - return NULL; } Network::Network(const bool local, WQueue* netQueue) - : m_netQueue(netQueue), m_listening(false), m_running(false) + : m_netQueue(netQueue), m_listening(false) { if (local == true) m_tcpServer = new TCPServer(A.getOptVal("port"), "127.0.0.1"); @@ -172,18 +167,16 @@ Network::~Network() delete connection; } - if (m_running == true) - stop(); + stop(); + join(); delete m_tcpServer; } -void* Network::run() +void Network::run() { if (m_listening == false) - return NULL; - - m_running = true; + return; int ret; struct timespec tdiff; @@ -239,8 +232,7 @@ void* Network::run() #ifdef HAVE_PPOLL // new data from notify if (fds[0].revents & POLLIN) { - m_running = false; - break; + return; } // new data from socket @@ -249,8 +241,7 @@ void* Network::run() #ifdef HAVE_PSELECT // new data from notify if (FD_ISSET(m_notify.notifyFD(), &readfds)) { - m_running = false; - break; + return; } // new data from socket @@ -272,8 +263,6 @@ void* Network::run() } } - - return NULL; } void Network::cleanConnections() diff --git a/src/ebusd/network.h b/src/ebusd/network.h index 35f6e47f..9a04b031 100644 --- a/src/ebusd/network.h +++ b/src/ebusd/network.h @@ -132,25 +132,18 @@ public: * @param netQueue the remote queue for network messages. */ Connection(TCPSocket* socket, WQueue* netQueue) - : m_socket(socket), m_netQueue(netQueue), m_running(false) + : m_socket(socket), m_netQueue(netQueue) { m_id = ++m_ids; } /** * @brief endless loop for connection instance. - * @return void pointer. */ - void* run(); + virtual void run(); /** * @brief close active connection. */ - void stop() const { m_notify.notify(); } - - /** - * @brief status of connection instance. - * @return true if connection is running. - */ - bool isRunning() const { return m_running; } + virtual void stop() { m_notify.notify(); Thread::stop(); } /** * @brief return own connection id. @@ -168,9 +161,6 @@ private: /** notification object for shutdown procedure */ Notify m_notify; - /** true if this instance is running */ - bool m_running; - /** id of current connection*/ int m_id; @@ -200,9 +190,8 @@ public: /** * @brief endless loop for network instance. - * @return void pointer. */ - void* run(); + virtual void run(); /** * @brief shutdown network subsystem. @@ -225,9 +214,6 @@ private: /** true if this instance is listening */ bool m_listening; - /** true if this instance is running */ - bool m_running; - /** * @brief clean inactive connections from container. */ diff --git a/src/lib/ebus/Makefile.am b/src/lib/ebus/Makefile.am old mode 100644 new mode 100755 index 570375bf..a0c1c0e2 --- a/src/lib/ebus/Makefile.am +++ b/src/lib/ebus/Makefile.am @@ -1,6 +1,7 @@ AM_CXXFLAGS = -fpic \ -Wall \ - -Wextra + -Wextra \ + -I$(top_srcdir)/src/lib/utils noinst_LIBRARIES = libebus.a @@ -12,16 +13,8 @@ libebus_a_SOURCES = result.cpp \ data.h \ port.cpp \ port.h \ - command.cpp \ - command.h \ - commands.cpp \ - commands.h \ - configfile.cpp \ - configfile.h \ - decode.cpp \ - decode.h \ - encode.cpp \ - encode.h + message.cpp \ + message.h distclean-local: -rm -f Makefile.in diff --git a/src/lib/ebus/command.cpp b/src/lib/ebus/command.cpp deleted file mode 100644 index 37fefaf4..00000000 --- a/src/lib/ebus/command.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "command.h" -#include "decode.h" -#include "encode.h" -#include -#include -#include -#include -#include -#include - -string Command::calcData() -{ - // encode - only first entry will be encoded - // ToDo: if more parts are needed, they will be implemented - encode(m_data, m_command[13], m_command[14]); - - if (m_error.length() > 0) - m_result = m_error; - - return m_result; -} - -string Command::calcResult(const cmd_t& cmd) -{ - int elements = strtol(m_command[9].c_str(), NULL, 10); - - if (cmd.size() > 3) { - bool found = false; - - for (size_t i = 3; i < cmd.size(); i++) { - int j; - - for (j = 0; j < elements; j++) { - if (m_command[10 + j*8] == cmd[i]) { - found = true; - break; - } - } - - if (found == true) { - found = false; - - // decode - calcSub(m_command[11 + j*8], m_command[12 + j*8], - m_command[13 + j*8], m_command[14 + j*8]); - } - - } - - } else { - for (int j = 0; j < elements; j++) { - - // decode - calcSub(m_command[11 + j*8], m_command[12 + j*8], - m_command[13 + j*8], m_command[14 + j*8]); - } - } - - if (m_error.length() > 0) - m_result = m_error; - - return m_result; -} - -void Command::calcSub(const string& part, const string& position, - const string& type, const string& factor) -{ - string data; - - // Master Data - if (strcasecmp(part.c_str(), "MD") == 0) { - // QQ ZZ PB SB NN - int md_pos = 10; - int md_len = strtol(m_command[7].c_str(), NULL, 10)*2; - data = m_data.substr(md_pos, md_len); - } - - // Slave Acknowledge - else if (strcasecmp(part.c_str(), "SA") == 0) { - // QQ ZZ PB SB NN + Dx + CRC - int sa_pos = 10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 2; - int sa_len = 2; - data = m_data.substr(sa_pos, sa_len); - } - - // Slave Data - else if (strcasecmp(part.c_str(), "SD") == 0) { - // QQ ZZ PB SB NN + Dx + CRC ACK NN - int sd_pos = 10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 6; - int sd_len = m_data.length() - (10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 6) - 4; - data = m_data.substr(sd_pos, sd_len); - } - - // Master Acknowledge - else if (strcasecmp(part.c_str(), "MA") == 0) { - // QQ ZZ PB SB NN + Dx + CRC ACK NN + Dx - int ma_pos = m_data.length() - 2; - int ma_len = 2; - data = m_data.substr(ma_pos, ma_len); - } - - decode(data, position, type, factor); -} - -void Command::decode(const string& data, const string& position, - const string& type, const string& factor) -{ - ostringstream result, value; - Decode* help = NULL; - - // prepare position - string token; - istringstream stream(position); - vector pos; - - while (getline(stream, token, ',') != 0) - pos.push_back(strtol(token.c_str(), NULL, 10)); - - if (strcasecmp(type.c_str(), "HEX") == 0) { - if (pos.size() <= 1 || pos[1] < pos[0]) - pos[1] = pos[0]; - - value << data.substr((pos[0]-1)*2, (pos[1]-pos[0]+1)*2); - help = new DecodeHEX(value.str()); - } - else if (strcasecmp(type.c_str(), "UCH") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeUCH(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "SCH") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeSCH(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "UIN") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); - help = new DecodeUIN(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "SIN") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); - help = new DecodeSIN(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "ULG") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2) << data.substr((pos[3]-1)*2, 2); - help = new DecodeULG(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "SLG") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2) << data.substr((pos[3]-1)*2, 2); - help = new DecodeSLG(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "FLT") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); - help = new DecodeFLT(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "STR") == 0) { - if (pos.size() <= 1 || pos[1] < pos[0]) - pos[1] = pos[0]; - - value << data.substr((pos[0]-1)*2, (pos[1]-pos[0]+1)*2); - help = new DecodeSTR(value.str()); - } - else if (strcasecmp(type.c_str(), "BCD") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeBCD(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "D1B") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeD1B(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "D1C") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeD1C(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "D2B") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); - help = new DecodeD2B(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "D2C") == 0) { - value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); - help = new DecodeD2C(value.str(), factor); - } - else if (strcasecmp(type.c_str(), "BDA") == 0) { - value << data.substr((pos[0]-1)*2, 2) - << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2); - help = new DecodeBDA(value.str()); - } - else if (strcasecmp(type.c_str(), "HDA") == 0) { - value << data.substr((pos[0]-1)*2, 2) - << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2); - help = new DecodeHDA(value.str()); - } - else if (strcasecmp(type.c_str(), "BTI") == 0) { - value << data.substr((pos[0]-1)*2, 2) - << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2); - help = new DecodeBTI(value.str()); - } - else if (strcasecmp(type.c_str(), "HTI") == 0) { - value << data.substr((pos[0]-1)*2, 2) - << data.substr((pos[1]-1)*2, 2) - << data.substr((pos[2]-1)*2, 2); - help = new DecodeHTI(value.str()); - } - else if (strcasecmp(type.c_str(), "BDY") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeBDY(value.str()); - } - else if (strcasecmp(type.c_str(), "HDY") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeHDY(value.str()); - } - else if (strcasecmp(type.c_str(), "TTM") == 0) { - value << data.substr((pos[0]-1)*2, 2); - help = new DecodeTTM(value.str()); - } - - if (help == NULL) { - result << "type '" << type.c_str() << "' not implemented!"; - m_error = result.str(); - } else { - result << help->decode(); - - if (m_result.length() > 0) - m_result += " "; - - m_result += result.str(); - } - - delete help; -} - -void Command::encode(const string& data, const string& type, - const string& factor) -{ - ostringstream result; - Encode* help = NULL; - - if (strcasecmp(type.c_str(), "HEX") == 0) { - help = new EncodeHEX(data); - } - else if (strcasecmp(type.c_str(), "UCH") == 0) { - help = new EncodeUCH(data, factor); - } - else if (strcasecmp(type.c_str(), "SCH") == 0) { - help = new EncodeSCH(data, factor); - } - else if (strcasecmp(type.c_str(), "UIN") == 0) { - help = new EncodeUIN(data, factor); - } - else if (strcasecmp(type.c_str(), "SIN") == 0) { - help = new EncodeSIN(data, factor); - } - else if (strcasecmp(type.c_str(), "ULG") == 0) { - help = new EncodeULG(data, factor); - } - else if (strcasecmp(type.c_str(), "SLG") == 0) { - help = new EncodeSLG(data, factor); - } - else if (strcasecmp(type.c_str(), "FLT") == 0) { - help = new EncodeSLG(data, factor); - } - else if (strcasecmp(type.c_str(), "STR") == 0) { - help = new EncodeSTR(data); - } - else if (strcasecmp(type.c_str(), "BCD") == 0) { - help = new EncodeBCD(data, factor); - } - else if (strcasecmp(type.c_str(), "D1B") == 0) { - help = new EncodeD1B(data, factor); - } - else if (strcasecmp(type.c_str(), "D1C") == 0) { - help = new EncodeD1C(data, factor); - } - else if (strcasecmp(type.c_str(), "D2B") == 0) { - help = new EncodeD2B(data, factor); - } - else if (strcasecmp(type.c_str(), "D2C") == 0) { - help = new EncodeD2C(data, factor); - } - else if (strcasecmp(type.c_str(), "BDA") == 0) { - help = new EncodeBDA(data); - } - else if (strcasecmp(type.c_str(), "HDA") == 0) { - help = new EncodeHDA(data); - } - else if (strcasecmp(type.c_str(), "BTI") == 0) { - help = new EncodeBTI(data); - } - else if (strcasecmp(type.c_str(), "HTI") == 0) { - help = new EncodeHTI(data); - } - else if (strcasecmp(type.c_str(), "BDY") == 0) { - help = new EncodeBDY(data); - } - else if (strcasecmp(type.c_str(), "HDY") == 0) { - help = new EncodeHDY(data); - } - else if (strcasecmp(type.c_str(), "TTM") == 0) { - help = new EncodeTTM(data); - } - - if (help == NULL) { - result << "type '" << type.c_str() << "' not implemented!"; - m_error = result.str(); - } else { - result << help->encode(); - - if (m_result.length() > 0) - m_result += " "; - - m_result += result.str(); - } - - delete help; -} - diff --git a/src/lib/ebus/command.h b/src/lib/ebus/command.h deleted file mode 100644 index b90a374a..00000000 --- a/src/lib/ebus/command.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef LIBEBUS_COMMAND_H_ -#define LIBEBUS_COMMAND_H_ - -#include -#include - -using namespace std; - -typedef vector cmd_t; -typedef cmd_t::const_iterator cmdCI_t; - -class Command -{ - -public: - Command(int index, cmd_t command) : m_index(index), m_command(command) {} - Command(int index, cmd_t command, string data) - : m_index(index), m_command(command), m_data(data) {} - - cmd_t getCommand() const { return m_command; } - void setData(const string& data) { m_data = data; } - string getData() const { return m_data; } - string calcData(); - - string calcResult(const cmd_t& cmd); - -private: - int m_index; - cmd_t m_command; - string m_data; - string m_result; - string m_error; - - void calcSub(const string& part, const string& position, - const string& type, const string& factor); - - void decode(const string& data, const string& position, - const string& type, const string& factor); - - void encode(const string& data, const string& type, - const string& factor); - -}; - -#endif // LIBEBUS_COMMAND_H_ diff --git a/src/lib/ebus/commands.cpp b/src/lib/ebus/commands.cpp deleted file mode 100644 index 3f781799..00000000 --- a/src/lib/ebus/commands.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "commands.h" -#include -#include -#include -#include -#include -#include - -using namespace std; - -Commands::~Commands() -{ - for (mapCI_t iter = m_pollDB.begin(); iter != m_pollDB.end(); ++iter) - delete iter->second; - - m_pollDB.clear(); - - for (mapCI_t iter = m_cycDB.begin(); iter != m_cycDB.end(); ++iter) - delete iter->second; - - m_cycDB.clear(); - - m_cmdDB.clear(); -} - -void Commands::addCommand(const cmd_t& command) -{ - m_cmdDB.push_back(command); - - if (strcasecmp(command[0].c_str(),"C") == 0) { - Command* cmd = new Command(m_cmdDB.size()-1, command); - m_cycDB.insert(pair_t(m_cmdDB.size()-1, cmd)); - } - - if (strcasecmp(command[0].c_str(),"P") == 0) { - Command* cmd = new Command(m_cmdDB.size()-1, command); - m_pollDB.insert(pair_t(m_cmdDB.size()-1, cmd)); - } -} - -void Commands::printCommands() const -{ - if (m_cmdDB.size() == 0) - return; - - for (cmdDBCI_t i = m_cmdDB.begin(); i != m_cmdDB.end(); i++) { - printCommand(*i); - cout << endl; - } -} - -int Commands::findCommand(const string& data) const -{ - // no commands definend - if (m_cmdDB.size() == 0) - return -2; - - // preapre string for searching command - string token; - istringstream isstr(data); - vector cmd; - - // split stream - while (getline(isstr, token, ' ') != 0) - cmd.push_back(token); - - size_t index; - cmdDBCI_t i = m_cmdDB.begin(); - - // walk through commands - GET - if (strcasecmp(cmd[0].c_str(), "GET") == 0) { - for (index = 0; i != m_cmdDB.end(); i++, index++) { - - // empty line - if ((*i).size() == 0) - continue; - - if (((strcasecmp((*i)[0].c_str(), "R") == 0) - || (strcasecmp((*i)[0].c_str(), "P") == 0)) - && (strcasecmp((*i)[1].c_str(), cmd[1].c_str()) == 0) - && (strcasecmp((*i)[2].c_str(), cmd[2].c_str()) == 0)) - return index; - } - // walk through commands - SET, CYC - } else { - // correct type - if (strcasecmp(cmd[0].c_str(), "SET") == 0) - cmd[0] = "W"; - else if (strcasecmp(cmd[0].c_str(), "CYC") == 0) - cmd[0] = "C"; - - for (index = 0; i != m_cmdDB.end(); i++, index++) { - - // empty line - if ((*i).size() == 0) - continue; - - if (strcasecmp((*i)[0].c_str(), cmd[0].c_str()) == 0 && - strcasecmp((*i)[1].c_str(), cmd[1].c_str()) == 0 && - strcasecmp((*i)[2].c_str(), cmd[2].c_str()) == 0) - return index; - } - } - - // command not found - return -1; -} - -string Commands::getBusCommand(const int index) const -{ - cmd_t command = m_cmdDB.at(index); - string cmd; - stringstream sstr; - - if (strcasecmp(command[0].c_str(), "C") == 0) - cmd += command[4]; // QQ - - cmd += command[5]; // ZZ - cmd += command[6]; // PBSB - sstr << setw(2) << hex << setfill('0') << command[7]; - cmd += sstr.str(); // NN - cmd += command[8]; // Dx - - return cmd; -} - -int Commands::storeCycData(const string& data) const -{ - // no commands defined - if (m_cycDB.size() == 0) - return -2; - - // search skipped - string too short - if (data.length() < 10) - return -3; - - // prepare string for searching command - string search(data.substr(2, 8 + strtol(data.substr(8,2).c_str(), NULL, 16) * 2)); - - mapCI_t iter = m_cycDB.begin(); - - // walk through commands - for (; iter != m_cycDB.end(); iter++) { - - string command = getBusCommand(iter->first); - - // skip wrong search string length - if (command.length() > search.length()) - continue; - - if (strcasecmp(command.c_str(), search.substr(0,command.length()).c_str()) == 0) { - iter->second->setData(data); - return iter->first; - } - } - - // command not found - return -1; -} - -string Commands::getCycData(int index) const -{ - mapCI_t iter = m_cycDB.find(index); - if (iter != m_cycDB.end()) - return iter->second->getData(); - else - return ""; -} - -int Commands::nextPollCommand() -{ - size_t index = 0; - - m_pollIndex++; - - if (m_pollIndex == m_pollDB.size()) - m_pollIndex = 0; - - mapCI_t iter = m_pollDB.begin(); - - for (; iter != m_pollDB.end(); iter++, index++) - if (index == m_pollIndex) - return iter->first; - - return -1; -} - -void Commands::storePollData(const string& data) const -{ - // prepare string for searching command - string search(data.substr(2, 8 + strtol(data.substr(8,2).c_str(), NULL, 16) * 2)); - - mapCI_t iter = m_pollDB.begin(); - - // walk through commands - for (; iter != m_pollDB.end(); iter++) { - - string command = getBusCommand(iter->first); - - // skip wrong search string length - if (command.length() > search.length()) - continue; - - if (strcasecmp(command.c_str(), search.substr(0,command.length()).c_str()) == 0) - iter->second->setData(data); - - } -} - -string Commands::getPollData(const int index) const -{ - mapCI_t iter = m_pollDB.find(index); - if (iter != m_pollDB.end()) - return iter->second->getData(); - else - return ""; -} - -void Commands::storeScanData(const string& data) -{ - vector::const_iterator iter = m_scanDB.begin(); - bool found = false; - - // walk through scan data - for (; iter != m_scanDB.end(); iter++) - if (data == (*iter)) - found = true; - - if (found == false) - m_scanDB.push_back(data); -} - -void Commands::printCommand(const cmd_t& command) const -{ - if (command.size() == 0) - return; - - for (cmdCI_t i = command.begin(); i != command.end(); i++) - cout << *i << ';'; -} - diff --git a/src/lib/ebus/commands.h b/src/lib/ebus/commands.h deleted file mode 100644 index a696a955..00000000 --- a/src/lib/ebus/commands.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef LIBEBUS_COMMANDS_H_ -#define LIBEBUS_COMMANDS_H_ - -#include "command.h" -#include -#include -#include - -using namespace std; - -typedef vector cmdDB_t; -typedef cmdDB_t::const_iterator cmdDBCI_t; - -typedef map map_t; -typedef map_t::const_iterator mapCI_t; -typedef pair pair_t; - -class Commands -{ - -public: - Commands() : m_pollIndex(-1) {} - ~Commands(); - - void addCommand(const cmd_t& command); - void printCommands() const; - - size_t sizeCmdDB() const { return m_cmdDB.size(); } - size_t sizeCycDB() const { return m_cycDB.size(); } - size_t sizePollDB() const { return m_pollDB.size(); } - size_t sizeScanDB() const { return m_scanDB.size(); } - - cmd_t const& operator[](const size_t& index) const { return m_cmdDB[index]; } - - int findCommand(const string& data) const; - - string getCmdType(const int index) const { return string(m_cmdDB.at(index)[0]); } - string getBusCommand(const int index) const; - - int storeCycData(const string& data) const; - string getCycData(int index) const; - - int nextPollCommand(); - void storePollData(const string& data) const; - string getPollData(const int index) const; - - void storeScanData(const string& data); - string getScanData(const int index) const { return m_scanDB[index]; } - -private: - cmdDB_t m_cmdDB; - map_t m_cycDB; - map_t m_pollDB; - size_t m_pollIndex; - vector m_scanDB; - - void printCommand(const cmd_t& command) const; - -}; - -#endif // LIBEBUS_COMMANDS_H_ - diff --git a/src/lib/ebus/configfile.cpp b/src/lib/ebus/configfile.cpp deleted file mode 100644 index 11019af0..00000000 --- a/src/lib/ebus/configfile.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "configfile.h" -#include -#include -#include - -using namespace std; - -void ConfigFileCSV::parse(istream& is, Commands& commands) -{ - string line; - - // read lines - while (getline(is, line) != 0) { - cmd_t row; - string column; - - istringstream isstr(line); - - // walk through columns - while (getline(isstr, column, ';') != 0) - row.push_back(column); - - // skip empty and commented rows - if (row.empty() == true || row[0][0] == '#') - continue; - - commands.addCommand(row); - } -}; - - -ConfigCommands::ConfigCommands(const string path, const FileType type) -{ - m_path = path; - m_configfile = NULL; - setType(type); - addFiles(m_path, m_extension); -} - -void ConfigCommands::setType(const FileType type) -{ - if (m_configfile != NULL) - delete m_configfile; - - switch (type) { - case ft_csv: - m_configfile = new ConfigFileCSV(); - m_extension = "csv"; - break; - }; -}; - -Commands* ConfigCommands::getCommands() -{ - Commands* commands = new Commands(); - vector::const_iterator i = m_files.begin(); - - for(; i != m_files.end(); i++) { - fstream file((*i).c_str(), ios::in); - - if(file.is_open() == true) { - m_configfile->parse(file, *commands); - file.close(); - } - } - return commands; -}; - -void ConfigCommands::addFiles(const string path, const string extension) -{ - DIR* dir = opendir(path.c_str()); - - if (dir == NULL) - return; - - dirent* d = readdir(dir); - - while (d != NULL) { - - if (d->d_type == DT_DIR) { - string fn = d->d_name; - - if (fn != "." && fn != "..") { - const string p = path + "/" + d->d_name; - addFiles(p, extension); - } - - } else if (d->d_type == DT_REG) { - string fn = d->d_name; - - if (fn.find(extension, (fn.length() - extension.length())) != string::npos) { - const string p = path + "/" + d->d_name; - m_files.push_back(p); - } - } - - d = readdir(dir); - } - - closedir(dir); -}; - diff --git a/src/lib/ebus/configfile.h b/src/lib/ebus/configfile.h deleted file mode 100644 index f6f37563..00000000 --- a/src/lib/ebus/configfile.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef LIBEBUS_CONFIGFILE_H_ -#define LIBEBUS_CONFIGFILE_H_ - -#include "commands.h" -#include -#include - -using namespace std; - -/** \file configfile.h */ - -/** available file endings / types. */ -enum FileType { - ft_csv /*!< CSV */ -}; - -/** - * @brief base class for config files. - */ -class ConfigFile -{ - -public: - /** - * @brief destructor. - */ - virtual ~ConfigFile() {} - - /** - * @brief read input stream and stored data into commands - * @param is open input stream for reading. - * @param commands object as datastore. - */ - virtual void parse(istream& is, Commands& commands) = 0; - -}; - -/** - * @brief derived class for CSV config files. - */ -class ConfigFileCSV : public ConfigFile -{ - -public: - /** - * @brief destructor. - */ - ~ConfigFileCSV() {} - - /** - * @brief read input stream and stored data into commands - * @param is open input stream for reading. - * @param commands object as datastore. - */ - void parse(istream& is, Commands& commands); - -}; - -/** - * @brief class to parse configuration files and store into commands instance. - */ -class ConfigCommands -{ - -public: - /** - * @brief set file type and add recursive files from given path. - * @param path to configuration files. - * @param type to parse. - */ - ConfigCommands(const string path, const FileType type); - - /** - * @brief destructor. - */ - ~ConfigCommands() { delete m_configfile; } - - /** - * @brief setter for file type. - * @param type of files. - */ - void setType(const FileType type); - - /** - * @brief parse files for commands and store them into commands instance. - * @return a commands instance - */ - Commands* getCommands(); - -private: - /** the configfile instance */ - ConfigFile* m_configfile; - - /** main path for configuration files */ - string m_path; - - /** valid file extension */ - string m_extension; - - /** vector of configuration files */ - vector m_files; - - /** - * @brief parse path for given file extension. - * @param path to configuration files. - * @param extension with file type. - */ - void addFiles(const string path, const string extension); - -}; - -#endif // LIBEBUS_CONFIGFILE_H_ - diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 7a845899..8f619b72 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -38,6 +38,8 @@ static const dataType_t dataTypes[] = { {"HDA", 32, bt_dat, 0, 0, 10, 10, 0, 0}, // date with weekday, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is weekday Mon=0x01 - Sun=0x07)) {"HDA", 24, bt_dat, 0, 0, 10, 10, 0, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) // TODO remove duplicate of BDA {"BTI", 24, bt_tim, BCD|REV, 0, 8, 8, 0, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) + {"HTI", 24, bt_tim, 0, 0, 8, 8, 0, 0}, // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x17,0x3b,0x3b) + {"VTI", 24, bt_tim, REV, 0x63, 8, 8, 0, 0}, // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x3b,0x3b,0x17, replacement 0x63) [Vaillant type] {"HTM", 16, bt_tim, 0, 0, 5, 5, 0, 0}, // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x17,0x3b) {"TTM", 8, bt_tim, 0, 0x90, 5, 5, 0, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) {"BDY", 8, bt_num, DAY|LST, 0x07, 0, 6, 1, 0}, // weekday, "Mon" - "Sun" (0x00 - 0x06) [ebus type] @@ -68,7 +70,6 @@ static const dataType_t dataTypes[] = { /** the week day names. */ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; -#define FIELD_SEPARATOR ';' #define VALUE_SEPARATOR ',' #define LENGTH_SEPARATOR ':' #define NULL_VALUE "-" @@ -80,12 +81,12 @@ unsigned int parseInt(const char* str, int base, const unsigned int minValue, co unsigned int ret = strtoul(str, &strEnd, base); if (strEnd == NULL || *strEnd != 0) { - result = RESULT_ERR_INVALID_ARG; // invalid value + result = RESULT_ERR_INVALID_NUM; // invalid value return 0; } if (ret < minValue || ret > maxValue) { - result = RESULT_ERR_INVALID_ARG; // invalid value + result = RESULT_ERR_OUT_OF_RANGE; // invalid value return 0; } if (length != NULL) @@ -95,23 +96,50 @@ unsigned int parseInt(const char* str, int base, const unsigned int minValue, co return ret; } +void printErrorPos(vector::iterator begin, const vector::iterator end, vector::iterator pos, char separator) +{ + cout << "Erroneous item is here:" << endl; + bool first = true; + int cnt = 0; + if (pos > begin) + pos--; + while (begin != end) { + if (first == true) + first = false; + else { + cout << separator; + if (begin <= pos) { + cnt++; + } + } + if (begin < pos) { + cnt += (*begin).length(); + } + cout << (*begin++); + } + cout << endl; + cout << setw(cnt) << " " << setw(0) << "^" << endl; +} + + result_t DataField::create(vector::iterator& it, const vector::iterator end, - const map< string, DataField*> templates, + DataFieldTemplates* templates, DataField*& returnField, const bool isSetMessage, const unsigned char dstAddress) { vector fields; string firstName, firstComment; result_t result = RESULT_OK; - while (it != end && result == RESULT_OK) { + if (it == end) + return RESULT_ERR_EOF; + + do { string unit, comment; PartType partType; unsigned int divisor = 0; const bool isTemplate = dstAddress == SYN; string token; - if (it == end) - break; // name;part;type[:len][;[divisor|values][;[unit][;[comment]]]] const string name = *it++; @@ -126,10 +154,7 @@ result_t DataField::create(vector::iterator& it, firstName = name; firstComment = comment; } - if (isTemplate == false && strcasecmp(partStr, "I") == 0) { - partType = pt_masterDataID; - } - else if (dstAddress == BROADCAST || isMaster(dstAddress) + if (dstAddress == BROADCAST || isMaster(dstAddress) == true || (isTemplate == false && isSetMessage == true && partStr[0] == 0) || strcasecmp(partStr, "M") == 0) { // master data partType = pt_masterData; @@ -142,14 +167,14 @@ result_t DataField::create(vector::iterator& it, partType = pt_any; } else { - result = RESULT_ERR_INVALID_ARG; + result = RESULT_ERR_INVALID_PART; break; } string typeStr = *it++; if (typeStr.empty() == true) { if (name.empty() == false || partStr[0] != 0) - result = RESULT_ERR_INVALID_ARG; + result = RESULT_ERR_MISSING_TYPE; break; } @@ -157,11 +182,8 @@ result_t DataField::create(vector::iterator& it, if (it != end) { string divisorStr = *it++; if (divisorStr.empty() == false) { - if (divisorStr.find('=') == string::npos) { + if (divisorStr.find('=') == string::npos) divisor = parseInt(divisorStr.c_str(), 10, 1, 10000, result); - if (result != RESULT_OK) - break; - } else { istringstream stream(divisorStr); while (getline(stream, token, VALUE_SEPARATOR) != 0) { @@ -169,15 +191,15 @@ result_t DataField::create(vector::iterator& it, char* strEnd = NULL; unsigned int id = strtoul(str, &strEnd, 10); if (strEnd == NULL || strEnd == str || *strEnd != '=') { - result = RESULT_ERR_INVALID_ARG; + result = RESULT_ERR_INVALID_LIST; break; } values[id] = string(strEnd + 1); } - if (result != RESULT_OK) - break; } + if (result != RESULT_OK) + break; } } @@ -202,22 +224,21 @@ result_t DataField::create(vector::iterator& it, if (pos == string::npos) { length = 0; // check for reference(s) to templates - if (templates.empty() == false) { + if (templates != NULL) { istringstream stream(typeStr); bool found = false; string lengthStr; - while (getline(stream, token, VALUE_SEPARATOR) != 0) { - map::const_iterator ref = templates.find(token); - if (ref == templates.end()) { + while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR) != 0) { + DataField* templ = templates->get(token); + if (templ == NULL) { if (found == false) break; // fallback to direct definition - result = RESULT_ERR_INVALID_ARG; // cannot mix reference and direct definition - break; + result = RESULT_ERR_NOTFOUND; // cannot mix reference and direct definition + } + else { + found = true; + result = templ->derive("", "", "", partType, divisor, values, fields); } - found = true; - result = ref->second->derive(name, comment, unit, partType, divisor, values, fields); - if (result != RESULT_OK) - break; } if (result != RESULT_OK) break; @@ -246,7 +267,7 @@ result_t DataField::create(vector::iterator& it, bitCount = 1; // default count: 1 bit } else if (length > bitCount) { - result = RESULT_ERR_INVALID_ARG; // invalid length + result = RESULT_ERR_OUT_OF_RANGE; // invalid length break; } else { @@ -261,7 +282,7 @@ result_t DataField::create(vector::iterator& it, useLength = length; } else { - result = RESULT_ERR_INVALID_ARG; // invalid length + result = RESULT_ERR_OUT_OF_RANGE; // invalid length break; } } @@ -293,7 +314,7 @@ result_t DataField::create(vector::iterator& it, } if (values.begin()->first < dataType.minValueOrLength || values.rbegin()->first > dataType.maxValueOrLength) { - result = RESULT_ERR_INVALID_ARG; + result = RESULT_ERR_OUT_OF_RANGE; break; } @@ -305,14 +326,16 @@ result_t DataField::create(vector::iterator& it, if (add != NULL) fields.push_back(add); else if (result == RESULT_OK) - result = RESULT_ERR_INVALID_ARG; // type not found - } + result = RESULT_ERR_NOTFOUND; // type not found + + } while (it != end && result == RESULT_OK); + if (fields.empty() == true || result != RESULT_OK) { - while (fields.empty() == false) { + while (fields.empty() == false) { // cleanup already created fields delete fields.back(); fields.pop_back(); } - return result == RESULT_OK ? RESULT_ERR_INVALID_ARG :result; + return result == RESULT_OK ? RESULT_ERR_INVALID_ARG : result; } if (fields.size() == 1) @@ -324,37 +347,49 @@ result_t DataField::create(vector::iterator& it, } -result_t SingleDataField::read(SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - ostringstream& output, +void SingleDataField::dump(ostream& output) +{ + output << m_name << FIELD_SEPARATOR; + if (m_partType == pt_masterData) + output << "m"; + else if (m_partType == pt_slaveData) + output << "s"; + output << FIELD_SEPARATOR << m_dataType.name; +} + +result_t SingleDataField::read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, bool leadingSeparator, bool verbose, char separator) { - SymbolString& input = m_partType != pt_slaveData ? masterData : slaveData; - unsigned char offset; + if (partType != m_partType) + return RESULT_OK; + switch (m_partType) { case pt_masterData: - case pt_masterDataID: - offset = 5 + masterOffset; // skip QQ ZZ PB SB NN + offset += 5; // skip QQ ZZ PB SB NN break; case pt_slaveData: - offset = 1 + slaveOffset; // skip NN + offset += 1; // skip NN break; default: - return RESULT_ERR_INVALID_ARG; // invalid part type + return RESULT_ERR_INVALID_PART; } - if (isIgnored() == true) { - if (offset + m_length > input.size()) { - return RESULT_ERR_INVALID_ARG; + if (offset + m_length > data.size()) { + return RESULT_ERR_INVALID_POS; } return RESULT_OK; } + if (leadingSeparator == true) + output << separator; + if (verbose == true) output << m_name << "="; - result_t result = readSymbols(input, offset, output); + result_t result = readSymbols(data, offset, output); if (result != RESULT_OK) return result; @@ -367,25 +402,24 @@ result_t SingleDataField::read(SymbolString& masterData, unsigned char masterOff } result_t SingleDataField::write(istringstream& input, - SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - char separator) + const PartType partType, SymbolString& data, + unsigned char offset, char separator) { - SymbolString& output = m_partType != pt_slaveData ? masterData : slaveData; - unsigned char offset; + if (partType != m_partType) + return RESULT_OK; + switch (m_partType) { case pt_masterData: - case pt_masterDataID: - offset = 5 + masterOffset; // skip QQ ZZ PB SB NN + offset += 5; // skip QQ ZZ PB SB NN break; case pt_slaveData: - offset = 1 + slaveOffset; // skip NN + offset += 1; // skip NN break; default: - return RESULT_ERR_INVALID_ARG; + return RESULT_ERR_INVALID_PART; } - return writeSymbols(input, offset, output); + return writeSymbols(input, offset, data); } @@ -395,7 +429,7 @@ result_t StringDataField::derive(string name, string comment, vector& fields) { if (m_partType != pt_any && partType == pt_any) - return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance if (divisor != 0 || values.empty() == false) return RESULT_ERR_INVALID_ARG; // cannot set divisor or values for string field if (name.empty() == true) @@ -410,6 +444,15 @@ result_t StringDataField::derive(string name, string comment, return RESULT_OK; } +void StringDataField::dump(ostream& output) +{ + SingleDataField::dump(output); + if ((m_dataType.flags & ADJ) != 0) + output << ":" << static_cast(m_length); + output << FIELD_SEPARATOR << FIELD_SEPARATOR; // no value list, no divisor + output << m_unit << FIELD_SEPARATOR << m_comment << FIELD_SEPARATOR; +} + result_t StringDataField::readSymbols(SymbolString& input, unsigned char baseOffset, ostringstream& output) { @@ -418,7 +461,7 @@ result_t StringDataField::readSymbols(SymbolString& input, unsigned char ch, last = 0; if (baseOffset + m_length > input.size()) { - return RESULT_ERR_INVALID_ARG; + return RESULT_ERR_INVALID_POS; } if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) @@ -430,9 +473,9 @@ result_t StringDataField::readSymbols(SymbolString& input, if (m_length == 4 && i == 2 && m_dataType.type == bt_dat) continue; // skip weekday in between ch = input[baseOffset + offset]; - if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat || (m_dataType.type == bt_tim && m_length > 2)) { + if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat) { if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) - return RESULT_ERR_INVALID_ARG; // invalid BCD + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD ch = (ch >> 4) * 10 + (ch & 0x0f); } switch (m_dataType.type) @@ -447,11 +490,21 @@ result_t StringDataField::readSymbols(SymbolString& input, if (i + 1 == m_length) output << (2000 + ch); else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) - return RESULT_ERR_INVALID_ARG; // invalid date + return RESULT_ERR_OUT_OF_RANGE; // invalid date else output << setw(2) << setfill('0') << static_cast(ch) << "."; break; case bt_tim: + if (m_dataType.replacement != 0 && ch == m_dataType.replacement) { + if (m_length == 1) { // truncated time + output << NULL_VALUE << ":" << NULL_VALUE; + break; + } + if (i > 0) + output << ":"; + output << NULL_VALUE; + break; + } if (m_length == 1) { // truncated time if (i == 0) { ch /= 6; // hours @@ -461,8 +514,8 @@ result_t StringDataField::readSymbols(SymbolString& input, else ch = (ch % 6) * 10; // minutes } - if ((i == 0 && ch > 24) || (i > 0 && (ch > 59 || ( last == 24 && ch > 0) ))) - return RESULT_ERR_INVALID_ARG; // invalid time + if ((i == 0 && ch > 24) || (i > 0 && (ch > 59 || (last == 24 && ch > 0) ))) + return RESULT_ERR_OUT_OF_RANGE; // invalid time if (i > 0) output << ":"; output << setw(2) << setfill('0') << static_cast(ch); @@ -512,10 +565,10 @@ result_t StringDataField::writeSymbols(istringstream& input, token.clear(); token.push_back(input.get()); if (input.eof() == true) - return RESULT_ERR_INVALID_ARG; // too short hex value + return RESULT_ERR_INVALID_NUM; // too short hex value token.push_back(input.get()); if (input.eof() == true) - return RESULT_ERR_INVALID_ARG; // too short hex value + return RESULT_ERR_INVALID_NUM; // too short hex value value = parseInt(token.c_str(), 16, 0, 0xff, result); if (result != RESULT_OK) @@ -526,7 +579,7 @@ result_t StringDataField::writeSymbols(istringstream& input, if (m_length == 4 && i == 2) continue; // skip weekday in between if (input.eof() == true || getline(input, token, '.') == 0) - return RESULT_ERR_INVALID_ARG; // incomplete + return RESULT_ERR_EOF; // incomplete value = parseInt(token.c_str(), 10, 0, 2099, result); if (result != RESULT_OK) return result; // invalid date part @@ -541,7 +594,7 @@ result_t StringDataField::writeSymbols(istringstream& input, t.tm_year = (value < 100 ? value + 2000 : value) - 1900; t.tm_isdst = 0; // automatic if (mktime(&t) < 0) - return RESULT_ERR_INVALID_ARG; // invalid date + return RESULT_ERR_INVALID_NUM; // invalid date unsigned char daysSinceSunday = (unsigned char)t.tm_wday; // Sun=0 if ((m_dataType.flags & BCD) != 0) output[baseOffset + offset - incr] = (6+daysSinceSunday) % 7; // Sun=0x06 @@ -551,18 +604,32 @@ result_t StringDataField::writeSymbols(istringstream& input, if (value >= 2000) value -= 2000; else if (value > 99) - return RESULT_ERR_INVALID_ARG; // invalid year + return RESULT_ERR_OUT_OF_RANGE; // invalid year } else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) - return RESULT_ERR_INVALID_ARG; // invalid date part + return RESULT_ERR_OUT_OF_RANGE; // invalid date part break; case bt_tim: if (input.eof() == true || getline(input, token, LENGTH_SEPARATOR) == 0) - return RESULT_ERR_INVALID_ARG; // incomplete + return RESULT_ERR_EOF; // incomplete + if (m_dataType.replacement != 0 && strcmp(token.c_str(), NULL_VALUE) == 0) { + value = m_dataType.replacement; + if (m_length == 1) { // truncated time + if (i == 0) { + last = value; + offset -= incr; // repeat for minutes + count++; + continue; + } + if (last != m_dataType.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_INVALID_ARG; // invalid time part + return RESULT_ERR_OUT_OF_RANGE; // invalid time part if (m_length == 1) { // truncated time if (i == 0) { last = value; @@ -571,10 +638,10 @@ result_t StringDataField::writeSymbols(istringstream& input, continue; } if ((value % 10) != 0) - return RESULT_ERR_INVALID_ARG; // invalid truncated time minutes + return RESULT_ERR_INVALID_NUM; // invalid truncated time minutes value = last * 6 + (value / 10); if (value > 24 * 6) - return RESULT_ERR_INVALID_ARG; // invalid time + return RESULT_ERR_OUT_OF_RANGE; // invalid time } break; default: @@ -589,18 +656,18 @@ result_t StringDataField::writeSymbols(istringstream& input, } lastLast = last; last = value; - if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat || (m_dataType.type == bt_tim && m_length > 2)) { + if ((m_dataType.flags & BCD) != 0 || m_dataType.type == bt_dat) { if (value > 99) - return RESULT_ERR_INVALID_ARG; // invalid BCD + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD value = ((value / 10) << 4) | (value % 10); } if (value > 0xff) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range output[baseOffset + offset] = (unsigned char)value; } if (i < m_length) - return RESULT_ERR_INVALID_ARG; // input too short + return RESULT_ERR_EOF; // input too short return RESULT_OK; } @@ -612,6 +679,18 @@ bool NumericDataField::hasFullByteOffset(bool after) || (after == true && m_bitOffset + (m_bitCount % 8) >= 8); } +void NumericDataField::dump(ostream& output) +{ + SingleDataField::dump(output); + if ((m_dataType.flags & ADJ) != 0) { + if ((m_dataType.maxBits % 8) != 0) + output << ":" << static_cast(m_bitCount); + else + output << ":" << static_cast(m_length); + } + output << FIELD_SEPARATOR; +} + result_t NumericDataField::readRawValue(SymbolString& input, unsigned char baseOffset, unsigned int& value) { @@ -620,7 +699,7 @@ result_t NumericDataField::readRawValue(SymbolString& input, unsigned char ch; if (baseOffset + m_length > input.size()) - return RESULT_ERR_INVALID_ARG; // not enough data available + return RESULT_ERR_INVALID_POS; // not enough data available if ((m_dataType.flags & REV) != 0) { // reverted binary representation (most significant byte first) start = m_length - 1; @@ -636,7 +715,7 @@ result_t NumericDataField::readRawValue(SymbolString& input, return RESULT_OK; } if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) - return RESULT_ERR_INVALID_ARG; // invalid BCD + return RESULT_ERR_OUT_OF_RANGE; // invalid BCD ch = (ch >> 4) * 10 + (ch & 0x0f); value += ch * exp; @@ -671,7 +750,7 @@ result_t NumericDataField::writeRawValue(unsigned int value, if ((m_dataType.flags & BCD) == 0) { if ((m_bitCount % 8) != 0 && (value & ~((1 << m_bitCount) - 1)) != 0) - return RESULT_ERR_INVALID_ARG; + return RESULT_ERR_OUT_OF_RANGE; value <<= m_bitOffset; } @@ -705,7 +784,7 @@ result_t NumberDataField::derive(string name, string comment, vector& fields) { if (m_partType != pt_any && partType == pt_any) - return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance if (name.empty() == true) name = m_name; if (comment.empty() == true) @@ -728,6 +807,13 @@ result_t NumberDataField::derive(string name, string comment, return RESULT_OK; } +void NumberDataField::dump(ostream& output) +{ + NumericDataField::dump(output); + output << static_cast(m_divisor) << FIELD_SEPARATOR; + output << m_unit << FIELD_SEPARATOR << m_comment << FIELD_SEPARATOR; +} + result_t NumberDataField::readSymbols(SymbolString& input, unsigned char baseOffset, ostringstream& output) { @@ -778,7 +864,7 @@ result_t NumberDataField::writeSymbols(istringstream& input, if (isIgnored() == true || strcasecmp(str, NULL_VALUE) == 0) value = m_dataType.replacement; // replacement value else if (str == NULL || *str == 0) - return RESULT_ERR_INVALID_ARG; // input too short + return RESULT_ERR_EOF; // input too short else { char* strEnd = NULL; if (m_divisor <= 1) { @@ -792,17 +878,17 @@ result_t NumberDataField::writeSymbols(istringstream& input, else value = strtoul(str, &strEnd, 10); if (strEnd == NULL || *strEnd != 0) - return RESULT_ERR_INVALID_ARG; // invalid value + return RESULT_ERR_INVALID_NUM; // invalid value } else { char* strEnd = NULL; double dvalue = strtod(str, &strEnd); if (strEnd == NULL || *strEnd != 0) - return RESULT_ERR_INVALID_ARG; // invalid value + return RESULT_ERR_INVALID_NUM; // invalid value dvalue = round(dvalue * m_divisor); if ((m_dataType.flags & SIG) != 0) { if (dvalue < -(1LL << (8 * m_length)) || dvalue >= (1LL << (8 * m_length))) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range if (dvalue < 0 && m_bitCount != 32) value = (unsigned int) (dvalue + (1 << m_bitCount)); else @@ -810,7 +896,7 @@ result_t NumberDataField::writeSymbols(istringstream& input, } else { if (dvalue < 0.0 || dvalue >= (1LL << (8 * m_length))) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range value = (unsigned int) dvalue; } } @@ -818,13 +904,13 @@ result_t NumberDataField::writeSymbols(istringstream& input, if ((m_dataType.flags & SIG) != 0) { // signed value if ((value & (1 << (m_bitCount - 1))) != 0) { // negative signed value if (value < m_dataType.minValueOrLength) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range } else if (value > m_dataType.maxValueOrLength) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range } else if (value < m_dataType.minValueOrLength || value > m_dataType.maxValueOrLength) - return RESULT_ERR_INVALID_ARG; // value out of range + return RESULT_ERR_OUT_OF_RANGE; // value out of range } return writeRawValue(value, baseOffset, output); @@ -837,7 +923,7 @@ result_t ValueListDataField::derive(string name, string comment, vector& fields) { if (m_partType != pt_any && partType == pt_any) - return RESULT_ERR_INVALID_ARG; // cannot create a template from a concrete instance + return RESULT_ERR_INVALID_PART; // cannot create a template from a concrete instance if (name.empty() == true) name = m_name; if (comment.empty() == true) @@ -860,6 +946,21 @@ result_t ValueListDataField::derive(string name, string comment, return RESULT_OK; } +void ValueListDataField::dump(ostream& output) +{ + NumericDataField::dump(output); + bool first = true; + for (map::iterator it = m_values.begin(); it != m_values.end(); it++) { + if (first == true) + first = false; + else + output << VALUE_SEPARATOR; + output << static_cast(it->first) << "=" << it->second; + } + output << FIELD_SEPARATOR; + output << m_unit << FIELD_SEPARATOR << m_comment << FIELD_SEPARATOR; +} + result_t ValueListDataField::readSymbols(SymbolString& input, unsigned char baseOffset, ostringstream& output) { @@ -880,7 +981,7 @@ result_t ValueListDataField::readSymbols(SymbolString& input, return RESULT_OK; } - return RESULT_ERR_INVALID_ARG; // value assignment not found + return RESULT_ERR_NOTFOUND; // value assignment not found } result_t ValueListDataField::writeSymbols(istringstream& input, @@ -898,7 +999,7 @@ result_t ValueListDataField::writeSymbols(istringstream& input, if (strcasecmp(str, NULL_VALUE) == 0) return writeRawValue(m_dataType.replacement, baseOffset, output); // replacement value - return RESULT_ERR_INVALID_ARG; // value assignment not found + return RESULT_ERR_NOTFOUND; // value assignment not found } DataFieldSet::~DataFieldSet() @@ -947,43 +1048,38 @@ result_t DataFieldSet::derive(string name, string comment, return RESULT_OK; } -result_t DataFieldSet::read(SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - ostringstream& output, bool verbose, char separator) +void DataFieldSet::dump(ostream& output) +{ + for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) + (*it)->dump(output); +} + +result_t DataFieldSet::read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, bool leadingSeparator, + bool verbose, char separator) { if (verbose) output << m_name << "={ "; - bool first = true; - unsigned char offsets[4]; - memset(offsets, 0, sizeof(offsets)); - offsets[pt_masterData] = masterOffset; - offsets[pt_slaveData] = slaveOffset; - bool previousFullByteOffset[] = { true, true, true, true }; + bool previousFullByteOffset = true; for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { SingleDataField* field = *it; - bool ignored = field->isIgnored(); - PartType partType = field->getPartType(); + if (partType != pt_any && field->getPartType() != partType) + continue; - if (ignored == false) { - if (first) - first = false; - else - output << separator; - } - if (partType == pt_masterDataID) - partType = pt_masterData; - if (previousFullByteOffset[partType] == false && field->hasFullByteOffset(false) == false) - offsets[partType]--; + if (previousFullByteOffset == false && field->hasFullByteOffset(false) == false) + offset--; - result_t result = field->read(masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], output, verbose, separator); +//cout<<"read "<getName().c_str()<<" in part "<(field->getPartType())<<" offset "<(offsets[field->getPartType()])<read(partType, data, offset, output, leadingSeparator, verbose, separator); if (result != RESULT_OK) return result; - offsets[partType] += field->getLength(partType); - - previousFullByteOffset[partType] = field->hasFullByteOffset(true); + offset += field->getLength(partType); + previousFullByteOffset = field->hasFullByteOffset(true); + leadingSeparator |= field->isIgnored() == false; } if (verbose == true) { @@ -996,46 +1092,93 @@ result_t DataFieldSet::read(SymbolString& masterData, unsigned char masterOffset } result_t DataFieldSet::write(istringstream& input, - SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - char separator) + const PartType partType, SymbolString& data, + unsigned char offset, char separator) { string token; - unsigned char offsets[4]; - memset(offsets, 0, sizeof(offsets)); - offsets[pt_masterData] = masterOffset; - offsets[pt_slaveData] = slaveOffset; - bool previousFullByteOffset[] = { true, true, true, true }; + bool previousFullByteOffset = true; for (vector::iterator it = m_fields.begin(); it < m_fields.end(); it++) { SingleDataField* field = *it; - bool ignored = field->isIgnored(); - PartType partType = field->getPartType(); + if (partType != pt_any && field->getPartType() != partType) + continue; - if (partType == pt_masterDataID) - partType = pt_masterData; - if (previousFullByteOffset[partType] == false && field->hasFullByteOffset(false) == false) - offsets[partType]--; + if (previousFullByteOffset == false && field->hasFullByteOffset(false) == false) + offset--; result_t result; if (m_fields.size() > 1) { - if (ignored == true) + if (field->isIgnored() == true) token.clear(); else if (getline(input, token, separator) == 0) - return RESULT_ERR_INVALID_ARG; // incomplete + token.clear(); istringstream single(token); - result = (*it)->write(single, masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], separator); + result = (*it)->write(single, partType, data, offset, separator); } else - result = (*it)->write(input, masterData, offsets[pt_masterData], slaveData, offsets[pt_slaveData], separator); + result = (*it)->write(input, partType, data, offset, separator); if (result != RESULT_OK) return result; - offsets[partType] += field->getLength(partType); - previousFullByteOffset[partType] = field->hasFullByteOffset(true); + offset += field->getLength(partType); + previousFullByteOffset = field->hasFullByteOffset(true); } return RESULT_OK; } + + +void DataFieldTemplates::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, bool replace) +{ + string name = field->getName(); + map::iterator it = m_fieldsByName.find(name); + if (it != m_fieldsByName.end()) { + if (replace == false) + return RESULT_ERR_DUPLICATE; // duplicate key + + delete it->second; + it->second = field; + + return RESULT_OK; + } + + m_fieldsByName[name] = field; + + return RESULT_OK; +} + +result_t DataFieldTemplates::addFromFile(vector& row, void* arg, vector< vector >* defaults) +{ + DataField* field = NULL; + vector::iterator it = row.begin(); + result_t result = DataField::create(it, row.end(), this, field); + if (result != RESULT_OK) + return result; + + result = add(field); + 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; +} + diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index a9cf9579..49e3f59f 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -23,16 +23,20 @@ #include "symbol.h" #include "result.h" #include +#include +#include +#include #include #include using namespace std; +#define FIELD_SEPARATOR ';' + /** 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_masterDataID, // stored in master data and also used as message ID part pt_slaveData, // stored in slave data }; @@ -79,7 +83,17 @@ typedef struct { */ unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result, unsigned int* length=NULL); +/** + * @brief Print the error position of the iterator to stdout. + * @param begin the iterator to the beginning of the items. + * @param end the iterator to the end of the items. + * @param pos the iterator with the erroneous position. + * @param separator the character to place between items. + */ +void printErrorPos(vector::iterator begin, const vector::iterator end, vector::iterator pos, char separator=';'); + +class DataFieldTemplates; class SingleDataField; /** @@ -104,7 +118,7 @@ public: * @brief 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 a map of @a DataField templates to be referenced by name. + * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. * @param returnField the variable in which to store the created instance. * @param isSetMessage whether the field is part of a set message (default false). * @param dstAddress the destination bus address (default @a SYN for creating a template @a DataField). @@ -112,7 +126,7 @@ public: * Note: the caller needs to free the created instance. */ static result_t create(vector::iterator& it, const vector::iterator end, - const map templates, DataField*& returnField, + DataFieldTemplates* templates, DataField*& returnField, const bool isSetMessage=false, const unsigned char dstAddress=SYN); /** * @brief Returns the length of this field (or contained fields) in bytes. @@ -145,31 +159,38 @@ public: */ string getComment() const { return m_comment; } /** - * @brief Reads the value from the master or slave @a SymbolString. - * @param masterData the unescaped master data @a SymbolString for reading binary data. - * @param slaveData the unescaped slave data @a SymbolString for reading binary data. + * @brief Dump the field settings to the output. + * @param output the @a ostream to dump to. + */ + virtual void dump(ostream& output) = 0; + /** + * @brief 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 leadingSeparator whether to prepend a separator before the formatted value. * @param verbose whether to prepend the name, append the unit (if present), and append * the comment in square brackets (if present). * @param separator the separator character between multiple fields. - * @return @a RESULT_OK on success, or an error code. + * @return @a RESULT_OK on success (or if the partType does not match), or an error code. */ - virtual result_t read(SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - ostringstream& output, + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, bool leadingSeparator=false, bool verbose=false, char separator=';') = 0; /** * @brief Writes the value to the master or slave @a SymbolString. * @param input the @a istringstream to parse the formatted value from. - * @param masterData the unescaped master data @a SymbolString for writing binary data. - * @param slaveData the unescaped slave data @a SymbolString for writing binary data. + * @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. * @return @a RESULT_OK on success, or an error code. */ virtual result_t write(istringstream& input, - SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - char separator=';') = 0; + const PartType partType, SymbolString& data, + unsigned char offset, char separator=';') = 0; protected: @@ -232,34 +253,17 @@ public: * only consumes a part of a byte and a subsequent field may re-use the same offset. */ virtual bool hasFullByteOffset(bool after) { return true; } - /** - * @brief Reads the value from the master or slave @a SymbolString. - * @param masterData the unescaped master data @a SymbolString for reading binary data. - * @param masterOffset the extra offset for reading master data. - * @param slaveData the unescaped slave data @a SymbolString for reading binary data. - * @param slaveOffset the extra offset for reading slave data. - * @param output the ostringstream to append the formatted value to. - * @param verbose whether to prepend the name, append the unit (if present), and append - * the comment in square brackets (if present). - * @return @a RESULT_OK on success, or an error code. - */ - virtual result_t read(SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - ostringstream& output, - bool verbose, char separator); - /** - * @brief Writes the value to the master or slave @a SymbolString. - * @param input the @a istringstream to parse the formatted value from. - * @param masterData the unescaped master data @a SymbolString for writing binary data. - * @param masterOffset the extra offset for writing master data. - * @param slaveData the unescaped slave data @a SymbolString for writing binary data. - * @param slaveOffset the extra offset for writing slave data. - * @return @a RESULT_OK on success, or an error code. - */ + // @copydoc + virtual void dump(ostream& output); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, bool leadingSeparator=false, + bool verbose=false, char separator=';'); + // @copydoc virtual result_t write(istringstream& input, - SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - char separator); + const PartType partType, SymbolString& data, + unsigned char offset, char separator=';');//TODO replace protected: @@ -280,6 +284,8 @@ protected: */ virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output) = 0; +protected: + /** the value unit. */ const string m_unit; /** the data type definition. */ @@ -321,6 +327,8 @@ public: string unit, const PartType partType, unsigned int divisor, map values, vector& fields); + // @copydoc + virtual void dump(ostream& output); protected: @@ -361,6 +369,8 @@ public: virtual ~NumericDataField() {} // @copydoc virtual bool hasFullByteOffset(bool after); + // @copydoc + virtual void dump(ostream& output); protected: @@ -387,7 +397,6 @@ protected: /** the offset to the first bit in the binary value. */ const unsigned char m_bitOffset; - }; @@ -424,6 +433,8 @@ public: string unit, const PartType partType, unsigned int divisor, map values, vector& fields); + // @copydoc + virtual void dump(ostream& output); protected: @@ -432,6 +443,8 @@ protected: // @copydoc virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output); +private: + /** the combined divisor to apply on the value, or 1 for none. */ const unsigned int m_divisor; @@ -471,6 +484,8 @@ public: string unit, const PartType partType, unsigned int divisor, map values, vector& fields); + // @copydoc + virtual void dump(ostream& output); protected: @@ -479,6 +494,8 @@ protected: // @copydoc virtual result_t writeSymbols(istringstream& input, const unsigned char offset, SymbolString& output); +private: + /** the value=text assignments. */ map m_values; @@ -531,17 +548,18 @@ public: */ size_t size() const { return m_fields.size(); } // @copydoc - virtual result_t read(SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - ostringstream& output, - bool verbose, char separator); + virtual void dump(ostream& output); + // @copydoc + virtual result_t read(const PartType partType, + SymbolString& data, unsigned char offset, + ostringstream& output, bool leadingSeparator=false, + bool verbose=false, char separator=';'); // @copydoc virtual result_t write(istringstream& input, - SymbolString& masterData, unsigned char masterOffset, - SymbolString& slaveData, unsigned char slaveOffset, - char separator); + const PartType partType, SymbolString& data, + unsigned char offset, char separator=';'); -protected: +private: /** the @a vector of @a SingleDataField instances part of this set. */ vector m_fields; @@ -549,4 +567,122 @@ protected: }; +/** + * @brief An abstract class that support reading definitions from a file. + */ +template +class FileReader +{ +public: + + /** + * @brief Constructs a new instance. + */ + FileReader(bool supportsDefaults) + : m_supportsDefaults(supportsDefaults) {} + /** + * @brief Destructor. + */ + virtual ~FileReader() {} + /** + * @brief Reads the definitions from a file. + * @param filename the name (and path) of the file to read. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t readFromFile(string filename, T arg=NULL) + { + ifstream ifs; + ifs.open(filename.c_str(), ifstream::in); + if (ifs.is_open() == false) + return RESULT_ERR_NOTFOUND; + + string line; + unsigned int lineNo = 0; + vector row; + string token; + vector< vector > defaults; + while (getline(ifs, line) != 0) { + lineNo++; + // skip empty lines and comments + if (line.length() == 0 || line.substr(0, 1) == "#" || line.substr(0, 2) == "//") + continue; + istringstream isstr(line); + row.clear(); + while (getline(isstr, token, FIELD_SEPARATOR) != 0) + row.push_back(token); + + if (m_supportsDefaults == true && line.substr(0, 1) == "*") { + row[0] = row[0].substr(1); + defaults.push_back(row); + continue; + } + result_t result = addFromFile(row, arg, m_supportsDefaults == true ? &defaults : NULL); + if (result != RESULT_OK) { + cerr << "error reading \"" << filename << "\" line " << static_cast(lineNo) << ": " << getResultCode(result) << endl; + ifs.close(); + return result; + } + } + + ifs.close(); + return RESULT_OK; + } + /** + * @brief Adds a definition that was read from a file. + * @param row the definition row read from the file. + * @param defaults all previously read default rows (initial star char removed), or NULL if not supported. + * @return @a RESULT_OK on success, or an error code. + */ + virtual result_t addFromFile(vector& row, T arg, vector< vector >* defaults) = 0; + +private: + /** whether this instance supports rows with defaults (starting with a star). */ + bool m_supportsDefaults; + +}; + + +/** + * @brief A map of template @a DataField instances. + */ +class DataFieldTemplates : public FileReader +{ +public: + + /** + * @brief Constructs a new instance. + */ + DataFieldTemplates() : FileReader(false) {} + /** + * @brief Destructor. + */ + virtual ~DataFieldTemplates() { clear(); } + /** + * @brief Removes all @a DataField instances. + */ + void clear(); + /** + * @brief Adds a template @a DataField instance to this map. + * @param field the @a DataField instance to add. + * @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* message, bool replace=false); + // @copydoc + virtual result_t addFromFile(vector& row, void* arg, vector< vector >* defaults); + /** + * @brief Gets the template @a DataField instance with the specified name. + * @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; + +}; + #endif // LIBEBUS_DATA_H_ diff --git a/src/lib/ebus/decode.cpp b/src/lib/ebus/decode.cpp deleted file mode 100644 index b86bed97..00000000 --- a/src/lib/ebus/decode.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "decode.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -Decode::Decode(const string& data, const string& factor) - : m_data(data) -{ - if ((factor.find_first_not_of("0123456789.") == string::npos) == true) - m_factor = static_cast(strtod(factor.c_str(), NULL)); - else - m_factor = 1.0; -} - - -string DecodeHEX::decode() -{ - ostringstream result; - - for (size_t i = 0; i < m_data.length()/2; i++) - result << m_data.substr(i*2, 2) << " "; - - return result.str().substr(0, result.str().length()-1); -} - -string DecodeUCH::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned short x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed << static_cast(x * m_factor); - - return result.str(); -} - -string DecodeSCH::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned short x; - ss >> x; - - ostringstream result; - if ((x & 0x80) == 0x80) - result << setprecision(3) << fixed - << static_cast(static_cast(- ( ((unsigned char) (~ x)) + 1) ) * m_factor); - else - result << setprecision(3) << fixed - << static_cast(static_cast(x) * m_factor); - - return result.str(); -} - -string DecodeUIN::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned short x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed << static_cast(x * m_factor); - - return result.str(); -} - -string DecodeSIN::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned short x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed << static_cast(static_cast(x) * m_factor); - - return result.str(); -} - -string DecodeULG::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned int x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed << static_cast(x * m_factor); - - return result.str(); -} - -string DecodeSLG::decode() -{ - stringstream ss; - ss << hex << m_data; - - unsigned int x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed <(static_cast(x) * m_factor); - - return result.str(); -} - -string DecodeFLT::decode() -{ - stringstream ss; - ss << hex << m_data; - - short x; - ss >> x; - - ostringstream result; - result << setprecision(3) << fixed << static_cast(x / 1000.0 * m_factor); - - return result.str(); -} - -string DecodeSTR::decode() -{ - ostringstream result; - - for (size_t i = 0; i <= m_data.length()/2; i++) { - char tmp = static_cast(strtol(m_data.substr(i*2, 2).c_str(), NULL, 16)); - if (tmp == 0x00) tmp = 0x20; - result << tmp; - } - - return result.str().substr(0, result.str().length()-1); -} - -string DecodeBCD::decode() -{ - ostringstream result; - unsigned char src = strtol(m_data.c_str(), NULL, 16); - - if ((src & 0x0F) > 0x09 || ((src >> 4) & 0x0F) > 0x09) - result << static_cast(0xFF); - else - result << static_cast(( ( ((src & 0xF0) >> 4) * 10) + (src & 0x0F) ) * m_factor); - - return result.str(); -} - -string DecodeD1B::decode() -{ - ostringstream result; - unsigned char src = strtol(m_data.c_str(), NULL, 16); - - if ((src & 0x80) == 0x80) - result << static_cast((- ( ((unsigned char) (~ src)) + 1) ) * m_factor); - else - result << static_cast(src * m_factor); - - return result.str(); -} - -string DecodeD1C::decode() -{ - ostringstream result; - unsigned char src = strtol(m_data.c_str(), NULL, 16); - - if (src > 0xC8) - result << static_cast(0xFF); - else - result << static_cast((src / 2.0) * m_factor); - - return result.str(); -} - -string DecodeD2B::decode() -{ - ostringstream result; - unsigned char src_lsb = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); - unsigned char src_msb = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); - - if ((src_msb & 0x80) == 0x80) - result << static_cast - ((- ( ((unsigned char) (~ src_msb)) + - ( ( ((unsigned char) (~ src_lsb)) + 1) / 256.0) ) ) * m_factor); - - else - result << static_cast((src_msb + (src_lsb / 256.0)) * m_factor); - - return result.str(); -} - -string DecodeD2C::decode() -{ - ostringstream result; - unsigned char src_lsb = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); - unsigned char src_msb = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); - - if ((src_msb & 0x80) == 0x80) - result << static_cast - ((- ( ( ( ((unsigned char) (~ src_msb)) * 16.0) ) + - ( ( ((unsigned char) (~ src_lsb)) & 0xF0) >> 4) + - ( ( ( ((unsigned char) (~ src_lsb)) & 0x0F) +1 ) / 16.0) ) ) * m_factor); - - else - result << static_cast(( (src_msb * 16.0) + ((src_lsb & 0xF0) >> 4) + - ((src_lsb & 0x0F) / 16.0) ) * m_factor); - - return result.str(); -} - -string DecodeBDA::decode() -{ - ostringstream result; - Decode* decode; - short array[3]; - for (int i = 0; i < 3; i++) { - decode = new DecodeBCD(m_data.substr(i*2, 2), "1.0"); - array[i] = static_cast(strtol(decode->decode().c_str(), NULL, 10)); - delete decode; - } - - result << setw(2) << setfill('0') << array[0] << "." - << setw(2) << setfill('0') << array[1] << "." - << array[2] + 2000; - - return result.str(); -} - -string DecodeHDA::decode() -{ - ostringstream result; - short dd = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); - short mm = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); - short yy = static_cast(strtol(m_data.substr(4, 2).c_str(), NULL, 16)); - - result << setw(2) << setfill('0') << dd << "." - << setw(2) << setfill('0') << mm << "." - << yy + 2000; - - return result.str(); -} - -string DecodeBTI::decode() -{ - ostringstream result; - Decode* decode; - short array[3]; - for (int i = 0; i < 3; i++) { - decode = new DecodeBCD(m_data.substr(i*2, 2), "1.0"); - array[i] = static_cast(strtol(decode->decode().c_str(), NULL, 10)); - delete decode; - } - - result << setw(2) << setfill('0') << array[0] << ":" - << setw(2) << setfill('0') << array[1] << ":" - << setw(2) << setfill('0') << array[2]; - - return result.str(); -} - -string DecodeHTI::decode() -{ - ostringstream result; - short hh = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); - short mm = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); - short ss = static_cast(strtol(m_data.substr(4, 2).c_str(), NULL, 16)); - - result << setw(2) << setfill('0') << hh << ":" - << setw(2) << setfill('0') << mm << ":" - << setw(2) << setfill('0') << ss; - - return result.str(); -} - -string DecodeBDY::decode() -{ - const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; - - ostringstream result; - short day = static_cast(strtol(m_data.c_str(), NULL, 16)); - - if (day < 0 || day > 6) - day = 7; - - result << days[day]; - - return result.str(); -} - -string DecodeHDY::decode() -{ - const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; - - ostringstream result; - short day = static_cast(strtol(m_data.c_str(), NULL, 16)) - 1; - - if (day < 0 || day > 6) - day = 7; - - result << days[day]; - - return result.str(); -} - -string DecodeTTM::decode() -{ - ostringstream result; - short hh = static_cast(strtol(m_data.c_str(), NULL, 16)) / 6; - short mm = static_cast(strtol(m_data.c_str(), NULL, 16)) % 6 * 10; - - result << setw(2) << setfill('0') << hh << ":" - << setw(2) << setfill('0') << mm; - - return result.str(); -} - diff --git a/src/lib/ebus/decode.h b/src/lib/ebus/decode.h deleted file mode 100644 index 73c8d2a4..00000000 --- a/src/lib/ebus/decode.h +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef LIBEBUS_DECODE_H_ -#define LIBEBUS_DECODE_H_ - -#include - -using namespace std; - -class Decode -{ - -public: - Decode(const string& data, const string& factor = ""); - virtual ~Decode() {} - - virtual string decode() = 0; - -protected: - string m_data; - float m_factor; - -}; - - -class DecodeHEX : public Decode -{ - -public: - DecodeHEX(const string& data) : Decode(data) {} - ~DecodeHEX() {} - - string decode(); - -}; - -class DecodeUCH : public Decode -{ - -public: - DecodeUCH(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeUCH() {} - - string decode(); - -}; - -class DecodeSCH : public Decode -{ - -public: - DecodeSCH(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeSCH() {} - - string decode(); - -}; - -class DecodeUIN : public Decode -{ - -public: - DecodeUIN(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeUIN() {} - - string decode(); - -}; - -class DecodeSIN : public Decode -{ - -public: - DecodeSIN(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeSIN() {} - - string decode(); - -}; - -class DecodeULG : public Decode -{ - -public: - DecodeULG(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeULG() {} - - string decode(); - -}; - -class DecodeSLG : public Decode -{ - -public: - DecodeSLG(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeSLG() {} - - string decode(); - -}; - -class DecodeFLT : public Decode -{ - -public: - DecodeFLT(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeFLT() {} - - string decode(); - -}; - -class DecodeSTR : public Decode -{ - -public: - DecodeSTR(string data) : Decode(data) {} - ~DecodeSTR() {} - - string decode(); - -}; - -class DecodeBCD : public Decode -{ - -public: - DecodeBCD(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeBCD() {} - - string decode(); - -}; - -class DecodeD1B : public Decode -{ - -public: - DecodeD1B(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeD1B() {} - - string decode(); - -}; - -class DecodeD1C : public Decode -{ - -public: - DecodeD1C(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeD1C() {} - - string decode(); - -}; - -class DecodeD2B : public Decode -{ - -public: - DecodeD2B(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeD2B() {} - - string decode(); - -}; - -class DecodeD2C : public Decode -{ - -public: - DecodeD2C(const string& data, const string& factor) - : Decode(data, factor) {} - ~DecodeD2C() {} - - string decode(); - -}; - -class DecodeBDA : public Decode -{ - -public: - DecodeBDA(const string& data) : Decode(data) {} - ~DecodeBDA() {} - - string decode(); - -}; - -class DecodeHDA : public Decode -{ - -public: - DecodeHDA(const string& data) : Decode(data) {} - ~DecodeHDA() {} - - string decode(); - -}; - -class DecodeBTI : public Decode -{ - -public: - DecodeBTI(const string& data) : Decode(data) {} - ~DecodeBTI() {} - - string decode(); - -}; - -class DecodeHTI : public Decode -{ - -public: - DecodeHTI(const string& data) : Decode(data) {} - ~DecodeHTI() {} - - string decode(); - -}; - -class DecodeBDY : public Decode -{ - -public: - DecodeBDY(const string& data) : Decode(data) {} - ~DecodeBDY() {} - - string decode(); - -}; - -class DecodeHDY : public Decode -{ - -public: - DecodeHDY(const string& data) : Decode(data) {} - ~DecodeHDY() {} - - string decode(); - -}; - -class DecodeTTM : public Decode -{ - -public: - DecodeTTM(const string& data) : Decode(data) {} - ~DecodeTTM() {} - - string decode(); - -}; - -#endif // LIBEBUS_DECODE_H_ diff --git a/src/lib/ebus/encode.cpp b/src/lib/ebus/encode.cpp deleted file mode 100644 index 42b58dc2..00000000 --- a/src/lib/ebus/encode.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "encode.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -Encode::Encode(const string& data, const string& factor) - : m_data(data) -{ - if ((factor.find_first_not_of("0123456789.") == string::npos) == true) - m_factor = static_cast(strtod(factor.c_str(), NULL)); - else - m_factor = 1.0; -} - - -string EncodeHEX::encode() -{ - m_data.erase(remove_if(m_data.begin(), m_data.end(), ::isspace), m_data.end()); - - return m_data; -} - -string EncodeUCH::encode() -{ - ostringstream result; - unsigned short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - result << setw(2) << hex << setfill('0') << src; - - return result.str().substr(result.str().length()-2,2); -} - -string EncodeSCH::encode() -{ - ostringstream result; - short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src < -127 || src > 127) - result << setw(2) << hex << setfill('0') - << static_cast(0x80); - else - result << setw(2) << hex << setfill('0') - << static_cast(src); - - return result.str().substr(result.str().length()-2,2); -} - -string EncodeUIN::encode() -{ - ostringstream result; - unsigned short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - result << setw(4) << hex << setfill('0') << src; - - return result.str().substr(2,2) + result.str().substr(0,2); -} - -string EncodeSIN::encode() -{ - ostringstream result; - short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - result << setw(4) << hex << setfill('0') << src; - - return result.str().substr(2,2) + result.str().substr(0,2); -} - -string EncodeULG::encode() -{ - ostringstream result; - unsigned long src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - result << setw(8) << hex << setfill('0') << src; - - return result.str().substr(6,2) + result.str().substr(4,2) + - result.str().substr(2,2) + result.str().substr(0,2); -} - -string EncodeSLG::encode() -{ - ostringstream result; - int src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - result << setw(8) << hex << setfill('0') << src; - - return result.str().substr(6,2) + result.str().substr(4,2) + - result.str().substr(2,2) + result.str().substr(0,2); -} - -string EncodeFLT::encode() -{ - ostringstream result; - short src = static_cast(strtod(m_data.c_str(), NULL) * 1000.0 / m_factor); - result << setw(4) << hex << setfill('0') << src; - - return result.str().substr(2,2) + result.str().substr(0,2); -} - -string EncodeSTR::encode() -{ - ostringstream result; - - for (size_t i = 0; i < m_data.length(); i++) - result << setw(2) << hex << setfill('0') << static_cast(m_data[i]); - - return result.str(); -} - -string EncodeBCD::encode() -{ - ostringstream result; - short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src > 99) - result << setw(2) << hex << setfill('0') - << static_cast(0xFF); - else - result << setw(2) << hex << setfill('0') - << static_cast( ((src / 10) << 4) | (src % 10) ); - - return result.str().substr(result.str().length()-2,2); -} - -string EncodeD1B::encode() -{ - ostringstream result; - short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src < -127 || src > 127) - result << setw(2) << hex << setfill('0') - << static_cast(0x80); - else - result << setw(2) << hex << setfill('0') - << static_cast(src); - - return result.str().substr(result.str().length()-2,2); -} - -string EncodeD1C::encode() -{ - ostringstream result; - float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src < 0.0 || src > 100.0) - result << setw(2) << hex << setfill('0') - << static_cast(0xFF); - else - result << setw(2) << hex << setfill('0') - << static_cast(src * 2.0); - - return result.str(); -} - -string EncodeD2B::encode() -{ - ostringstream result; - float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src < -127.999 || src > 127.999) { - result << setw(2) << hex << setfill('0') - << static_cast(0x80) - << setw(2) << hex << setfill('0') - << static_cast(0x00); - } else { - unsigned char tgt_lsb = static_cast((src - ((short) src)) * 256.0); - unsigned char tgt_msb; - - if (src < 0.0 && tgt_lsb != 0x00) - tgt_msb = static_cast((short) src - 1); - else - tgt_msb = static_cast((short) src); - - result << setw(2) << hex << setfill('0') - << static_cast(tgt_msb) - << setw(2) << hex << setfill('0') - << static_cast(tgt_lsb); - } - - return result.str(); -} - -string EncodeD2C::encode() -{ - ostringstream result; - float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); - - if (src < -2047.999 || src > 2047.999) { - result << setw(2) << hex << setfill('0') - << static_cast(0x80) - << setw(2) << hex << setfill('0') - << static_cast(0x00); - } else { - unsigned char tgt_lsb = static_cast( - ((unsigned char) ( ((short) src) % 16) << 4) + - ((unsigned char) ( (src - ((short) src)) * 16.0)) ); - - unsigned char tgt_msb; - - if (src < 0.0 && tgt_lsb != 0x00) - tgt_msb = static_cast((short) (src / 16.0) - 1); - else - tgt_msb = static_cast((short) src / 16.0); - - result << setw(2) << hex << setfill('0') - << static_cast(tgt_msb) - << setw(2) << hex << setfill('0') - << static_cast(tgt_lsb); - } - - return result.str(); -} - -string EncodeBDA::encode() -{ - // prepare data - string token; - istringstream stream(m_data); - vector data; - - while (getline(stream, token, '.') != 0) - data.push_back(token); - - ostringstream result; - result << setw(2) << dec << setfill('0') - << static_cast(strtod(data[0].c_str(), NULL)) - << setw(2) << dec << setfill('0') - << static_cast(strtod(data[1].c_str(), NULL)) - << setw(2) << dec << setfill('0') - << static_cast(strtod(data[2].c_str(), NULL) - 2000); - - return result.str(); -} - -string EncodeHDA::encode() -{ - // prepare data - string token; - istringstream stream(m_data); - vector data; - - while (getline(stream, token, '.') != 0) - data.push_back(token); - - ostringstream result; - result << setw(2) << hex << setfill('0') - << static_cast(strtod(data[0].c_str(), NULL)) - << setw(2) << hex << setfill('0') - << static_cast(strtod(data[1].c_str(), NULL)) - << setw(2) << hex << setfill('0') - << static_cast(strtod(data[2].c_str(), NULL) - 2000); - - return result.str(); -} - -string EncodeBTI::encode() -{ - // prepare data - string token; - istringstream stream(m_data); - vector data; - - while (getline(stream, token, ':') != 0) - data.push_back(token); - - ostringstream result; - result << setw(2) << dec << setfill('0') - << static_cast(strtod(data[0].c_str(), NULL)) - << setw(2) << dec << setfill('0') - << static_cast(strtod(data[1].c_str(), NULL)) - << setw(2) << dec << setfill('0') - << static_cast(strtod(data[2].c_str(), NULL)); - - return result.str(); -} - -string EncodeHTI::encode() -{ - // prepare data - string token; - istringstream stream(m_data); - vector data; - - while (getline(stream, token, ':') != 0) - data.push_back(token); - - ostringstream result; - result << setw(2) << hex << setfill('0') - << static_cast(strtod(data[0].c_str(), NULL)) - << setw(2) << hex << setfill('0') - << static_cast(strtod(data[1].c_str(), NULL)) - << setw(2) << hex << setfill('0') - << static_cast(strtod(data[2].c_str(), NULL)); - - return result.str(); -} - -string EncodeBDY::encode() -{ - const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; - short day = 7; - - for (short i = 0; i < 7; i++) - if (strcasecmp(days[i], m_data.c_str()) == 0) - day = i; - - ostringstream result; - result << setw(2) << hex << setfill('0') << day; - - return result.str(); -} - -string EncodeHDY::encode() -{ - const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; - short day = 8; - - for (short i = 0; i < 7; i++) - if (strcasecmp(days[i], m_data.c_str()) == 0) - day = i + 1; - - ostringstream result; - result << setw(2) << hex << setfill('0') << day; - - return result.str(); -} - -string EncodeTTM::encode() -{ - // prepare data - string token; - istringstream stream(m_data); - vector data; - - while (getline(stream, token, ':') != 0) - data.push_back(token); - - ostringstream result; - result << setw(2) << hex << setfill('0') - << static_cast( (strtod(data[0].c_str(), NULL) * 6) - + (strtod(data[1].c_str(), NULL) / 10) ); - - return result.str(); -} - diff --git a/src/lib/ebus/encode.h b/src/lib/ebus/encode.h deleted file mode 100644 index e5367086..00000000 --- a/src/lib/ebus/encode.h +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#ifndef LIBEBUS_ENCODE_H_ -#define LIBEBUS_ENCODE_H_ - -#include - -using namespace std; - -class Encode -{ - -public: - Encode(const string& data, const string& factor = ""); - virtual ~Encode() {} - - virtual string encode() = 0; - -protected: - string m_data; - float m_factor; - -}; - - -class EncodeHEX : public Encode -{ - -public: - EncodeHEX(const string& data) : Encode(data) {} - ~EncodeHEX() {} - - string encode(); - -}; - -class EncodeUCH : public Encode -{ - -public: - EncodeUCH(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeUCH() {} - - string encode(); - -}; - -class EncodeSCH : public Encode -{ - -public: - EncodeSCH(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeSCH() {} - - string encode(); - -}; - -class EncodeUIN : public Encode -{ - -public: - EncodeUIN(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeUIN() {} - - string encode(); - -}; - -class EncodeSIN : public Encode -{ - -public: - EncodeSIN(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeSIN() {} - - string encode(); - -}; - -class EncodeULG : public Encode -{ - -public: - EncodeULG(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeULG() {} - - string encode(); - -}; - -class EncodeSLG : public Encode -{ - -public: - EncodeSLG(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeSLG() {} - - string encode(); - -}; - -class EncodeFLT : public Encode -{ - -public: - EncodeFLT(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeFLT() {} - - string encode(); - -}; - -class EncodeSTR : public Encode -{ - -public: - EncodeSTR(const string& data) : Encode(data) {} - ~EncodeSTR() {} - - string encode(); - -}; - -class EncodeBCD : public Encode -{ - -public: - EncodeBCD(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeBCD() {} - - string encode(); - -}; - -class EncodeD1B : public Encode -{ - -public: - EncodeD1B(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeD1B() {} - - string encode(); - -}; - -class EncodeD1C : public Encode -{ - -public: - EncodeD1C(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeD1C() {} - - string encode(); - -}; - -class EncodeD2B : public Encode -{ - -public: - EncodeD2B(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeD2B() {} - - string encode(); - -}; - -class EncodeD2C : public Encode -{ - -public: - EncodeD2C(const string& data, const string& factor) - : Encode(data, factor) {} - ~EncodeD2C() {} - - string encode(); - -}; - -class EncodeBDA : public Encode -{ - -public: - EncodeBDA(const string& data) : Encode(data) {} - ~EncodeBDA() {} - - string encode(); - -}; - -class EncodeHDA : public Encode -{ - -public: - EncodeHDA(const string& data) : Encode(data) {} - ~EncodeHDA() {} - - string encode(); - -}; - -class EncodeBTI : public Encode -{ - -public: - EncodeBTI(const string& data) : Encode(data) {} - ~EncodeBTI() {} - - string encode(); - -}; - -class EncodeHTI : public Encode -{ - -public: - EncodeHTI(const string& data) : Encode(data) {} - ~EncodeHTI() {} - - string encode(); - -}; - -class EncodeBDY : public Encode -{ - -public: - EncodeBDY(const string& data) : Encode(data) {} - ~EncodeBDY() {} - - string encode(); - -}; - -class EncodeHDY : public Encode -{ - -public: - EncodeHDY(const string& data) : Encode(data) {} - ~EncodeHDY() {} - - string encode(); - -}; - -class EncodeTTM : public Encode -{ - -public: - EncodeTTM(const string& data) : Encode(data) {} - ~EncodeTTM() {} - - string encode(); - -}; - -#endif // LIBEBUS_ENCODE_H_ diff --git a/src/lib/ebus/message.cpp b/src/lib/ebus/message.cpp index 6bd69d92..4e245ae0 100644 --- a/src/lib/ebus/message.cpp +++ b/src/lib/ebus/message.cpp @@ -27,28 +27,76 @@ using namespace std; -result_t Message::create(vector::iterator& it, const vector::iterator end, - const map templates, Message*& returnValue) +Message::Message(const string clazz, const string name, const bool isSet, + const bool isPassive, const string comment, + const unsigned char srcAddress, const unsigned char dstAddress, + const vector id, DataField* data, + const unsigned int pollPriority) + : m_class(clazz), m_name(name), m_isSet(isSet), + m_isPassive(isPassive), m_comment(comment), + m_srcAddress(srcAddress), m_dstAddress(dstAddress), + m_id(id), m_data(data), m_pollPriority(pollPriority), + m_lastUpdateTime(0) { + int exp = 7; + unsigned long long key = (unsigned long long)(id.size()-2) << (8 * exp + 5); + if (isPassive == true) + key |= (unsigned long long)getMasterNumber(srcAddress) << (8 * exp--); // 0..25 + else + key |= 0x1fLL << (8 * exp--); // special value for active + key |= (unsigned long long)dstAddress << (8 * exp--); + for (vector::const_iterator it=id.begin(); it* defaults, size_t pos) +{ +/*cout<<"getDefault("<(defaults->size()); +cout<<","<(pos)<<"=";*/ + if (value.length() > 0 || defaults == NULL || pos > defaults->size()) { +//cout<at(pos); +//cout<::iterator& it, const vector::iterator end, + vector< vector >* defaultsRows, + DataFieldTemplates* templates, Message*& returnValue) +{ + // [type];[class];name;[comment];[QQ];ZZ;id;fields... result_t result; - // [type];class;name;[comment];[QQ];ZZ;id;fields... + bool isSet = false, isPassive = false; + char defaultsChar; + unsigned int pollPriority = 0; + size_t defaultPos = 1; if (it == end) return RESULT_ERR_EOF; const char* str = (*it++).c_str(); if (it == end) return RESULT_ERR_EOF; - bool isSetMessage, isActiveMessage; - unsigned int pollPriority = 0; - if (strcasecmp(str, "W") == 0) { - isActiveMessage = true; - isSetMessage = true; - } else if (str[0] == 'C' || str[0] == 'c') { - isActiveMessage = false; - isSetMessage = str[1] == 'W' || str[1] == 'w'; - } else if (str[0] == 'P' || str[0] == 'p') { - isActiveMessage = true; - isSetMessage = false; + if (str[0] == 0 || strncasecmp(str, "R", 1) == 0) { // default: active get + defaultsChar = 'r'; + } else if (strncasecmp(str, "W", 1) == 0) { // active set + isSet = true; + defaultsChar = 'w'; + } else if (strncasecmp(str, "P", 1) == 0) { // poll (=active get) if (str[1] == 0) pollPriority = 1; else { @@ -57,12 +105,31 @@ result_t Message::create(vector::iterator& it, const vector::ite if (result != RESULT_OK) return result; } - } else { - isActiveMessage = true; - isSetMessage = false; + defaultsChar = 'r'; + } else if (str[0] >= '0' && str[0] <= '9') { // poll priority (=active get) + result_t result; + pollPriority = parseInt(str, 10, 1, 9, result); + if (result != RESULT_OK) + return result; + defaultsChar = 'r'; + } else { // any other: passive set/get + isPassive = true; + isSet = strncasecmp(str+1, "W", 1) == 0; + defaultsChar = str[0]; } - string clazz = *it++; + 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[0] == defaultsChar) { + defaults = &(*it); + break; + } + } + } + + string clazz = getDefault(*it++, defaults, defaultPos++); if (it == end) return RESULT_ERR_EOF; @@ -71,17 +138,18 @@ result_t Message::create(vector::iterator& it, const vector::ite return RESULT_ERR_EOF; if (name.length() == 0) return RESULT_ERR_INVALID_ARG; // empty name + defaultPos++; - string comment = *it++; + string comment = getDefault(*it++, defaults, defaultPos++); if (it == end) return RESULT_ERR_EOF; - str = (*it++).c_str(); + str = getDefault(*it++, defaults, defaultPos++).c_str(); if (it == end) return RESULT_ERR_EOF; unsigned char srcAddress; - if (*str == 0 || isActiveMessage == true) - srcAddress = SYN; // no specific source defined, or ignore for active message + if (*str == 0) + srcAddress = SYN; // no specific source defined else { srcAddress = parseInt(str, 16, 0, 0xff, result); if (result != RESULT_OK) @@ -90,7 +158,7 @@ result_t Message::create(vector::iterator& it, const vector::ite return RESULT_ERR_INVALID_ARG; } - str = (*it++).c_str(); + str = getDefault(*it++, defaults, defaultPos++).c_str(); if (it == end) return RESULT_ERR_EOF; @@ -100,73 +168,262 @@ result_t Message::create(vector::iterator& it, const vector::ite if (isValidAddress(dstAddress) == false) return RESULT_ERR_INVALID_ARG; - istringstream input(*it++); // message id (PBSB + optional master data) vector id; - string token; - if (it == end) - return RESULT_ERR_EOF; - while (input.eof() == false) { - while (input.peek() == ' ') - input.get(); - if (input.eof() == true) // no more digits - break; - token.clear(); - token.push_back(input.get()); - if (input.eof() == true) - return RESULT_ERR_INVALID_ARG; // too short hex - token.push_back(input.get()); + for (int pos=0, useDefaults=1; pos<2; pos++) { // message id (PBSB, optional master data) + string token = *it++; + if (useDefaults == 1) { + if (pos == 0 && token.size() > 0) { + useDefaults = 0; + } else { + token = getDefault("", defaults, defaultPos).append(token); + } + } + istringstream input(token); + if (it == end) + return RESULT_ERR_EOF; + while (input.eof() == false) { + while (input.peek() == ' ') + input.get(); + if (input.eof() == true) // no more digits + break; + token.clear(); + token.push_back(input.get()); + if (input.eof() == true) { + return RESULT_ERR_INVALID_ARG; // too short hex + } + token.push_back(input.get()); - unsigned char value = parseInt(token.c_str(), 16, 0, 0xff, result); - if (result != RESULT_OK) - return result; // invalid hex value - id.push_back(value); + unsigned char value = parseInt(token.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) { + return result; // invalid hex value + } + id.push_back(value); + } + if (pos == 0 && id.size() != 2) { + return RESULT_ERR_INVALID_ARG; // missing/too short/too PBSB + } + defaultPos++; } - if (id.size() < 2 || id.size() > 6) + if (id.size() < 2 || id.size() > 6) { return RESULT_ERR_INVALID_ARG; // missing/too short/too long ID + } + 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; - result = DataField::create(it, end, templates, data, isSetMessage, dstAddress); + result = DataField::create(it, realEnd, templates, data, isSet, dstAddress); + if (result != RESULT_OK) { + return result; + } + returnValue = new Message(clazz, name, isSet, isPassive, comment, srcAddress, dstAddress, id, data, pollPriority); + return RESULT_OK; +} + +result_t Message::prepareMaster(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator) +{ + if (m_isPassive == true) + return RESULT_ERR_INVALID_ARG; // prepare not possible + + SymbolString master; + master.clear(); + result_t result = master.push_back(srcAddress, false, false); if (result != RESULT_OK) return result; - - returnValue = new Message(clazz, name, isSetMessage, isActiveMessage, comment, srcAddress, dstAddress, id, data, pollPriority); - return RESULT_OK; -} - -result_t Message::prepare(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator) -{ - if (m_isActiveMessage == true) { - masterData.clear(); - masterData.push_back(srcAddress, false); - masterData.push_back(m_dstAddress, false); - masterData.push_back(m_id[0], false); - masterData.push_back(m_id[1], false); - unsigned char addData = m_data->getLength(pt_masterData); - masterData.push_back(m_id.size() - 2 + addData, false); - for (size_t i=2; iwrite(input, masterData, m_id.size() - 2, slaveData, 0, separator); - if (result != RESULT_OK) - return result; - masterData.push_back(masterData.getCRC(), false, false); - } - return RESULT_OK; -} - -result_t Message::handle(SymbolString& masterData, SymbolString& slaveData, - ostringstream& output, char separator, bool answer) -{ - if (m_isActiveMessage == true) { - result_t result = m_data->read(masterData, m_id.size() - 2, slaveData, 0, output, false, separator); + result = master.push_back(m_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; + unsigned char addData = m_data->getLength(pt_masterData); + result = master.push_back(m_id.size() - 2 + addData, false, false); + if (result != RESULT_OK) + return result; + for (size_t i=2; iwrite(input, pt_masterData, master, m_id.size() - 2, separator); + if (result != RESULT_OK) + return result; + masterData = SymbolString(master); + return result; +} + +result_t Message::decode(const PartType partType, SymbolString& data, + ostringstream& output, bool leadingSeparator, char separator) +{ + unsigned char offset; + if (partType == pt_masterData) + offset = m_id.size() - 2; + else + offset = 0; + int startPos = output.str().length(); + result_t result = m_data->read(partType, data, offset, output, leadingSeparator, false, separator); + time(&m_lastUpdateTime); + if (result != RESULT_OK) { + m_lastValue.clear(); + return result; + } + m_lastValue = output.str().substr(startPos); + /*if (m_isPassive == false && answer == true) { istringstream input; // TODO create input from database of internal variables result_t result = m_data->write(input, masterData, m_id.size() - 2, slaveData, 0, separator); if (result != RESULT_OK) return result; - } + }*/ return RESULT_OK; } + +result_t MessageMap::add(Message* message) +{ + unsigned long long pkey = message->getKey(); + bool isPassive = message->isPassive(); + if (isPassive == true) { + map::iterator keyIt = m_passiveMessagesByKey.find(pkey); + if (keyIt != m_passiveMessagesByKey.end()) { + return RESULT_ERR_DUPLICATE; // duplicate key + } + } + bool isSet = message->isSet(); + string clazz = message->getClass(); + string name = message->getName(); + string key = string(isPassive ? "P" : (isSet ? "W" : "R")) + clazz + ";" + name; + map::iterator nameIt = m_messagesByName.find(key); + if (nameIt != m_messagesByName.end()) { + return RESULT_ERR_DUPLICATE; // duplicate key + } + + m_messagesByName[key] = message; + + key = string(isPassive ? "-P;" : (isSet ? "-W;" : "-R;")) + name; // also store without class + m_messagesByName[key] = message; // last key without class overrides previous + + if (message->isPassive() == true) { + unsigned char idLength = message->getId().size() - 2; + if (idLength < m_minIdLength) + m_minIdLength = idLength; + if (idLength > m_maxIdLength) + m_maxIdLength = idLength; + m_passiveMessagesByKey[pkey] = message; + } + + //m_pollMessages.push() + + return RESULT_OK; +} + +result_t MessageMap::addFromFile(vector& row, DataFieldTemplates* arg, vector< vector >* defaults) +{ + Message* message = NULL; + string types = row[0]; + if (types.length() == 0) + types.append("r"); + result_t result = RESULT_ERR_EOF; + + istringstream stream(types); + string type; + while (getline(stream, type, ',') != 0) { + row[0] = type; + vector::iterator it = row.begin(); + result = Message::create(it, row.end(), defaults, arg, message); + if (result != RESULT_OK) { + printErrorPos(row.begin(), row.end(), it); + return result; + } + result = add(message); + if (result != RESULT_OK) { + delete message; + } + } + return result; +} + +Message* MessageMap::find(const string& clazz, const string& name, const bool isSet,const bool isPassive) +{ + for (int i=0; i<2; i++) { + string key; + if (i==0) + key = string(isPassive ? "P" : (isSet ? "W" : "R")) + clazz + ";" + name; + else + key = string(isPassive ? "-P;" : (isSet ? "-W;" : "-R;")) + name; // second try: without class + map::iterator it = m_messagesByName.find(key); + if (it != m_messagesByName.end()) + return it->second; + } + + return NULL; +} + +Message* MessageMap::find(SymbolString& master) +{ + if (master.size() < 5) + return NULL; + unsigned char maxIdLength = master[4]; + if (maxIdLength < m_minIdLength) + return NULL; + if (maxIdLength > m_maxIdLength) + maxIdLength = m_maxIdLength; + if (master.size() < 5+maxIdLength) + return NULL; + + unsigned long long sourceMask = 0x1fLL << (8 * 7); + for (int idLength=maxIdLength; idLength>=m_minIdLength; idLength--) { + int exp = 7; + unsigned long long key = (unsigned long long)idLength << (8 * exp + 5); + key |= (unsigned long long)getMasterNumber(master[0]) << (8 * exp--); + key |= (unsigned long long)master[1] << (8 * exp--); + key |= (unsigned long long)master[2] << (8 * exp--); + key |= (unsigned long long)master[3] << (8 * exp--); + for (unsigned char i=0; i::iterator it = m_passiveMessagesByKey.find(key); + if (it != m_passiveMessagesByKey.end()) + return it->second; + + if ((key & sourceMask) != 0) { + key &= ~sourceMask; // try again without specific source master + it = m_passiveMessagesByKey.find(key); + if (it != m_passiveMessagesByKey.end()) + return it->second; + } + } + + return NULL; +} + +void MessageMap::clear() +{ + for (map::iterator it=m_messagesByName.begin(); it!=m_messagesByName.end(); it++) { + if (it->first[0] != '-') // avoid double free + delete it->second; + it->second = NULL; + } + m_messagesByName.clear(); + m_passiveMessagesByKey.clear(); + m_maxIdLength = 0; +} diff --git a/src/lib/ebus/message.h b/src/lib/ebus/message.h index 4856f9bb..a5bef802 100644 --- a/src/lib/ebus/message.h +++ b/src/lib/ebus/message.h @@ -25,40 +25,36 @@ #include "symbol.h" #include #include +#include using namespace std; /** - * @brief Base class for all kinds of bus messages. + * @brief Defines parameters of a message sent or received on the bus. */ class Message { public: /** - * @brief Constructs a new instance. + * @brief Construct a new instance. * @param class the optional device class. * @param name the message name (unique within the same class and type). - * @param isSetMessage whether this is a set message. - * @param isActiveMessage true if message can be initiated by the daemon - * itself any any other participant, false if message can only be initiated - * by a participant other than the daemon. + * @param isSet whether this is a set 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 (optional if passive), or @a SYN for any. + * @param srcAddress the source address, or @a SYN for any (only relevant if passive). * @param dstAddress the destination address. * @param id the primary, secondary, and optional further ID bytes. * @param data the @a DataField for encoding/decoding the message. * @param pollPriority the priority for polling, or 0 for no polling at all. */ - Message(const string clazz, const string name, const bool isSetMessage, - const bool isActiveMessage, const string comment, + Message(const string clazz, const string name, const bool isSet, + const bool isPassive, const string comment, const unsigned char srcAddress, const unsigned char dstAddress, const vector id, DataField* data, - const unsigned int pollPriority) - : m_class(clazz), m_name(name), m_isSetMessage(isSetMessage), - m_isActiveMessage(isActiveMessage), m_comment(comment), - m_srcAddress(srcAddress), m_dstAddress(dstAddress), - m_id(id), m_data(data), m_pollPriority(pollPriority) {} + const unsigned int pollPriority); /** * @brief Destructor. */ @@ -67,13 +63,15 @@ public: * @brief Factory method for creating a new instance. * @param it the iterator to traverse for the definition parts. * @param end the iterator pointing to the end of the definition parts. - * @param templates a map of @a DataField templates to be referenced by name. + * @param defaultsRows a @a vector with rows containing defaults, or NULL. + * @param templates the @a DataFieldTemplates to be referenced by name, or NULL. * @param returnValue the variable in which to store the created instance. * @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, - const map templates, Message*& returnValue); + vector< vector >* defaultsRows, + DataFieldTemplates* templates, Message*& returnValue); /** * @brief Get the optional device class. * @return the optional device class. @@ -88,15 +86,13 @@ public: * @brief Get whether this is a set message. * @return whether this is a set message. */ - bool isSetMessage() const { return m_isSetMessage; } + bool isSet() const { return m_isSet; } /** - * @brief Get whether message can be initiated by the daemon itself and any other - * participant. - * @return true if message can be initiated by the daemon itself and any other - * participant, false if message can only be initiated by a participant - * other than the daemon. + * @brief 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 isActiveMessage() const { return m_isActiveMessage; } + bool isPassive() const { return m_isPassive; } /** * @brief Get the comment. * @return the comment. @@ -118,30 +114,47 @@ public: */ vector getId() const { return m_id; } /** - * @brief Reads the value from the master or slave @a SymbolString. - * @param masterData the unescaped master data @a SymbolString for reading binary data. - * @param slaveData the unescaped slave data @a SymbolString for reading binary data. - * @param output the @a ostringstream to append the formatted value to. - * @param verbose whether to prepend the name, append the unit (if present), and append - * the comment in square brackets (if present). - * @param separator the separator character between multiple fields. - * @return @a RESULT_OK on success, or an error code. + * @brief Return the key for storing in @a MessageSet. + * @return the key for storing in @a MessageSet. */ - //result_t read(SymbolString& masterData, SymbolString& slaveData, ostringstream& output, - // bool verbose=false, char separator=';') = 0; + unsigned long long getKey() { return m_key; } /** - * @brief Writes the value to the master or slave @a SymbolString. - * @param input the @a istringstream to parse the formatted value from. - * @param masterData the unescaped master data @a SymbolString for writing binary data. - * @param slaveData the unescaped slave data @a SymbolString for writing binary data. + * @brief 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; } + /** + * @brief Prepare master @a SymbolString for sending to the bus. + * @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. * @return @a RESULT_OK on success, or an error code. */ - result_t prepare(const unsigned char srcAddress, SymbolString& masterData, + result_t prepareMaster(const unsigned char srcAddress, SymbolString& masterData, istringstream& input, char separator=';'); - result_t handle(SymbolString& masterData, SymbolString& slaveData, - ostringstream& output, char separator=';', bool answer=false); + /** + * @brief Decode a received message. + * @param partType the @a PartType of the data. + * @param data the unescaped data @a SymbolString for reading binary data. + * @param output the @a ostringstream to append the formatted value to. + * @param leadingSeparator whether to prepend a separator before the formatted value. + * @param separator the separator character between multiple fields. + * @return @a RESULT_OK on success, or an error code. + */ + result_t decode(const PartType partType, SymbolString& data, + ostringstream& output, bool leadingSeparator=false, char separator=';'); + /** + * @brief Get the last decoded value. + * @return the last decoded value, or the empty string if it was not successful. + */ + string getLastValue() { return m_lastValue; } + + /** + * @brief Get the system time when @a m_lastValue was updated. + * @return the system time when @a m_lastValue was updated, or 0 if this message was not decoded yet. + */ + time_t getLastUpdateTime() { return m_lastUpdateTime; } private: @@ -150,23 +163,89 @@ private: /** the message name (unique within the same class and type). */ const string m_name; /** whether this is a set message. */ - const bool m_isSetMessage; - /** true if message can be initiated by the daemon itself and any other - * participant, false if message can only be initiated by a participant - * other than the daemon. */ - const bool m_isActiveMessage; + const bool m_isSet; + /** 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 (optional if passive), or @a SYN for any. */ + /** the source address, or @a SYN for any (only relevant if passive). */ const unsigned char m_srcAddress; /** the destination address. */ const unsigned char m_dstAddress; /** the primary, secondary, and optionally further command ID bytes. */ const vector m_id; + /** the key for storing in @a MessageSet. */ + unsigned long long m_key; /** the @a DataField for encoding/decoding the message. */ DataField* m_data; /** the priority for polling, or 0 for no polling at all. */ const unsigned char m_pollPriority; + /** the last decoded value. */ + string m_lastValue; + /** the system time when @a m_lastValue was updated. */ + time_t m_lastUpdateTime; +}; + +/** + * @brief Holds a map of all known @a Message instances. + */ +class MessageMap : public FileReader +{ +public: + + /** + * @brief Construct a new instance. + */ + MessageMap() : FileReader(true), m_minIdLength(4), m_maxIdLength(0) {} + /** + * @brief Destructor. + */ + virtual ~MessageMap() { clear(); } + /** + * @brief Add a @a Message instance to this set. + * @param message the @a Message instance to add. + * @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); + // @copydoc + virtual result_t addFromFile(vector& row, DataFieldTemplates* arg, vector< vector >* defaults); + /** + * @brief Find the @a Message instance for the specified class and name. + * @param class the optional device class. + * @param name the message name. + * @param isSet whether this is a set 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& clazz, const string& name, const bool isSet, const bool isPassive=false); + /** + * @brief Find the @a Message instance for the specified master data. + * @param master the master @a SymbolString for identifying the @a Message. + * @return the @a Message instance, or NULL. + * Note: the caller may not free the returned instance. + */ + Message* find(SymbolString& master); + /** + * @brief Removes all @a Message instances. + */ + void clear(); + +private: + + /** the minimum ID length used by any of the known @a Message instances. */ + unsigned char m_minIdLength; + + /** the maximum ID length used by any of the known @a Message instances. */ + unsigned char m_maxIdLength; + + /** the known @a Message instances by class and name. */ + map m_messagesByName; + + /** the known passive @a Message instances by key. */ + map m_passiveMessagesByKey; }; diff --git a/src/lib/ebus/port.cpp b/src/lib/ebus/port.cpp index c5d7d4cc..abccd666 100644 --- a/src/lib/ebus/port.cpp +++ b/src/lib/ebus/port.cpp @@ -22,9 +22,11 @@ #endif #include "port.h" +#include "result.h" #include #include #include +#include #include #include #include @@ -61,16 +63,16 @@ bool Device::isValid() ssize_t Device::sendBytes(const unsigned char* buffer, size_t nbytes) { if (isValid() == false) - return -1; // TODO RESULT_ERR_DEVICE + return RESULT_ERR_DEVICE; // write bytes to device return write(m_fd, buffer, nbytes); } -ssize_t Device::recvBytes(const long timeout, size_t maxCount) +ssize_t Device::recvBytes(const long timeout, size_t maxCount, unsigned char* buffer) { if (isValid() == false) - return -1; // TODO RESULT_ERR_DEVICE + return RESULT_ERR_DEVICE; if (timeout > 0) { int ret; @@ -100,16 +102,26 @@ ssize_t Device::recvBytes(const long timeout, size_t maxCount) ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL); #endif #endif + if (ret == -1) return RESULT_ERR_DEVICE; + if (ret == 0) return RESULT_ERR_TIMEOUT; + } - if (ret == -1) return -1; // TODO RESULT_ERR_DEVICE - if (ret == 0) return -2; // TODO RESULT_ERR_TIMEOUT + if (buffer != NULL) { + // read bytes from device directly into provided buffer + ssize_t nbytes = read(m_fd, buffer, maxCount); + if (nbytes == 0) + return RESULT_ERR_EOF; + + return nbytes; } if (maxCount > sizeof(m_buffer)) maxCount = sizeof(m_buffer); - // read bytes from device + // read bytes from device into temporary buffer ssize_t nbytes = read(m_fd, m_buffer, maxCount); + if (nbytes == 0) + return RESULT_ERR_EOF; for (int i = 0; i < nbytes; i++) m_recvBuffer.push(m_buffer[i]); @@ -132,7 +144,7 @@ unsigned char Device::getByte() } -void DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck) +result_t DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck) { m_noDeviceCheck = noDeviceCheck; struct termios newSettings; @@ -142,7 +154,7 @@ void DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck) m_fd = open(deviceName.c_str(), O_RDWR | O_NOCTTY); if (m_fd < 0 || isatty(m_fd) == 0) - return; + return RESULT_ERR_NOTFOUND; // save current settings tcgetattr(m_fd, &m_oldSettings); @@ -151,10 +163,11 @@ void DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck) memset(&newSettings, '\0', sizeof(newSettings)); newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); - newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - newSettings.c_iflag |= IGNPAR; + 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; @@ -168,7 +181,7 @@ void DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck) fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); m_open = true; - + return RESULT_OK; } void DeviceSerial::closeDevice() @@ -189,7 +202,7 @@ void DeviceSerial::closeDevice() } -void DeviceNetwork::openDevice(const string deviceName, const bool noDeviceCheck) +result_t DeviceNetwork::openDevice(const string deviceName, const bool noDeviceCheck) { m_noDeviceCheck = noDeviceCheck; @@ -210,13 +223,13 @@ void DeviceNetwork::openDevice(const string deviceName, const bool noDeviceCheck he = gethostbyname(host); if (he == NULL) - return; + return RESULT_ERR_NOTFOUND; memcpy(&sock.sin_addr, he->h_addr_list[0], he->h_length); } else { ret = inet_aton(host, &sock.sin_addr); if (ret == 0) - return; + return RESULT_ERR_NOTFOUND; } sock.sin_family = AF_INET; @@ -224,14 +237,16 @@ void DeviceNetwork::openDevice(const string deviceName, const bool noDeviceCheck m_fd = socket(AF_INET, SOCK_STREAM, 0); if (m_fd < 0) - return; + return RESULT_ERR_GENERIC_IO; ret = connect(m_fd, (struct sockaddr*) &sock, sizeof(sock)); if (ret < 0) - return; + return RESULT_ERR_GENERIC_IO; free(hostport); m_open = true; + + return RESULT_OK; } void DeviceNetwork::closeDevice() @@ -246,8 +261,12 @@ void DeviceNetwork::closeDevice() } -Port::Port(const string deviceName, const bool noDeviceCheck) - : m_deviceName(deviceName), m_noDeviceCheck(noDeviceCheck) +Port::Port(const string deviceName, const bool noDeviceCheck, + const bool logRaw, void (*logRawFunc)(const unsigned char byte, bool received), + const bool dumpRaw, const char* dumpRawFile, const long dumpRawMaxSize) + : m_deviceName(deviceName), m_noDeviceCheck(noDeviceCheck), + m_logRaw(logRaw), m_logRawFunc(logRawFunc), + m_dumpRawFile(dumpRawFile), m_dumpRawMaxSize(dumpRawMaxSize) { m_device = NULL; @@ -256,6 +275,56 @@ Port::Port(const string deviceName, const bool noDeviceCheck) setType(dt_network); else setType(dt_serial); + + m_dumpRaw = false; + + setDumpRaw(dumpRaw); // open fstream if necessary +} + +unsigned char Port::byte() +{ + unsigned char byte = m_device->getByte(); + + if (m_logRaw == true && m_logRawFunc != NULL) + (*m_logRawFunc)(byte, true); + + if (m_dumpRaw == true && m_dumpRawStream.is_open() == true) { + m_dumpRawStream.write((char*)&byte, 1); + + if (m_dumpRawStream.tellp() >= m_dumpRawMaxSize * 1024) { + string oldfile = m_dumpRawFile + ".old"; + if (rename(m_dumpRawFile.c_str(), oldfile.c_str()) == 0) { + m_dumpRawStream.close(); + m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app); + } + } + } + + return byte; +} + +void Port::setDumpRaw(bool dumpRaw) +{ + if (dumpRaw == m_dumpRaw) + return; + + m_dumpRaw = dumpRaw; + + if (dumpRaw == false) + m_dumpRawStream.close(); + else + m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app); +} + +void Port::setDumpRawFile(const string& dumpFile) { + if (dumpFile == m_dumpRawFile) + return; + + m_dumpRawStream.close(); + m_dumpRawFile = dumpFile; + + if (m_dumpRaw == true) + m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app); } void Port::setType(const DeviceType type) diff --git a/src/lib/ebus/port.h b/src/lib/ebus/port.h index 1abc4880..abd2d55d 100644 --- a/src/lib/ebus/port.h +++ b/src/lib/ebus/port.h @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include "result.h" using namespace std; @@ -64,7 +67,7 @@ public: * @param deviceName to determine device type. * @param noDeviceCheck en-/disable device check. */ - virtual void openDevice(const string deviceName, const bool noDeviceCheck) = 0; + virtual result_t openDevice(const string deviceName, const bool noDeviceCheck) = 0; /** * @brief virtual close function for closing opened file descriptor @@ -89,9 +92,10 @@ public: * @brief recvBytes read bytes from opened file descriptor. * @param timeout time for new input data [usec]. * @param maxCount max size of receive buffer. + * @param buffer optional direct buffer to write to (instead of queuing the data). * @return number of read bytes or -1 if an error has occured. */ - ssize_t recvBytes(const long timeout, size_t maxCount); + ssize_t recvBytes(const long timeout, size_t maxCount, unsigned char* buffer=NULL); /** * @brief fetch first byte from receive buffer. @@ -147,7 +151,7 @@ public: * @param deviceName to determine device type. * @param noDeviceCheck en-/disable device check. */ - void openDevice(const string deviceName, const bool noDeviceCheck); + virtual result_t openDevice(const string deviceName, const bool noDeviceCheck); /** * @brief close function for closing opened file descriptor @@ -177,7 +181,7 @@ public: * @param deviceName to determine device type. * @param noDeviceCheck en-/disable device check. */ - void openDevice(const string deviceName, const bool noDeviceCheck); + virtual result_t openDevice(const string deviceName, const bool noDeviceCheck); /** * @brief close opened file descriptor @@ -199,18 +203,25 @@ public: * @brief constructs a new instance and determine device type. * @param deviceName to determine device type. * @param noDeviceCheck en-/disable device check. + * @param logRaw whether logging of raw data is enabled. + * @param logRawFunc a function to call for logging raw data, or NULL. + * @param dumpRaw whether dumping of raw data to a file is enabled. + * @param dumpRawFile the name of the file to dump raw data to. + * @param dumpRawMaxSize the maximum size of @a m_dumpFile. */ - Port(const string deviceName, const bool noDeviceCheck); + Port(const string deviceName, const bool noDeviceCheck, + const bool logRaw, void (*logRawFunc)(const unsigned char byte, bool received), + const bool dumpRaw, const char* dumpRawFile, const long dumpRawMaxSize); /** * @brief destructor. */ - ~Port() { delete m_device; } + ~Port() { delete m_device; m_dumpRawStream.close(); } /** * @brief open device */ - void open() { m_device->openDevice(m_deviceName, m_noDeviceCheck); } + result_t open() { return m_device->openDevice(m_deviceName, m_noDeviceCheck); } /** * @brief close device @@ -230,22 +241,33 @@ public: * @return number of written bytes or -1 if an error has occured. */ ssize_t send(const unsigned char* buffer, size_t nbytes = MAX_WRITE_SIZE) - { return m_device->sendBytes(buffer, nbytes); } + { + ssize_t ret = m_device->sendBytes(buffer, nbytes); + if (ret>0 && m_logRaw == true && m_logRawFunc != NULL) + (*m_logRawFunc)(buffer[0], false); + return ret; + } /** * @brief recv read bytes from opened file descriptor. - * @param timeout max time out for new input data. + * @param timeout max time out for new input data [usec], or 0 for infinite. * @param maxCount max size of receive buffer. - * @return number of read bytes or -1 if an error has occured. + * @param buffer optional direct buffer to write to (instead of queuing the data). + * @return number of read bytes (never 0) or a negative result_t code. */ - ssize_t recv(const long timeout, size_t maxCount = MAX_READ_SIZE) - { return m_device->recvBytes(timeout, maxCount); } + ssize_t recv(const long timeout, size_t maxCount = MAX_READ_SIZE, unsigned char* buffer=NULL) + { + ssize_t ret = m_device->recvBytes(timeout, maxCount, buffer); + if (buffer && ret>0 && m_logRaw == true && m_logRawFunc != NULL) + (*m_logRawFunc)(buffer[0], true); + return ret; + } /** * @brief fetch first byte from receive buffer. * @return first byte (raw) */ - unsigned char byte() { return m_device->getByte(); } + unsigned char byte(); /** * @brief get current size (bytes) of the receive buffer. @@ -253,9 +275,45 @@ public: */ ssize_t size() const { return m_device->sizeRecvBuffer(); } + /** + * @brief Get whether logging of raw data is enabled. + * @return whether logging of raw data is enabled. + */ + bool getLogRaw() { return m_logRaw; } + + /** + * @brief Enable or disable logging of raw data. + * @param logRawData true to enable logging of raw data, false to disable it. + */ + void setLogRaw(bool logRaw=true) { m_logRaw = logRaw; } + + /** + * @brief Get whether dumping of raw data to a file is enabled. + * @return whether dumping of raw data to a file is enabled. + */ + bool getDumpRaw() { return m_dumpRaw; } + + /** + * @brief Enable or disable dumping of raw data to a file. + * @param dumpRaw true to enable dumping of raw data to a file, false to disable it. + */ + void setDumpRaw(bool dumpRaw=true); + + /** + * @brief Set the name of the file to dump raw data to. + * @param dumpFile the name of the file to dump raw data to. + */ + void setDumpRawFile(const string& dumpFile); + + /** + * @brief Set the maximum size of a file to dump raw data to. + * @param maxSize the maximum size of a file to dump raw data to. + */ + void setDumpRawMaxSize(const long maxSize) { m_dumpRawMaxSize = maxSize; } + private: /** the device name */ - string m_deviceName; + const string m_deviceName; /** the device instance */ Device* m_device; @@ -263,6 +321,24 @@ private: /** true if device check is disabled */ bool m_noDeviceCheck; + /** whether logging of raw data is enabled. */ + bool m_logRaw; + + /** a function to call for logging raw data, or NULL. */ + void (*m_logRawFunc)(const unsigned char byte, bool received); + + /** whether dumping of raw data to a file is enabled. */ + bool m_dumpRaw; + + /** the name of the file to dump raw data to. */ + string m_dumpRawFile; + + /** the maximum size of @a m_dumpFile. */ + long m_dumpRawMaxSize; + + /** the @a ofstream for dumping raw data to. */ + ofstream m_dumpRawStream; + /** * @brief internal setter for device type. * @param type of device diff --git a/src/lib/ebus/result.cpp b/src/lib/ebus/result.cpp index ac44d46d..43f850e2 100644 --- a/src/lib/ebus/result.cpp +++ b/src/lib/ebus/result.cpp @@ -23,24 +23,33 @@ using namespace std; const char* getResultCode(result_t resultCode) { -cout << "DEBUG error code: " << static_cast(resultCode) << endl; switch (resultCode) { - case RESULT_ERR_SEND: return "ERR_SEND: send error"; - case RESULT_ERR_EXTRA_DATA: return "ERR_EXTRA_DATA: received bytes > sent bytes"; - case RESULT_ERR_NAK: return "ERR_NAK: NAK received"; - case RESULT_ERR_CRC: return "ERR_CRC: CRC error"; - case RESULT_ERR_ACK: return "ERR_ACK: ACK error"; - case RESULT_ERR_TIMEOUT: return "ERR_TIMEOUT: read timeout"; - case RESULT_ERR_SYN: return "ERR_SYN: SYN received"; - case RESULT_ERR_BUS_LOST: return "ERR_BUS_LOST: lost bus arbitration"; - case RESULT_ERR_ESC: return "ERR_ESC: invalid escape sequence received"; - case RESULT_ERR_INVALID_ARG: return "ERR_INVALID_ARG: invalid argument specified"; - case RESULT_ERR_DEVICE: return "ERR_DEVICE: generic device error"; - case RESULT_ERR_EOF: return "ERR_EOF: end of input reached"; - default: - if (resultCode >= 0) - return "success"; - return "ERR: unknown error code"; + case RESULT_OK: return "success"; + case RESULT_IN_ESC: return "success: escape sequence received"; + case RESULT_SYN: return "success: SYN received"; + 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: file/element not found or not readable"; + 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_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 value"; + 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_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"; + default: + if (resultCode >= 0) + return "success: unknown result code"; + return "ERR: unknown result code"; } } diff --git a/src/lib/ebus/result.h b/src/lib/ebus/result.h index df1af2d2..f419e31b 100644 --- a/src/lib/ebus/result.h +++ b/src/lib/ebus/result.h @@ -20,27 +20,32 @@ #ifndef LIBEBUS_RESULT_H_ #define LIBEBUS_RESULT_H_ -static const int RESULT_OK = 0; +static const int RESULT_OK = 0; // success -static const int RESULT_BUS_ACQUIRED = 1; // bus successfully acquired -static const int RESULT_DATA = 2; // some data received -static const int RESULT_SYN = 3; // regular SYN after message received -static const int RESULT_BUS_LOCKED = 4; // bus is locked for access -static const int RESULT_BUS_PRIOR_RETRY = 5; // retry to access bus -static const int RESULT_IN_ESC = 6; // start of escape sequence received +static const int RESULT_IN_ESC = 1; // start of escape sequence received +static const int RESULT_SYN = 2; // regular SYN after message received -static const int RESULT_ERR_SEND = -1; // send error -static const int RESULT_ERR_EXTRA_DATA = -2; // received bytes > sent bytes -static const int RESULT_ERR_NAK = -3; // NAK received -static const int RESULT_ERR_CRC = -4; // CRC error -static const int RESULT_ERR_ACK = -5; // ACK error -static const int RESULT_ERR_TIMEOUT = -6; // read timeout -static const int RESULT_ERR_SYN = -7; // SYN received -static const int RESULT_ERR_BUS_LOST = -8; // arbitration lost -static const int RESULT_ERR_ESC = -9; // invalid escape sequence received -static const int RESULT_ERR_INVALID_ARG = -10; // invalid argument -static const int RESULT_ERR_DEVICE = -11; // generic device error (usually fatal) -static const int RESULT_ERR_EOF = -12; // end of input reached +static const int RESULT_ERR_GENERIC_IO = -1; // generic I/O error (usually fatal) +static const int RESULT_ERR_DEVICE = -2; // generic device error (usually fatal) +static const int RESULT_ERR_SEND = -3; // send error +static const int RESULT_ERR_ESC = -4; // invalid escape sequence +static const int RESULT_ERR_TIMEOUT = -5; // read timeout + +static const int RESULT_ERR_NOTFOUND = -6; // file/element not found or not readable +static const int RESULT_ERR_EOF = -7; // end of input reached +static const int RESULT_ERR_INVALID_ARG = -8; // invalid argument +static const int RESULT_ERR_INVALID_NUM = -9; // invalid numeric argument +static const int RESULT_ERR_INVALID_POS = -10; // invalid position +static const int RESULT_ERR_OUT_OF_RANGE = -11; // argument value out of valid range +static const int RESULT_ERR_INVALID_PART = -12; // invalid part type value +static const int RESULT_ERR_MISSING_TYPE = -13; // missing data type +static const int RESULT_ERR_INVALID_LIST = -14; // invalid value list +static const int RESULT_ERR_DUPLICATE = -15; // duplicate entry + +static const int RESULT_ERR_BUS_LOST = -16; // arbitration lost +static const int RESULT_ERR_CRC = -17; // CRC error +static const int RESULT_ERR_ACK = -18; // ACK error +static const int RESULT_ERR_NAK = -19; // NAK received /** type for result code. */ typedef int result_t; diff --git a/src/lib/ebus/symbol.cpp b/src/lib/ebus/symbol.cpp index 730003f6..253236d4 100644 --- a/src/lib/ebus/symbol.cpp +++ b/src/lib/ebus/symbol.cpp @@ -48,7 +48,7 @@ static const unsigned char CRC_LOOKUP_TABLE[] = }; -SymbolString::SymbolString(const string str) +SymbolString::SymbolString(const string& str) //TODO use a factory method instead : m_unescapeState(0), m_crc(0) { // parse + escape @@ -60,7 +60,18 @@ SymbolString::SymbolString(const string str) push_back(m_crc, false, false); } -SymbolString::SymbolString(const string str, bool isEscaped) +SymbolString::SymbolString(const SymbolString& str) + : m_unescapeState(0), m_crc(0) +{ + // escape + for (size_t i = 0; i < str.size(); i++) { + push_back(str[i], false, true); + } + // add CRC + escape + push_back(m_crc, false, false); +} + +SymbolString::SymbolString(const string& str, bool isEscaped) : m_unescapeState(1), m_crc(0) { // parse + optionally unescape @@ -99,7 +110,7 @@ const string SymbolString::getDataStr(const bool unescape) return sstr.str(); } -int SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) +result_t SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) { if (m_unescapeState == 0) { // store escaped data if (isEscaped == false && value == ESC) { @@ -189,6 +200,49 @@ bool isMaster(unsigned char addr) { && ((addrLo == 0x0) || (addrLo == 0x1) || (addrLo == 0x3) || (addrLo == 0x7) || (addrLo == 0xF)); } +unsigned char getMasterNumber(unsigned char addr) { + unsigned char addrHi = (addr & 0xF0) >> 4; + unsigned char addrLo = (addr & 0x0F); + + unsigned char priority; + switch (addrLo) + { + case 0x0: + priority = 0; + break; + case 0x1: + priority = 1; + break; + case 0x3: + priority = 2; + break; + case 0x7: + priority = 3; + break; + case 0xF: + priority = 4; + break; + default: + return 0; + } + + switch (addrHi) + { + case 0x0: + return 5*0 + priority + 1; + case 0x1: + return 5*1 + priority + 2; + case 0x3: + return 5*2 + priority + 3; + case 0x7: + return 5*3 + priority + 4; + case 0xF: + return 5*4 + priority + 5; + default: + return 0; + } +} + bool isValidAddress(unsigned char addr, bool allowBroadcast) { return addr != SYN && addr != ESC && (allowBroadcast == true || addr != BROADCAST); } diff --git a/src/lib/ebus/symbol.h b/src/lib/ebus/symbol.h index e8b3b136..b90a51d8 100644 --- a/src/lib/ebus/symbol.h +++ b/src/lib/ebus/symbol.h @@ -20,6 +20,7 @@ #ifndef LIBEBUS_SYMBOL_H_ #define LIBEBUS_SYMBOL_H_ +#include "result.h" #include #include #include @@ -43,20 +44,24 @@ class SymbolString public: /** * @brief Creates a new unescaped empty instance. - * @param escaped whether to create an escaped instance. */ SymbolString() : m_unescapeState(1), m_crc(0) {} /** * @brief Creates a new escaped instance from an unescaped hex string and adds the calculated CRC. * @param str the unescaped hex string. */ - SymbolString(const string str); + SymbolString(const string& str); + /** + * @brief Creates a new escaped instance from an unescaped @a SymbolString and adds the calculated CRC. + * @param str the unescaped SymbolString. + */ + SymbolString(const SymbolString& str); /** * @brief Creates a new unescaped instance from a hex string. * @param isEscaped whether the hex string is escaped and shall be unescaped. * @param str the hex string. */ - SymbolString(const string str, const bool isEscaped); + SymbolString(const string& str, const bool isEscaped); /** * @brief Returns the symbols as hex string. * @param unescape whether to unescape an escaped instance. @@ -80,7 +85,20 @@ public: * @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; } + bool operator==(SymbolString& other) { + return m_unescapeState==other.m_unescapeState && m_data==other.m_data; + /*bool ret = m_unescapeState==other.m_unescapeState && m_data==other.m_data; + for (int i=0; i(m_data[i])<<" "; + } + cout<<"["<(m_unescapeState)<<"]"; + cout<<(ret?" == ":" != "); + for (int i=0; i(other.m_data[i])<<" "; + } + cout<<"["<(other.m_unescapeState)<<"]"<true if the specified address is a master address. */ bool isMaster(unsigned char addr); /** - * Returns whether the address is a valid bus address. + * @brief Returns the number of the master if the address is a valid bus address. + * @param addr the bus address. + * @return the number of the master if the address is a valid bus address (1 to 25), or 0. + */ +unsigned char getMasterNumber(unsigned char addr); + +/** + * @brief Returns whether the address is a valid bus address. * @param addr the address to check. * @param allowBroadcast whether to also allow @a addr to be the broadcast address (default true). * @return true if the specified address is a valid bus address. diff --git a/src/lib/ebus/test/Makefile.am b/src/lib/ebus/test/Makefile.am old mode 100644 new mode 100755 index 55dff9aa..ac8369f7 --- a/src/lib/ebus/test/Makefile.am +++ b/src/lib/ebus/test/Makefile.am @@ -1,15 +1,13 @@ AM_CXXFLAGS = -fpic \ -Wall \ -Wextra \ - -I$(top_srcdir)/src/lib/ebus + -I$(top_srcdir)/src/lib/ebus \ + -I$(top_srcdir)/src/lib/utils noinst_PROGRAMS = test_port \ test_symbol \ test_data \ - test_commands \ - test_configfile \ - test_decode \ - test_encode + test_message test_port_SOURCES = test_port.cpp test_port_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a @@ -20,17 +18,8 @@ test_symbol_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a test_data_SOURCES = test_data.cpp test_data_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a -test_commands_SOURCES = test_commands.cpp -test_commands_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a - -test_configfile_SOURCES = test_configfile.cpp -test_configfile_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a - -test_decode_SOURCES = test_decode.cpp -test_decode_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a - -test_encode_SOURCES = test_encode.cpp -test_encode_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a +test_message_SOURCES = test_message.cpp +test_message_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a distclean-local: -rm -f Makefile.in diff --git a/src/lib/ebus/test/test_commands.cpp b/src/lib/ebus/test/test_commands.cpp deleted file mode 100644 index 6dc0a981..00000000 --- a/src/lib/ebus/test/test_commands.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "configfile.h" -#include "commands.h" -#include -#include -#include -#include -#include - -using namespace std; - -// will be part of cfg csv class -void readCSV(istream& is, Commands& commands){ - string line; - - // read lines - while (getline(is, line) != 0) { - vector row; - string column; - int count; - - count = 0; - - istringstream stream(line); - - // walk through columns - while (getline(stream, column, ';') != 0) { - row.push_back(column); - count++; - } - - commands.addCommand(row); - } -} - -int main() -{ - Commands* commands = ConfigCommands("test", ft_csv).getCommands(); - cout << "Commands: " << commands->sizeCmdDB() << endl; - - //~ string data("g ci password pin1"); - string data("s vwxmk DesiredTemp"); - - int index = commands->findCommand(data); - cout << "found at index: " << index << endl; - - // prepare data - string token; - istringstream stream(data); - vector cmd; - - // split stream - while (getline(stream, token, ' ') != 0) - cmd.push_back(token); - - //~ Command* command = new Command(index, (*commands)[index], "ff15b509030d2c0035000401000000cf00"); - Command* command = new Command(index, (*commands)[index], "19.0"); - - //~ string result = command->calcResult(cmd); - string result = command->calcData(); - cout << "result: " << result << endl; - - delete command; - - return 0; -} - - diff --git a/src/lib/ebus/test/test_configfile.cpp b/src/lib/ebus/test/test_configfile.cpp deleted file mode 100644 index df3d2ce9..00000000 --- a/src/lib/ebus/test/test_configfile.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "configfile.h" -#include -#include -#include -#include -#include - -using namespace std; - -int main() { - - string dir("test"); - ConfigCommands config(dir, ft_csv); - - Commands* commands = config.getCommands(); - - cout << "size: " << commands->sizeCmdDB() << endl; - - commands->findCommand("g ci Password"); - - cout << (*commands)[0][0] << endl; - - delete commands; - - return 0; -} - - diff --git a/src/lib/ebus/test/test_data.cpp b/src/lib/ebus/test/test_data.cpp index 0591bf6b..4096b032 100644 --- a/src/lib/ebus/test/test_data.cpp +++ b/src/lib/ebus/test/test_data.cpp @@ -68,6 +68,9 @@ int main() {"x;;bti", "00:00:00", "10fe070003000000", "00", ""}, {"x;;bti", "23:59:59", "10fe070003595923", "00", ""}, {"x;;bti", "", "10fe070003605923", "00", "rw"}, + {"x;;hti", "21:04:58", "10fe07000315043a", "00", ""}, + {"x;;vti", "21:04:58", "10fe0700033a0415", "00", ""}, + {"x;;vti", "-:-:-", "10fe070003636363", "00", ""}, {"x;;htm", "21:04", "10fe0700021504", "00", ""}, {"x;;htm", "00:00", "10fe0700020000", "00", ""}, {"x;;htm", "23:59", "10fe070002173b", "00", ""}, @@ -77,7 +80,7 @@ int main() {"x;;ttm", "22:40", "10fe07000188", "00", ""}, {"x;;ttm", "00:00", "10fe07000100", "00", ""}, {"x;;ttm", "23:50", "10fe0700018f", "00", ""}, - {"x;;ttm", "24:00", "10fe07000190", "00", ""}, + {"x;;ttm", "-:-", "10fe07000190", "00", ""}, {"x;;ttm", "", "10fe07000191", "00", "rw"}, {"x;;bdy", "Mon", "10fe07000300", "00", ""}, {"x;;bdy", "Sun", "10fe07000306", "00", ""}, @@ -94,7 +97,7 @@ int main() {"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", "2;3","1025ffff0103", "0102", ""}, + {"x;s;uch;;;;y;m;uch", "3;2","1025ffff0103", "0102", ""}, {"x;;uch", "38", "10feffff0126", "00", ""}, {"x;;uch", "0", "10feffff0100", "00", ""}, {"x;;uch", "254", "10feffff01fe", "00", ""}, @@ -165,7 +168,7 @@ int main() {"x;;bi3:2;0=off,1=on","off","10feffff0100", "00", ""}, {"x;;uch;1=test,2=high,3=off,4=on","on","10feffff0104", "00", ""}, {"x;s;uch","3","1050ffff00", "0103", ""}, - {"x;;d2b;;°C;Aussentemperatur","x=18.004 °C [Aussentemperatur]","10fe0700090112", "00", "v"}, + {"x;;d2b;;�C;Aussentemperatur","x=18.004 �C [Aussentemperatur]","10fe0700090112", "00", "v"}, {"x;;bti;;;;y;;bda;;;;z;;bdy", "21:04:58;26.10.2014;Sun","10fe0700085804212610061406", "00", ""}, // combination {"x;;bi3;;;;y;;bi5", "1;-", "10feffff0108", "00", ""}, // bit combination {"x;;bi3;;;;y;;bi5", "1;1", "10feffff0128", "00", ""}, // bit combination @@ -173,7 +176,7 @@ int main() {"x;;bi3;;;;y;;bi5", "-;-", "10feffff0100", "00", ""}, // bit combination {"x;;bi3;;;;y;;bi7;;;;t;;uch", "-;-;9","10feffff020009", "00", ""}, // bit combination {"x;;bi6:2;;;;y;;bi0:2;;;;t;;uch", "2;1;9","10feffff03800109", "00", ""}, // bit combination - {"temp;;d2b;;°C;Aussentemperatur","","", "", "t"}, // template with relative pos + {"temp;;d2b;;�C;Aussentemperatur","","", "", "t"}, // template with relative pos {"x;;temp","18.004","10fe0700020112", "00", ""}, // reference to template {"relrel;;d2b;;;;y;;d1c","","", "", "t"}, // template struct with relative pos {"x;;relrel","18.004;9.5","10fe070003011213", "00", ""}, // reference to template struct @@ -181,7 +184,7 @@ int main() {"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 }; - map templates; + DataFieldTemplates* templates = new DataFieldTemplates(); DataField* fields = NULL; for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; @@ -233,21 +236,23 @@ int main() if (isTemplate) { // store new template string name = fields->getName(); - map::iterator current = templates.find(name); - if (current == templates.end()) { - templates[name] = fields; - } else { - delete current->second; - current->second = fields; + result = templates->add(fields, true); + if (result == RESULT_OK) { + fields = NULL; + cout << " store template OK" << endl; } - fields = NULL; + else + cout << " store template error: " << getResultCode(result) << endl; continue; } ostringstream output; SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); - result = fields->read(mstr, 0, sstr, 0, output, verbose); + result = fields->read(pt_masterData, mstr, 0, output, false, verbose); + if (result == RESULT_OK) { + result = fields->read(pt_slaveData, sstr, 0, output, output.str().empty() == false, verbose); + } if (failedRead == true) if (result == RESULT_OK) cout << " failed read " << fields->getName() << " >" @@ -266,7 +271,9 @@ int main() if (verbose == false) { istringstream input(expectStr); - result = fields->write(input, writeMstr, 0, writeSstr, 0); + result = fields->write(input, pt_masterData, writeMstr, 0); + if (result == RESULT_OK) + result = fields->write(input, pt_slaveData, writeSstr, 0); if (failedWrite == true) { if (result == RESULT_OK) cout << " failed write " << fields->getName() << " >" @@ -288,8 +295,7 @@ int main() fields = NULL; } - for (map::iterator it = templates.begin(); it != templates.end(); it++) - delete it->second; + delete templates; return 0; diff --git a/src/lib/ebus/test/test_decode.cpp b/src/lib/ebus/test/test_decode.cpp deleted file mode 100644 index 346bda96..00000000 --- a/src/lib/ebus/test/test_decode.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "decode.h" -#include -#include - -using namespace std; - -int main() -{ - Decode* help_dec = NULL; - - cout << endl; - - // HEX - { - const char* hex[] = {"53706569636865722020"}; - for (size_t i = 0; i < sizeof(hex)/sizeof(hex[0]); i++) { - help_dec = new DecodeHEX(hex[i]); - cout << "DecodeHEX: " << setw(20) << hex[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // UCH - { - const char* uch[] = {"00", "01", "7f", "80", "fe", "ff", "a1"}; - for (size_t i = 0; i < sizeof(uch)/sizeof(uch[0]); i++) { - help_dec = new DecodeUCH(uch[i], "1.0"); - cout << "DecodeUCH: " << setw(20) << uch[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // SCH - { - const char* sch[] = {"00", "01", "7f", "80", "fe", "ff", "a1"}; - for (size_t i = 0; i < sizeof(sch)/sizeof(sch[0]); i++) { - help_dec = new DecodeSCH(sch[i], "1.0"); - cout << "DecodeSCH: " << setw(20) << sch[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // UIN - { - const char* uin[] = {"0000", "0001", "7fff", "8000", "fffe", "ffff", "a1b2"}; - for (size_t i = 0; i < sizeof(uin)/sizeof(uin[0]); i++) { - help_dec = new DecodeUIN(uin[i], "1.0"); - cout << "DecodeUIN: " << setw(20) << uin[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // SIN - { - const char* sin[] = {"0000", "0001", "7fff", "8000", "fffe", "ffff", "a1b2"}; - for (size_t i = 0; i < sizeof(sin)/sizeof(sin[0]); i++) { - help_dec = new DecodeSIN(sin[i], "1.0"); - cout << "DecodeSIN: " << setw(20) << sin[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // ULG - { - const char* ulg[] = {"00000000", "00000001", "7fffffff", "80000000", "fffffffe", "ffffffff", "a1b2c3d4"}; - for (size_t i = 0; i < sizeof(ulg)/sizeof(ulg[0]); i++) { - help_dec = new DecodeULG(ulg[i], "1.0"); - cout << "DecodeULG: " << setw(20) << ulg[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // SLG - { - const char* slg[] = {"00000000", "00000001", "7fffffff", "80000000", "fffffffe", "ffffffff", "a1b2c3d4"}; - for (size_t i = 0; i < sizeof(slg)/sizeof(slg[0]); i++) { - help_dec = new DecodeSLG(slg[i], "1.0"); - cout << "DecodeSLG: " << setw(20) << slg[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // FLT - { - const char* flt[] = {"0000", "081b", "2532", "2689", "0851"}; - for (size_t i = 0; i < sizeof(flt)/sizeof(flt[0]); i++) { - help_dec = new DecodeFLT(flt[i], "1.0"); - cout << "DecodeFLT: " << setw(20) << flt[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // STR - { - const char* str[] = {"53706569636865722020", "5644363030" }; - for (size_t i = 0; i < sizeof(str)/sizeof(str[0]); i++) { - help_dec = new DecodeSTR(str[i]); - cout << "DecodeSTR: " << setw(20) << str[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // BCD - { - const char* bcd[] = {"00", "01", "02", "03", "12", "99"}; - for (size_t i = 0; i < sizeof(bcd)/sizeof(bcd[0]); i++) { - help_dec = new DecodeBCD(bcd[i], "1.0"); - cout << "DecodeBCD: " << setw(20) << bcd[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // D1B - { - const char* d1b[] = {"00", "01", "7f", "81", "80"}; - for (size_t i = 0; i < sizeof(d1b)/sizeof(d1b[0]); i++) { - help_dec = new DecodeD1B(d1b[i], "1.0"); - cout << "DecodeD1B: " << setw(20) << d1b[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // D1C - { - const char* d1c[] = {"00", "64", "c8"}; - for (size_t i = 0; i < sizeof(d1c)/sizeof(d1c[0]); i++) { - help_dec = new DecodeD1C(d1c[i], "1.0"); - cout << "DecodeD1C: " << setw(20) << d1c[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // D2B - { - const char* d2b[] = {"0000", "0100", "ffff", "00ff", "0080", "0180", "ff7f"}; - for (size_t i = 0; i < sizeof(d2b)/sizeof(d2b[0]); i++) { - help_dec = new DecodeD2B(d2b[i], "1.0"); - cout << "DecodeD2B: " << setw(20) << d2b[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // D2C - { - const char* d2c[] = {"0000", "0100", "ffff", "f0ff", "0080", "0180", "ff7f"}; - for (size_t i = 0; i < sizeof(d2c)/sizeof(d2c[0]); i++) { - help_dec = new DecodeD2C(d2c[i], "1.0"); - cout << "DecodeD2C: " << setw(20) << d2c[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // BDA - { - const char* bda[] = {"171113", "220901"}; - for (size_t i = 0; i < sizeof(bda)/sizeof(bda[0]); i++) { - help_dec = new DecodeBDA(bda[i]); - cout << "DecodeBDA: " << setw(20) << bda[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // HDA - { - const char* hda[] = {"010101", "1f0c1b"}; - for (size_t i = 0; i < sizeof(hda)/sizeof(hda[0]); i++) { - help_dec = new DecodeHDA(hda[i]); - cout << "DecodeHDA: " << setw(20) << hda[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // BTI - { - const char* bti[] = {"010101", "174209", "235959"}; - for (size_t i = 0; i < sizeof(bti)/sizeof(bti[0]); i++) { - help_dec = new DecodeBTI(bti[i]); - cout << "DecodeBTI: " << setw(20) << bti[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // HTI - { - const char* hti[] = {"010101", "112a09", "173b3b"}; - for (size_t i = 0; i < sizeof(hti)/sizeof(hti[0]); i++) { - help_dec = new DecodeHTI(hti[i]); - cout << "DecodeHTI: " << setw(20) << hti[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // BDY - { - const char* bdy[] = {"01", "03", "06", "07"}; - for (size_t i = 0; i < sizeof(bdy)/sizeof(bdy[0]); i++) { - help_dec = new DecodeBDY(bdy[i]); - cout << "DecodeBDY: " << setw(20) << bdy[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // HDY - { - const char* hdy[] = {"01", "03", "07", "08"}; - for (size_t i = 0; i < sizeof(hdy)/sizeof(hdy[0]); i++) { - help_dec = new DecodeHDY(hdy[i]); - cout << "DecodeHDY: " << setw(20) << hdy[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - // TTM - { - const char* ttm[] = {"00", "23", "4f", "90"}; - for (size_t i = 0; i < sizeof(ttm)/sizeof(ttm[0]); i++) { - help_dec = new DecodeTTM(ttm[i]); - cout << "DecodeTTM: " << setw(20) << ttm[i] << " = " << help_dec->decode() << endl; - - delete help_dec; - } - - cout << endl; - } - - return 0; -} - - diff --git a/src/lib/ebus/test/test_encode.cpp b/src/lib/ebus/test/test_encode.cpp deleted file mode 100644 index 3e3bc887..00000000 --- a/src/lib/ebus/test/test_encode.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) Roland Jax 2012-2014 - * - * This file is part of ebusd. - * - * ebusd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ebusd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ebusd. If not, see http://www.gnu.org/licenses/. - */ - -#include "encode.h" -#include -#include - -using namespace std; - -int main() -{ - Encode* help_enc = NULL; - - cout << endl; - - // HEX - { - const char* hex[] = {"53 70 65 69 63 68 65 72 20 20"}; - for (size_t i = 0; i < sizeof(hex)/sizeof(hex[0]); i++) { - help_enc = new EncodeHEX(hex[i]); - cout << "EncodeHEX: " << setw(20) << hex[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // UCH - { - const char* uch[] = {"0", "1", "127", "128", "254", "255", "161"}; - for (size_t i = 0; i < sizeof(uch)/sizeof(uch[0]); i++) { - help_enc = new EncodeUCH(uch[i], "1.0"); - cout << "EncodeUCH: " << setw(20) << uch[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // SCH - { - const char* sch[] = {"0", "1", "127", "-128", "-2", "-1", "-95"}; - for (size_t i = 0; i < sizeof(sch)/sizeof(sch[0]); i++) { - help_enc = new EncodeSCH(sch[i], "1.0"); - cout << "EncodeSCH: " << setw(20) << sch[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // UIN - { - const char* uin[] = {"0", "1", "32767", "32768", "65534", "65535", "41394"}; - for (size_t i = 0; i < sizeof(uin)/sizeof(uin[0]); i++) { - help_enc = new EncodeUIN(uin[i], "1.0"); - cout << "EncodeUIN: " << setw(20) << uin[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // SIN - { - const char* sin[] = {"0", "1", "32767", "-32768", "-2", "-1", "-24142"}; - for (size_t i = 0; i < sizeof(sin)/sizeof(sin[0]); i++) { - help_enc = new EncodeSIN(sin[i], "1.0"); - cout << "EncodeSIN: " << setw(20) << sin[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // ULG - { - const char* ulg[] = {"0", "1", "2147483647", "2147483648", "4294967294", "4294967295", "2712847316"}; - for (size_t i = 0; i < sizeof(ulg)/sizeof(ulg[0]); i++) { - help_enc = new EncodeULG(ulg[i], "1.0"); - cout << "EncodeULG: " << setw(20) << ulg[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // SLG - { - const char* slg[] = {"0", "1", "2147483647", "-2147483648", "-2", "-1", "-1582119980"}; - for (size_t i = 0; i < sizeof(slg)/sizeof(slg[0]); i++) { - help_enc = new EncodeSLG(slg[i], "1.0"); - cout << "EncodeSLG: " << setw(20) << slg[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // FLT - { - const char* flt[] = {"0.000", "2.075", "9.522", "9.865", "2.129"}; - for (size_t i = 0; i < sizeof(flt)/sizeof(flt[0]); i++) { - help_enc = new EncodeFLT(flt[i], "1.0"); - cout << "EncodeFLT: " << setw(20) << flt[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // STR - { - const char* str[] = {"Speicher ", "VD600" }; - for (size_t i = 0; i < sizeof(str)/sizeof(str[0]); i++) { - help_enc = new EncodeSTR(str[i]); - cout << "EncodeSTR: " << setw(20) << str[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // BCD - { - const char* bcd[] = {"0", "1", "2", "3", "12", "99"}; - for (size_t i = 0; i < sizeof(bcd)/sizeof(bcd[0]); i++) { - help_enc = new EncodeBCD(bcd[i], "1.0"); - cout << "EncodeBCD: " << setw(20) << bcd[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // D1B - { - const char* d1b[] = {"00", "01", "127", "-127", "-128"}; - for (size_t i = 0; i < sizeof(d1b)/sizeof(d1b[0]); i++) { - help_enc = new EncodeD1B(d1b[i], "1.0"); - cout << "EncodeD1B: " << setw(20) << d1b[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // D1C - { - const char* d1c[] = {"0", "50", "100"}; - for (size_t i = 0; i < sizeof(d1c)/sizeof(d1c[0]); i++) { - help_enc = new EncodeD1C(d1c[i], "1.0"); - cout << "EncodeD1C: " << setw(20) << d1c[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // D2B - { - const char* d2b[] = {"0", "0.00390625", "-0.00390625", "-1", "-128", "-127.99609375", "127.99609375"}; - for (size_t i = 0; i < sizeof(d2b)/sizeof(d2b[0]); i++) { - help_enc = new EncodeD2B(d2b[i], "1.0"); - cout << "EncodeD2B: " << setw(20) << d2b[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // D2C - { - const char* d2c[] = {"0", "0.0625", "-0.0625", "-1", "-2048", "-2047.9375", "2047.9375"}; - for (size_t i = 0; i < sizeof(d2c)/sizeof(d2c[0]); i++) { - help_enc = new EncodeD2C(d2c[i], "1.0"); - cout << "EncodeD2C: " << setw(20) << d2c[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // BDA - { - const char* bda[] = {"17.11.2013", "22.09.2001"}; - for (size_t i = 0; i < sizeof(bda)/sizeof(bda[0]); i++) { - help_enc = new EncodeBDA(bda[i]); - cout << "EncodeBDA: " << setw(20) << bda[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // HDA - { - const char* hda[] = {"01.01.2001", "31.12.2027"}; - for (size_t i = 0; i < sizeof(hda)/sizeof(hda[0]); i++) { - help_enc = new EncodeHDA(hda[i]); - cout << "EncodeHDA: " << setw(20) << hda[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // BTI - { - const char* bti[] = {"01:01:01", "17:42:09", "23:59:59"}; - for (size_t i = 0; i < sizeof(bti)/sizeof(bti[0]); i++) { - help_enc = new EncodeBTI(bti[i]); - cout << "EncodeBTI: " << setw(20) << bti[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // HTI - { - const char* hti[] = {"01:01:01", "17:42:09", "23:59:59"}; - for (size_t i = 0; i < sizeof(hti)/sizeof(hti[0]); i++) { - help_enc = new EncodeHTI(hti[i]); - cout << "EncodeHTI: " << setw(20) << hti[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // BDY - { - const char* bdy[] = {"Tue", "Thu", "Sun", "Err"}; - for (size_t i = 0; i < sizeof(bdy)/sizeof(bdy[0]); i++) { - help_enc = new EncodeBDY(bdy[i]); - cout << "EncodeBDY: " << setw(20) << bdy[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - - // HDY - { - const char* hdy[] = {"Mon", "Wed", "Sun", "Err"}; - for (size_t i = 0; i < sizeof(hdy)/sizeof(hdy[0]); i++) { - help_enc = new EncodeHDY(hdy[i]); - cout << "EncodeHDY: " << setw(20) << hdy[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - // TTM - { - const char* ttm[] = {"00:00", "05:50", "13:10", "24:00"}; - for (size_t i = 0; i < sizeof(ttm)/sizeof(ttm[0]); i++) { - help_enc = new EncodeTTM(ttm[i]); - cout << "EncodeTTM: " << setw(20) << ttm[i] << " = " << help_enc->encode() << endl; - - delete help_enc; - } - - cout << endl; - } - - return 0; -} - - diff --git a/src/lib/ebus/test/test_message.cpp b/src/lib/ebus/test/test_message.cpp index 58000e5b..dbd69e08 100644 --- a/src/lib/ebus/test/test_message.cpp +++ b/src/lib/ebus/test/test_message.cpp @@ -19,6 +19,7 @@ #include "message.h" #include +#include #include using namespace std; @@ -40,42 +41,37 @@ void verify(bool expectFailMatch, string type, string input, << gotStr << "<, expected >" << expectStr << "<" << endl; } -void printErrorPos(vector::iterator it, const vector::iterator end, vector::iterator pos) -{ - cout << "Errroneous item is here:" << endl; - bool first = true; - int cnt = 0; - if (pos > it) - pos--; - while (it != end) { - if (first == true) - first = false; - else { - cout << ';'; - if (it <= pos) { - cnt++; - } - } - if (it < pos) { - cnt += (*it).length(); - } - cout << (*it++); - } - cout << endl; - cout << setw(cnt) << " " << setw(0) << "^" << endl; -} - int main() { // message= [type];class;name;[comment];[QQ];ZZ;PBSB;fields... // field= name;[pos];type[;[divisor|values][;[unit][;[comment]]]] string checks[][5] = { // "message", "flags" - {";;first;;;fe;0700;x;;bda", "26.10.2014", "fffe0700042610061451", "00", ""}, - {"w;;first;;;15;b5090400;date;;bda", "26.10.2014", "ff15b5090604002610061445", "00", ""}, + {"u;;first;;;fe;0700;;x;;bda", "26.10.2014", "fffe0700042610061451", "00", "p"}, + {"w;;first;;;15;b509;0400;date;;bda", "26.10.2014", "ff15b5090604002610061445", "00", "m"}, + {"r;ehp;time;;;08;b509;0d2800;;;time", "15:00:17", "ff08b509030d2800ea", "0311000f00", "m"}, + {"r;ehp;date;;;08;b509;0d2900;;;hda:3", "23.11.2014", "ff08b509030d290071", "03170b0e5a", "m"}, + {"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"}, + {"","55.50;ok","1025b50903290000","050000780300",""}, + {"","no;25","10feb505042700190023","",""}, }; - map templates; + DataFieldTemplates* templates = new DataFieldTemplates(); + result_t result = templates->readFromFile("_types.csv"); + if (result == RESULT_OK) + cout << "read templates OK" << endl; + else + cout << "read templates error: " << getResultCode(result) << endl; + + MessageMap* messages = new MessageMap(); + result = messages->readFromFile("neu-ehp00.csv", templates); + if (result == RESULT_OK) + cout << "read messages OK" << endl; + else + cout << "read messages error: " << getResultCode(result) << endl; + Message* message = NULL; + Message* deleteMessage = NULL; for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { string check[5] = checks[i]; istringstream isstr(check[0]); @@ -83,6 +79,7 @@ int main() SymbolString mstr = SymbolString(check[2], false); SymbolString sstr = SymbolString(check[3], false); string flags = check[4]; + bool dontMap = flags.find('m') != string::npos; bool failedCreate = flags.find('c') != string::npos; bool failedPrepare = flags.find('p') != string::npos; bool failedPrepareMatch = flags.find('P') != string::npos; @@ -92,63 +89,108 @@ int main() while (getline(isstr, item, ';') != 0) entries.push_back(item); - if (message != NULL) { - delete message; - message = NULL; + if (deleteMessage != NULL) { + delete deleteMessage; + deleteMessage = NULL; } - vector::iterator it = entries.begin(); - result_t result = Message::create(it, entries.end(), templates, message); - - if (failedCreate == true) { - if (result == RESULT_OK) - cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; + if (entries.size() == 0) { + message = messages->find(mstr); + if (message == NULL) { + cout << "\"" << check[2] << "\": find error: NULL" << endl; + continue; + } + cout << "\"" << check[2] << "\": find OK" << endl; + } else { + vector::iterator it = entries.begin(); + result = Message::create(it, entries.end(), NULL, templates, deleteMessage); + if (failedCreate == true) { + 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(entries.begin(), entries.end(), it); + continue; + } + if (deleteMessage == NULL) { + cout << "\"" << check[0] << "\": create error: NULL" << endl; + continue; + } + if (it != entries.end()) { + cout << "\"" << check[0] << "\": create error: trailing input" << endl; + continue; + } + cout << "\"" << check[0] << "\": create OK" << endl; + if (dontMap == false) { + result_t result = messages->add(deleteMessage); + if (result != RESULT_OK) { + cout << "\"" << check[0] << "\": add error: " + << getResultCode(result) << endl; + continue; + } + cout << " map OK" << endl; + message = deleteMessage; + deleteMessage = NULL; + Message* foundMessage = messages->find(mstr); + if (foundMessage == message) + cout << " find OK" << endl; + else if (foundMessage == NULL) + cout << " find error: NULL" << endl; + else + cout << " find error: different" << endl; + } else - cout << "\"" << check[0] << "\": failed create OK" << endl; - continue; + message = deleteMessage; } - if (result != RESULT_OK) { - cout << "\"" << check[0] << "\": create error: " - << getResultCode(result) << endl; - printErrorPos(entries.begin(), entries.end(), it); - continue; - } - if (message == NULL) { - cout << "\"" << check[0] << "\": create error: NULL" << endl; - continue; - } - if (it != entries.end()) { - cout << "\"" << check[0] << "\": create error: trailing input" << endl; - continue; - } - cout << "\"" << check[0] << "\": create OK" << endl; - istringstream input(inputStr); SymbolString writeMstr = SymbolString(); - result = message->prepare(0xff, writeMstr, input); - if (failedPrepare == true) { + if (message->isPassive() == true) { + ostringstream output; + result = message->decode(pt_masterData, mstr, output); if (result == RESULT_OK) - cout << "\"" << check[0] << "\": failed prepare error: unexpectedly succeeded" << endl; - else - cout << "\"" << check[0] << "\": failed prepare OK" << endl; - continue; + result = message->decode(pt_slaveData, sstr, output, output.str().empty() == false); + if (result != RESULT_OK) { + cout << " \"" << inputStr << "\": decode error: " + << getResultCode(result) << endl; + continue; + } + cout << " \"" << inputStr << "\": decode OK" << endl; + + bool match = inputStr == output.str(); + verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str()); + } else { + result = message->prepareMaster(0xff, writeMstr, input); + if (failedPrepare == true) { + 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; + + bool match = writeMstr==mstr; + verify(failedPrepareMatch, "prepare", inputStr, match, mstr.getDataStr(), writeMstr.getDataStr()); } - - if (result != RESULT_OK) { - cout << " prepare >" << inputStr << "< error: " - << getResultCode(result) << endl; - continue; - } - cout << " prepare >" << inputStr << "< OK" << endl; - - bool match = writeMstr==mstr; - verify(failedPrepareMatch, "prepare", inputStr, match, mstr.getDataStr(), writeMstr.getDataStr()); - - delete message; - message = NULL; } - for (map::iterator it = templates.begin(); it != templates.end(); it++) - delete it->second; + if (deleteMessage != NULL) { + delete deleteMessage; + deleteMessage = NULL; + } + + delete templates; + delete messages; return 0; diff --git a/src/lib/ebus/test/test_port.cpp b/src/lib/ebus/test/test_port.cpp index ebdd8ea4..853f03db 100644 --- a/src/lib/ebus/test/test_port.cpp +++ b/src/lib/ebus/test/test_port.cpp @@ -26,7 +26,7 @@ using namespace std; int main () { string dev("/dev/ttyUSB20"); - Port port(dev, true); + Port port(dev, true, false, NULL, false, "", 1); port.open(); diff --git a/src/lib/ebus/test/test_symbol.cpp b/src/lib/ebus/test/test_symbol.cpp index b91c3f6e..a3858469 100644 --- a/src/lib/ebus/test/test_symbol.cpp +++ b/src/lib/ebus/test/test_symbol.cpp @@ -30,17 +30,17 @@ int main () std::string gotStr = sstr.getDataStr(false), expectStr = "10feb5050427a90015a90177"; if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) - std::cout << "ctor escaped successful." << std::endl; + std::cout << "ctor escaped OK" << std::endl; else - std::cout << "ctor escaped invalid: got " << gotStr << ", expected " + std::cout << "ctor escaped error: got " << gotStr << ", expected " << expectStr << std::endl; unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; if (gotCrc == expectCrc) - std::cout << "CRC successful." << std::endl; + std::cout << "CRC OK" << std::endl; else - std::cout << "CRC invalid: got 0x" << std::nouppercase << std::setw(2) + std::cout << "CRC error: got 0x" << std::nouppercase << std::setw(2) << std::hex << std::setfill('0') << static_cast(gotCrc) << ", expected 0x" << std::nouppercase << std::setw(2) << std::hex @@ -50,9 +50,9 @@ int main () gotStr = sstr.getDataStr(), expectStr = "10feb5050427a915aa77"; if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) - std::cout << "unescape successful." << std::endl; + std::cout << "unescape OK" << std::endl; else - std::cout << "unescape invalid: got " << gotStr << ", expected " + std::cout << "unescape error: got " << gotStr << ", expected " << expectStr << std::endl; sstr = SymbolString("10feb5050427a90015a90177", true); @@ -60,9 +60,9 @@ int main () gotStr = sstr.getDataStr(); if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) - std::cout << "ctor unescaped successful." << std::endl; + std::cout << "ctor unescaped OK" << std::endl; else - std::cout << "ctor unescaped invalid: got " << gotStr << ", expected " + std::cout << "ctor unescaped error: got " << gotStr << ", expected " << expectStr << std::endl; return 0; diff --git a/src/lib/utils/Makefile.am b/src/lib/utils/Makefile.am old mode 100644 new mode 100755 diff --git a/src/lib/utils/appl.cpp b/src/lib/utils/appl.cpp index 92028def..6a62c344 100644 --- a/src/lib/utils/appl.cpp +++ b/src/lib/utils/appl.cpp @@ -160,6 +160,9 @@ void Appl::setOptVal(const char* option, const string value, DataType datatype) case dt_bool: m_optvals[option] = true; break; + case dt_hex: + m_optvals[option] = strtol(value.c_str(), NULL, 16); + break; case dt_int: m_optvals[option] = strtol(value.c_str(), NULL, 10); break; diff --git a/src/lib/utils/appl.h b/src/lib/utils/appl.h index a03e5e3a..1804049e 100644 --- a/src/lib/utils/appl.h +++ b/src/lib/utils/appl.h @@ -33,7 +33,8 @@ using namespace std; enum DataType { dt_none, /*!< default for __text_only__ */ dt_bool, /*!< boolean */ - dt_int, /*!< integer */ + dt_hex, /*!< hex integer */ + dt_int, /*!< dec integer */ dt_long, /*!< long */ dt_float, /*!< float */ dt_string /*!< string */ diff --git a/src/lib/utils/logger.cpp b/src/lib/utils/logger.cpp index 70b410f1..04442631 100644 --- a/src/lib/utils/logger.cpp +++ b/src/lib/utils/logger.cpp @@ -100,7 +100,7 @@ void LogSink::addMessage(const LogMessage& message) m_logQueue.add((tmp)); } -void* LogSink::run() +void LogSink::run() { while (1) { LogMessage* message = m_logQueue.remove(); @@ -111,13 +111,12 @@ void* LogSink::run() write(*message); delete message; } - return NULL; + return; } write(*message); delete message; } - return NULL; } @@ -187,7 +186,7 @@ Logger& Logger::operator-=(const LogSink* sink) void Logger::log(const int area, const int level, const string& data, ...) { - if (m_running == true) { + if (isRunning() == true) { char* tmp; va_list ap; va_start(ap, data); @@ -203,11 +202,11 @@ void Logger::log(const int area, const int level, const string& data, ...) } -void* Logger::run() +void Logger::run() { - m_running = true; + bool running = true; - while (m_running == true) { + do { LogMessage* message = m_logQueue.remove(); sinkCI_t iter = m_sinks.begin(); @@ -215,28 +214,27 @@ void* Logger::run() for (; iter != m_sinks.end(); ++iter) { if (*iter != 0) { - if (((*iter)->getAreas() & message->getArea() + if ((((*iter)->getAreas() & message->getArea()) != 0 && (*iter)->getLevel() >= message->getLevel()) && message->isRunning() == true) { (*iter)->addMessage(*message); } else if (message->isRunning() == false) { (*iter)->addMessage(*message); - m_running = false; + running = false; } - } } delete message; - } - return NULL; + } while (running == true); } void Logger::stop() { m_logQueue.add(new LogMessage(LogMessage(bas, error, "", false))); usleep(100000); + Thread::stop(); } diff --git a/src/lib/utils/logger.h b/src/lib/utils/logger.h index b5bbabb9..daf2d75f 100644 --- a/src/lib/utils/logger.h +++ b/src/lib/utils/logger.h @@ -143,9 +143,8 @@ public: /** * @brief endless loop for logging sink instance. - * @return void pointer. */ - void* run(); + void run(); /** * @brief get the logging areas. @@ -294,14 +293,13 @@ public: /** * @brief endless loop for logger instance. - * @return void pointer. */ - void* run(); + virtual void run(); /** * @brief shutdown logger subsystem. */ - void stop(); + virtual void stop(); private: /** @@ -334,9 +332,6 @@ private: /** queue for logging messages */ WQueue m_logQueue; - /** true if this instance is running */ - bool m_running; - }; #endif // LIBUTILS_LOGGER_H_ diff --git a/src/lib/utils/thread.cpp b/src/lib/utils/thread.cpp index 4ea84136..b2f6f4ab 100644 --- a/src/lib/utils/thread.cpp +++ b/src/lib/utils/thread.cpp @@ -23,25 +23,22 @@ #include "thread.h" -/** - * @brief static function which will be called on thread startup. - * @return void pointer. - */ -static void* runThread(void* arg) +void* Thread::runThread(void* arg) { - return ((Thread*)arg)->run(); + ((Thread*)arg)->enter(); + return NULL; } Thread::~Thread() { - if (m_running == true && m_detached == false) + if (m_started == true && m_detached == false) pthread_detach(m_threadid); - if (m_running == true) + if (m_started == true) pthread_cancel(m_threadid); } -int Thread::start(const char* name) +bool Thread::start(const char* name) { int result = pthread_create(&m_threadid, NULL, runThread, this); @@ -52,32 +49,36 @@ int Thread::start(const char* name) pthread_setname_np(m_threadid, name); #endif - m_running = true; + m_started = true; + + return true; } - return result; + return false; } -int Thread::join() +bool Thread::join() { int result = -1; - if (m_running == true) { + if (m_started == true) { + m_stopped = true; result = pthread_join(m_threadid, NULL); - if (result == 0) + if (result == 0) { m_detached = false; - + m_started = false; + } } - return result; + return result == 0; } -int Thread::detach() +bool Thread::detach() { int result = -1; - if (m_running == true && m_detached == false) { + if (m_started == true && m_detached == false) { result = pthread_detach(m_threadid); if (result == 0) @@ -85,6 +86,11 @@ int Thread::detach() } - return result; + return result == 0; } +void Thread::enter() { + m_running = true; + run(); + m_running = false; +} diff --git a/src/lib/utils/thread.h b/src/lib/utils/thread.h index e18e4062..67846d22 100644 --- a/src/lib/utils/thread.h +++ b/src/lib/utils/thread.h @@ -32,7 +32,7 @@ public: /** * @brief constructor. */ - Thread() : m_threadid(0), m_running(false), m_detached(false) {} + Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false), m_detached(false) {} /** * @brief virtual destructor. @@ -40,44 +40,73 @@ public: virtual ~Thread(); /** - * @brief create the thread and set name for process list. - * @param name the thread name which show in process list. - * @return value of thread creating. + * @brief Thread entry helper for pthread_create. + * @param arg pointer to the @a Thread. + * @return NULL. */ - int start(const char* name); + static void* runThread(void* arg); /** - * @brief join the thread. - * @return value of thread joining. + * @brief 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. */ - int join(); + virtual bool isRunning() { return m_running == true && m_stopped == false; } /** - * @brief detach the thread. - * @return value of thread detaching. + * @brief 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. */ - int detach(); + virtual bool start(const char* name); /** - * @brief return the thread id. - * @return own thread id. + * @brief Notify the thread that it shall stop. + */ + virtual void stop() { m_stopped = true; } + + /** + * @brief Join the thread. + * @return whether the thread was joined. + */ + virtual bool join(); + + /** + * @brief Detach the thread. + * @return whether the thread was detached. + */ + virtual bool detach(); + + /** + * @brief Get the thread id. + * @return the thread id. */ pthread_t self() {return m_threadid; } /** - * @brief virtul function which must be implemented in derived class. - * @return void pointer. + * @brief Thread entry method to be overridden by derived class. */ - virtual void* run() = 0; + virtual void run() = 0; private: + + /** + * @brief Enter the Thread loop by calling run(). + */ + void enter(); + /** own thread id */ pthread_t m_threadid; - /** true if thread is running */ + /** Whether the thread was started. */ + bool m_started; + + /** Whether the thread is still running (i.e. in @a run() ). */ bool m_running; - /** true if thread is detached */ + /** Whether the thread was stopped by @a stop() or @a join(). */ + bool m_stopped; + + /** Whether the thread was detached */ bool m_detached; }; diff --git a/src/lib/utils/wqueue.h b/src/lib/utils/wqueue.h index 6964c476..5362571e 100644 --- a/src/lib/utils/wqueue.h +++ b/src/lib/utils/wqueue.h @@ -67,17 +67,25 @@ public: /** * @brief remove the first item from queue. - * @return the item. + * @param wait true to wait for an item to be added to the queue, false to return NULL if no item is available. + * @return the item, or NULL if no item is available and wait was false. */ - T remove() + T remove(bool wait=true) { pthread_mutex_lock(&m_mutex); - while (m_queue.size() == 0) - pthread_cond_wait(&m_cond, &m_mutex); - - T item = m_queue.front(); - m_queue.pop_front(); + T item; + if (wait == true) { + while (m_queue.size() == 0) + pthread_cond_wait(&m_cond, &m_mutex); + item = m_queue.front(); + m_queue.pop_front(); + } + else if (m_queue.size() > 0) { + item = m_queue.front(); + m_queue.pop_front(); + } else + item = NULL; pthread_mutex_unlock(&m_mutex); @@ -85,17 +93,39 @@ public: } /** - * @brief return the first item from queue without remove. - * @return the item. + * @brief Remove the specified item from queue. + * @param item the item to remove. + * @return whether the item was removed. */ - T next() + bool remove(T item) + { + pthread_mutex_lock(&m_mutex); + int oldSize = m_queue.size(); + if (oldSize > 0) + m_queue.remove(item); + int newSize = m_queue.size(); + pthread_mutex_unlock(&m_mutex); + return newSize != oldSize; + } + + /** + * @brief return the first item from queue without remove. + * @return the item, or NULL if no item is available and wait was false. + */ + T next(bool wait=true) { pthread_mutex_lock(&m_mutex); - while (m_queue.size() == 0) - pthread_cond_wait(&m_cond, &m_mutex); - - T item = m_queue.front(); + T item; + if (wait == true) { + while (m_queue.size() == 0) + pthread_cond_wait(&m_cond, &m_mutex); + item = m_queue.front(); + } + else if (m_queue.size() > 0) + item = m_queue.front(); + else + item = NULL; pthread_mutex_unlock(&m_mutex); diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am old mode 100644 new mode 100755