From ae15b0e63126ad2c911d9e98806c4bd38a22dddb Mon Sep 17 00:00:00 2001 From: john30 Date: Wed, 29 Oct 2014 22:39:35 +0100 Subject: [PATCH 01/10] removed unused variable --- src/ebusloop.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ebusloop.cpp b/src/ebusloop.cpp index 836aa7c2..0030b72e 100644 --- a/src/ebusloop.cpp +++ b/src/ebusloop.cpp @@ -70,7 +70,6 @@ void* EBusLoop::run() time_t pollStart, pollEnd; time(&pollStart); double pollDelta = 0.0; - bool pollCommandActive = false; for (;;) { if (m_bus->isConnected() == true) { @@ -115,7 +114,7 @@ void* EBusLoop::run() // add new bus command to send if (busResult == RESULT_SYN && busCommandActive == false && m_sendBuffer.size() != 0) { BusCommand* busCommand = m_sendBuffer.remove(); - L.log(bus, debug, " msg: %s", busCommand->getCommand().getDataStr(true).c_str()); + L.log(bus, debug, " msg: %s", busCommand->getCommand().getDataStr().c_str()); m_bus->addCommand(busCommand); L.log(bus, debug, " addCommand success"); busCommandActive = true; @@ -154,7 +153,6 @@ void* EBusLoop::run() m_bus->addCommand(busCommand); L.log(bus, debug, " addCommand success"); busCommandActive = true; - pollCommandActive = true; time(&pollStart); } @@ -181,7 +179,6 @@ void* EBusLoop::run() m_commands->storePolData(busCommand->getMessageStr().c_str()); // TODO use getResult() delete busCommand; - pollCommandActive = false; } else { busCommand->sendSignal(); } @@ -206,7 +203,6 @@ void* EBusLoop::run() } lookbusretries = 0; busCommandActive = false; - pollCommandActive = false; }else { lookbusretries++; } From 7643b0593591807da6b70a27aecc8a84cc5b9b07 Mon Sep 17 00:00:00 2001 From: john30 Date: Wed, 29 Oct 2014 23:28:51 +0100 Subject: [PATCH 02/10] merge in latest libebus commit 9520d5a3283472c2a53b8396b0796723f660bed7: simplified SymbolString interface and let it handle unescaping itself --- src/libebus/result.h | 1 + src/libebus/symbol.cpp | 125 ++++++++++++++++++++++----------------- src/libebus/symbol.h | 66 ++++++++++----------- src/test/test_symbol.cpp | 16 ++--- 4 files changed, 110 insertions(+), 98 deletions(-) diff --git a/src/libebus/result.h b/src/libebus/result.h index bd0a9c13..c0f86edf 100644 --- a/src/libebus/result.h +++ b/src/libebus/result.h @@ -31,6 +31,7 @@ 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_ERR_SEND = -1; // send error static const int RESULT_ERR_EXTRA_DATA = -2; // received bytes > sent bytes diff --git a/src/libebus/symbol.cpp b/src/libebus/symbol.cpp index 102c4760..287020bf 100644 --- a/src/libebus/symbol.cpp +++ b/src/libebus/symbol.cpp @@ -18,6 +18,7 @@ */ #include "symbol.h" +#include "result.h" #include #include @@ -27,7 +28,7 @@ namespace libebus /** * @brief CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1. */ -static const unsigned char CRC_LOOKUP_TABLE[] +static const unsigned char CRC_LOOKUP_TABLE[] = { 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee, 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc, @@ -49,41 +50,35 @@ static const unsigned char CRC_LOOKUP_TABLE[] SymbolString::SymbolString(const std::string str) - : m_crc(0) + : m_unescapeState(0), m_crc(0) { // parse + escape for (size_t i = 0; i+1 < str.size(); i += 2) { - unsigned long value = strtoul(str.substr(i, 2).c_str(), NULL, 16); - push_back_escape((unsigned char)value); + unsigned long value = strtoul(str.substr(i, 2).c_str(), NULL, 16); // TODO check + push_back((unsigned char)value, false, true); } // add CRC + escape - push_back_escape(m_crc, false); + push_back(m_crc, false, false); } -SymbolString::SymbolString(const std::string str, bool escaped) - : m_crc(0) +SymbolString::SymbolString(const std::string str, bool isEscaped) + : m_unescapeState(1), m_crc(0) { - bool previousEscape = false; - // parse + optionally unescape for (size_t i = 0; i+1 < str.size(); i += 2) { - unsigned long value = strtoul(str.substr(i, 2).c_str(), NULL, 16); - if (escaped == true) { - push_back_unescape((unsigned char)value, previousEscape, false); - } - else - m_data.push_back((unsigned char)value); + unsigned long value = strtoul(str.substr(i, 2).c_str(), NULL, 16); // TODO check + push_back((unsigned char)value, isEscaped, false); } } -const std::string SymbolString::getDataStr(bool unescape) +const std::string SymbolString::getDataStr(const bool unescape) { std::stringstream sstr; bool previousEscape = false; - for (size_t i = 0; i < size(); i++) { - unsigned char value = at(i); - if (unescape == true && previousEscape == true) { + for (size_t i = 0; i < m_data.size(); i++) { + unsigned char value = m_data[i]; + if (m_unescapeState == 0 && unescape == true && previousEscape == true) { if (value == 0x00) { sstr << "a9"; // ESC } @@ -95,7 +90,7 @@ const std::string SymbolString::getDataStr(bool unescape) } previousEscape = false; } - else if (unescape == true && value == ESC) { + else if (m_unescapeState == 0 && unescape == true && value == ESC) { previousEscape = true; // escape sequence not yet finished } else { @@ -107,56 +102,80 @@ const std::string SymbolString::getDataStr(bool unescape) return sstr.str(); } -void SymbolString::push_back_escape(const unsigned char value, bool updateCRC) +int SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) { - if (value == ESC) { - m_data.push_back(ESC); - m_data.push_back(0x00); - if (updateCRC) { - addCRC(ESC); - addCRC(0x00); + if (m_unescapeState == 0) { // store escaped data + if (isEscaped == false && value == ESC) { + m_data.push_back(ESC); + m_data.push_back(0x00); + if (updateCRC) { + addCRC(ESC); + addCRC(0x00); + } } - } - else if (value == SYN) { - m_data.push_back(ESC); - m_data.push_back(0x01); - if (updateCRC) { - addCRC(ESC); - addCRC(0x01); + else if (isEscaped == false && value == SYN) { + m_data.push_back(ESC); + m_data.push_back(0x01); + if (updateCRC) { + addCRC(ESC); + addCRC(0x01); + } } + else { + m_data.push_back(value); + if (updateCRC) { + addCRC(value); + } + } + return RESULT_OK; } - else { + else if (isEscaped == false) { + if (m_unescapeState != 1) + return RESULT_ERR_ESC; // invalid unescape state m_data.push_back(value); + if (updateCRC) { + if (value == ESC) { + addCRC(ESC); + addCRC(0x00); + } + else if (value == SYN) { + addCRC(ESC); + addCRC(0x01); + } + else { + addCRC(value); + } + } + return RESULT_OK; + } + else if (m_unescapeState != 1) { if (updateCRC) { addCRC(value); } - } -} - -unsigned char SymbolString::push_back_unescape(const unsigned char value, bool& previousEscape, bool updateCRC) -{ - if (updateCRC) { - addCRC(value); - } - if (previousEscape == true) { if (value == 0x00) { m_data.push_back(ESC); - previousEscape = false; - return ESC; + m_unescapeState = 1; + return RESULT_OK; } if (value == 0x01) { m_data.push_back(SYN); - previousEscape = false; - return SYN; + m_unescapeState = 1; + return RESULT_OK; } - return 0; // invalid escape sequence + return RESULT_ERR_ESC; // invalid escape sequence } - if (value == ESC) { - previousEscape = true; - return 1; // escape sequence not yet finished + else if (value == ESC) { + if (updateCRC) { + addCRC(value); + } + m_unescapeState = 2; + return RESULT_IN_ESC; + } + if (updateCRC) { + addCRC(value); } m_data.push_back(value); - return value; + return RESULT_OK; } void SymbolString::addCRC(const unsigned char value) { diff --git a/src/libebus/symbol.h b/src/libebus/symbol.h index 7ed10ce4..f4199a63 100644 --- a/src/libebus/symbol.h +++ b/src/libebus/symbol.h @@ -37,71 +37,61 @@ static const unsigned char BROADCAST = 0xFE; // the broadcast destination addres /** - * @brief A string of bus symbols. + * @brief A string of escaped or unescaped bus symbols. */ class SymbolString { public: /** - * @brief Creates a new empty SymbolString. + * @brief Creates a new unescaped empty instance. + * @param escaped whether to create an escaped instance. */ - SymbolString() : m_crc(0) {} + SymbolString() : m_unescapeState(1), m_crc(0) {} /** - * @brief Creates a new escaped SymbolString from an unescaped hex string and adds the calculated CRC. + * @brief Creates a new escaped instance from an unescaped hex string and adds the calculated CRC. * @param str the unescaped hex string. */ SymbolString(const std::string str); /** - * @brief Creates a new unescaped SymbolString from a hex string. - * @param escaped whether the hex string is escaped and shall be unescaped. + * @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 std::string str, bool escaped); + SymbolString(const std::string str, const bool isEscaped); /** * @brief Returns the symbols as hex string. - * @param escaped whether to unescape the symbols. + * @param unescape whether to unescape an escaped instance. * @return the symbols as hex string. */ - const std::string getDataStr(bool unescape=false); + const std::string getDataStr(const bool unescape=true); /** - * @brief Returns the symbol at the specified index. + * @brief Returns a reference to the symbol at the specified index. * @param index the index of the symbol to return. - * @return the symbol at the specified index. - * @throw std::out_of_range if @a index is invalid. + * @return the reference to the symbol at the specified index. */ - unsigned char at(const size_t index) { return m_data.at(index); } + unsigned char& operator[](const size_t index) { if (index >= m_data.size()) m_data.resize(index+1); return m_data[index]; } /** * @brief Returns the symbol at the specified index. * @param index the index of the symbol to return. * @return the symbol at the specified index. */ - unsigned char operator[](const size_t index) { return m_data[index]; } + const unsigned char& operator[](const size_t index) const { return m_data[index]; } /** - * @brief Returns the symbol at the specified index. - * @param index the index of the symbol to return. - * @return the symbol at the specified index. + * @brief Returns whether this instance is equal to the other instance. + * @param other the other instance. + * @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). */ - unsigned char operator[](const size_t index) const { return m_data[index]; } + bool operator==(SymbolString other) { return (m_unescapeState==0)==(m_unescapeState==0) && m_data==other.m_data; } /** - * @brief Inserts a the symbol at the specified index. - * @param index the index at which to insert the symbol. - * @param value the symbol to insert. - */ - void insert(const size_t index, const unsigned char value) { m_data.insert(m_data.begin()+index, value); } - /** - * @brief Appends a the symbol to the end of the symbol string and escapes it if necessary. + * @brief Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. * @param value the symbol to append. + * @param isEscaped whether the symbol is escaped. * @param updateCrc whether to update the calculated CRC in @a m_crc. + * @return RESULT_OK if another symbol was appended, + * RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received, + * RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected. */ - void push_back_escape(const unsigned char value, bool updateCRC=true); - /** - * @brief Appends a the symbol to the end of the symbol string and unescapes it. - * @param value the symbol to append. - * @param previousEscape whether the previous value was the escape symbol (set to false for the initial call). - * @param updateCrc whether to update the calculated CRC in @a m_crc. - * @return if previousEscape is false on return: the unescaped symbol. otherwise: zero if the escape sequence was invalid, one if the escape sequence is not yet finished. - */ - unsigned char push_back_unescape(const unsigned char value, bool& previousEscape, bool updateCRC=true); + int push_back(const unsigned char value, const bool isEscaped, const bool updateCRC=true); /** * @brief Returns the number of symbols in this symbol string. * @return the number of available symbols. @@ -115,7 +105,7 @@ public: /** * @brief Clears the symbols. */ - void clear() { m_crc=0; m_data.clear(); } + void clear() { m_data.clear(); m_unescapeState = m_unescapeState==0 ? 0 : 1; m_crc = 0; } private: /** @@ -128,6 +118,12 @@ private: * @brief the string of bus symbols. */ std::vector m_data; + /** + * @brief 0 if the symbols in @a m_data are escaped, + * 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol, + * 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol. + */ + int m_unescapeState; /** * @brief the calculated CRC. */ diff --git a/src/test/test_symbol.cpp b/src/test/test_symbol.cpp index 2e495d0b..48abdb10 100644 --- a/src/test/test_symbol.cpp +++ b/src/test/test_symbol.cpp @@ -25,15 +25,9 @@ using namespace libebus; int main () { - SymbolString sstr("10feb5050427a915aa"); + SymbolString sstr = SymbolString("10feb5050427a915aa"); - std::stringstream out; - for (size_t i = 0; i(sstr[i]); - } - - std::string gotStr = out.str(), expectStr = "10feb5050427a90015a90177"; + std::string gotStr = sstr.getDataStr(false), expectStr = "10feb5050427a90015a90177"; if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "ctor escaped successful." << std::endl; @@ -54,7 +48,7 @@ int main () << std::setfill('0') << static_cast(expectCrc) << std::endl; - gotStr = sstr.getDataStr(true), expectStr = "10feb5050427a915aa77"; + gotStr = sstr.getDataStr(), expectStr = "10feb5050427a915aa77"; if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "unescape successful." << std::endl; @@ -63,7 +57,9 @@ int main () << ", expected " << expectStr << std::endl; sstr = SymbolString("10feb5050427a90015a90177", true); - gotStr = sstr.getDataStr(false); + + gotStr = sstr.getDataStr(); + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "ctor unescaped successful." << std::endl; else From 614af5984bf60affc12c05a208bcac83271cfed6 Mon Sep 17 00:00:00 2001 From: john30 Date: Wed, 29 Oct 2014 23:30:15 +0100 Subject: [PATCH 03/10] merge in latest libebus commit 9520d5a3283472c2a53b8396b0796723f660bed7: simplified recvSlaveDataAndCRC, adjusted tot nicer SymbolString, add error description to getMessageStr() --- src/libebus/bus.cpp | 88 +++++++++++--------------------------- src/libebus/bus.h | 3 +- src/libebus/buscommand.cpp | 6 +-- 3 files changed, 29 insertions(+), 68 deletions(-) diff --git a/src/libebus/bus.cpp b/src/libebus/bus.cpp index 6748371c..c9fd742f 100644 --- a/src/libebus/bus.cpp +++ b/src/libebus/bus.cpp @@ -27,7 +27,7 @@ namespace libebus Bus::Bus(const std::string deviceName, const bool noDeviceCheck, const long recvTimeout, const std::string dumpFile, const long dumpSize, const bool dumpState) - : m_previousEscape(false), m_recvTimeout(recvTimeout), m_dumpState(dumpState), + : m_sstr(), m_recvTimeout(recvTimeout), m_dumpState(dumpState), m_busLocked(false), m_busPriorRetry(false) { m_port = new Port(deviceName, noDeviceCheck); @@ -91,14 +91,13 @@ int Bus::proceed() int Bus::proceedCycData(const unsigned char byte) { if (byte != SYN) { - m_sstr.push_back_unescape(byte, m_previousEscape, false); + m_sstr.push_back(byte, true, false); if (m_busLocked == true) m_busLocked = false; return RESULT_DATA; } - m_previousEscape = false; if (byte == SYN && m_sstr.size() != 0) { // lock bus after SYN-BYTE-SYN Sequence if (m_sstr.size() == 1 && m_busPriorRetry == false) @@ -265,7 +264,7 @@ BusCommand* Bus::sendCommand() goto on_exit; // receive NN, Dx, CRC - slaveData = SymbolString(); + slaveData.clear(); retval = recvSlaveDataAndCRC(slaveData); // are calculated and received CRC equal? @@ -346,76 +345,39 @@ unsigned char Bus::recvByte() int Bus::recvSlaveDataAndCRC(SymbolString& result) { - unsigned char byte_recv; + unsigned char byte_recv, crc_calc = 0; ssize_t bytes_recv; - bool previousEscape = false; + size_t NN = 0; + bool updateCrc = true; + int retval = 0; - // receive NN - bytes_recv = m_port->recv(RECV_TIMEOUT, 1); - if (bytes_recv < 0) - return RESULT_ERR_TIMEOUT; - - byte_recv = recvByte(); - byte_recv = result.push_back_unescape(byte_recv, previousEscape); - if (previousEscape == true && byte_recv == 0) - return RESULT_ERR_ESC; - - // escape sequence: get another symbol to find NN - if (previousEscape == true) { + for (size_t i = 0, needed = 1; i < needed; i++) { bytes_recv = m_port->recv(RECV_TIMEOUT, 1); if (bytes_recv < 0) return RESULT_ERR_TIMEOUT; byte_recv = recvByte(); - byte_recv = result.push_back_unescape(byte_recv, previousEscape); - if (previousEscape == true) - return RESULT_ERR_ESC; + retval = result.push_back(byte_recv, 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; + crc_calc = result.getCRC(); + needed++; + } } - int NN = byte_recv; - - // receive Dx - for (int i = 0; i < NN; i++) { - bytes_recv = m_port->recv(RECV_TIMEOUT, 1); - if (bytes_recv < 0) - return RESULT_ERR_TIMEOUT; - - byte_recv = recvByte(); - byte_recv = result.push_back_unescape(byte_recv, previousEscape); - if (previousEscape == true && byte_recv == 0) - return RESULT_ERR_ESC; - - // escape sequence: increase NN - if (previousEscape == true) - NN++; - } - if (previousEscape == true) + if (retval==RESULT_IN_ESC) return RESULT_ERR_ESC; - unsigned char crc_calc = result.getCRC(); - // receive CRC - bytes_recv = m_port->recv(RECV_TIMEOUT, 1); - if (bytes_recv < 0) - return RESULT_ERR_TIMEOUT; - - byte_recv = recvByte(); - byte_recv = result.push_back_unescape(byte_recv, previousEscape, false); - if (previousEscape == true && byte_recv == 0) - return RESULT_ERR_ESC; - - // escape sequence: get another symbol to find CRC - if (previousEscape == true) { - bytes_recv = m_port->recv(RECV_TIMEOUT, 1); - if (bytes_recv < 0) - return RESULT_ERR_TIMEOUT; - - byte_recv = recvByte(); - byte_recv = result.push_back_unescape(byte_recv, previousEscape); - if (previousEscape == true) - return RESULT_ERR_ESC; - } - - if (crc_calc != byte_recv) + if (updateCrc || crc_calc != result[result.size()-1]) return RESULT_ERR_CRC; return RESULT_OK; diff --git a/src/libebus/bus.h b/src/libebus/bus.h index be07313c..59fde9ed 100644 --- a/src/libebus/bus.h +++ b/src/libebus/bus.h @@ -64,7 +64,6 @@ public: private: Port* m_port; - bool m_previousEscape; SymbolString m_sstr; std::queue m_cycBuffer; std::queue m_sendBuffer; @@ -87,4 +86,4 @@ private: } //namespace -#endif // LIBEBUS_BUS_H_ +#endif // LIBEBUS_BUS_HPP_ diff --git a/src/libebus/buscommand.cpp b/src/libebus/buscommand.cpp index 59a6d891..14a3b60d 100644 --- a/src/libebus/buscommand.cpp +++ b/src/libebus/buscommand.cpp @@ -24,7 +24,7 @@ namespace libebus BusCommand::BusCommand(const std::string commandStr, const bool isPoll) - : m_isPoll(isPoll), m_command(commandStr), m_resultCode(RESULT_OK) + : m_isPoll(isPoll), m_command(commandStr), m_result(), m_resultCode(RESULT_OK) { unsigned char dstAddress = m_command[1]; @@ -55,7 +55,7 @@ const std::string BusCommand::getMessageStr() if (m_resultCode >= 0) { if (m_type == masterSlave) { - result = m_command.getDataStr(true); + result = m_command.getDataStr(); result += "00"; result += m_result.getDataStr(); result += "00"; @@ -64,7 +64,7 @@ const std::string BusCommand::getMessageStr() } } else - result = "error"; + result = "error: "+std::string(getResultCodeCStr()); return result; } From f798e8ee2e197dde466201d7ffc0d6490cd65234 Mon Sep 17 00:00:00 2001 From: john30 Date: Wed, 29 Oct 2014 23:31:22 +0100 Subject: [PATCH 04/10] merge in latest libebus commit 9520d5a3283472c2a53b8396b0796723f660bed7: started new DataField classes --- src/libebus/Makefile.am | 2 + src/libebus/data.cpp | 575 ++++++++++++++++++++++++++++++++++++++++ src/libebus/data.h | 226 ++++++++++++++++ src/test/Makefile.am | 4 + src/test/test_data.cpp | 108 ++++++++ 5 files changed, 915 insertions(+) mode change 100644 => 100755 src/libebus/Makefile.am create mode 100644 src/libebus/data.cpp create mode 100644 src/libebus/data.h mode change 100644 => 100755 src/test/Makefile.am create mode 100644 src/test/test_data.cpp diff --git a/src/libebus/Makefile.am b/src/libebus/Makefile.am old mode 100644 new mode 100755 index e5451b7b..e78d40bc --- a/src/libebus/Makefile.am +++ b/src/libebus/Makefile.am @@ -8,6 +8,8 @@ libebus_a_SOURCES = result.cpp \ result.h \ symbol.cpp \ symbol.h \ + data.cpp \ + data.h \ port.cpp \ port.h \ buscommand.cpp \ diff --git a/src/libebus/data.cpp b/src/libebus/data.cpp new file mode 100644 index 00000000..e300d0a0 --- /dev/null +++ b/src/libebus/data.cpp @@ -0,0 +1,575 @@ +/* + * 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 "data.h" +#include "decode.h" +#include "encode.h" +#include +#include +#include +#include +#include +#include + +namespace libebus +{ + + +static const char* days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + + +DataField* DataField::create(const unsigned char dstAddress, const bool isSetMessage, + std::vector::iterator& it, const std::vector::iterator end) { + std::string name, unit, comment; + PartType partType; + float factor; + size_t baseOffset = 0, offset = 0, length = 0, maxPos = 16, offsetCnt = 0; + + if (it == end) + return NULL; + name = *it++; + if (it == end) + return NULL; + const char* posStr = (*it++).c_str(); + if (it == end) + return NULL; + if (posStr[0] >= 'a' && posStr[1] == 0) { + if (posStr[0] == 's') { // slave ACK + // QQ ZZ PB SB NN + Dx + CRC + //offset = 5+command.getMasterDataLength()+1; // skip QQ ZZ PB SB NN Dx CRC + } else if (posStr[0] == 'm') { // master ACK + //offset = 5+command.getMasterDataLength()+3+command.getSlaveDataLength()+1; // skip QQ ZZ PB SB NN Dx CRC ACK NN Dx CRC + } else { + return NULL; // TODO error code: invalid pos definition + } + } else { + if (dstAddress == BROADCAST + || isMaster(dstAddress) + || (isSetMessage == true && posStr[0] <= '9') + || posStr[0] == 'm') { // master data + partType = pt_masterData; + baseOffset = 5; // skip QQ ZZ PB SB NN + //len = command.getMasterDataLength(); + if (posStr[0] == 'm') + posStr++; + } else if ((isSetMessage == false && posStr[0] <= '9') + || posStr[0] == 's') { // slave data + baseOffset = 1; + //offset = 5+command.getMasterDataLength()+3; // skip QQ ZZ PB SB NN Dx CRC ACK NN + //len = command.getSlaveDataLength(); + if (posStr[0] == 's') + posStr++; + } else { + return NULL; // TODO error code: invalid pos definition + } + std::string token; + std::istringstream stream(posStr); + while (std::getline(stream, token, '-') != 0) { + if (++offsetCnt > 2) + return NULL; // TODO error code: invalid pos definition + const char* start = token.c_str(); + char* end = NULL; + int pos = strtoul(start, &end, 10)-1; // 1-based + if (end != start+strlen(start)) + return NULL; // TODO error code: invalid pos definition + if (pos < 0 || baseOffset+pos > maxPos) + return NULL; // TODO error code: invalid pos definition + else if (offsetCnt==1) + offset = baseOffset+pos; + else if (baseOffset+pos >= offset) + length = baseOffset+pos+1-offset; + else { // wrong order e.g. 4-3 + length = offset-(baseOffset+pos+1); + offset = baseOffset+pos; + } + } + } + + const char* typeStr = (*it++).c_str(); + + if (it == end) + factor = 1.0; + else { + std::string factorStr = *it++; + if (factorStr.length() > 0 && factorStr.find_first_not_of("0123456789.") == std::string::npos) + factor = static_cast(strtod(factorStr.c_str(), NULL)); + else + factor = 1.0; + } + + if (it == end) + unit = ""; + else { + unit = *it++; + + if (unit.length() == 1 && unit[0] == '-') + unit.clear(); + } + + if (it == end) + comment = ""; + else { + comment = *it++; + if (comment.length() == 1 && comment[0] == '-') + comment.clear(); + } + + if (strcasecmp(typeStr, "STR") == 0) { + if (length == 0) + length = 1; + return new StringDataField(name, partType, offset, length, dt_string, unit, comment); + } + if (strcasecmp(typeStr, "HEX") == 0) { + if (length == 0) + length = 1; + return new StringDataField(name, partType, offset, length, dt_hex, unit, comment); + } + if (strcasecmp(typeStr, "UCH") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 1, dt_uchar, unit, comment, factor, 0xff); + } + if (strcasecmp(typeStr, "SCH") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 1, dt_schar, unit, comment, factor, 0x80); + } + if (strcasecmp(typeStr, "BCD") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 1, dt_bcd, unit, comment, factor, 0xff); // TODO max value 99 + } + if (strcasecmp(typeStr, "D1B") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 1, dt_d1b, unit, comment, factor, 0x80); + } + if (strcasecmp(typeStr, "D1C") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 1, dt_d1c, unit, comment, factor*0.5, 0xff); // TODO max value 100 + } + if (strcasecmp(typeStr, "UIN") == 0) { + if (length != 0 && length != 2) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 2, dt_uint, unit, comment, factor, 0xffff); + } + if (strcasecmp(typeStr, "SIN") == 0) { + if (length != 0 && length != 2) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 2, dt_sint, unit, comment, factor, 0x8000); + } + if (strcasecmp(typeStr, "D2B") == 0) { + if (length != 0 && length != 2) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 2, dt_d2b, unit, comment, factor/256.0, 0x8000); + } + if (strcasecmp(typeStr, "D2C") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 2, dt_d2c, unit, comment, factor/16.0, 0x8000); + } + if (strcasecmp(typeStr, "ULG") == 0) { + if (length != 0 && length != 4) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 4, dt_ulong, unit, comment, factor, 0xffffffff); + } + if (strcasecmp(typeStr, "SLG") == 0) { + if (length != 0 && length != 4) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 4, dt_slong, unit, comment, factor, 0x80000000); + } + if (strcasecmp(typeStr, "FLT") == 0) { + if (length != 0 && length != 2) + return NULL; // TODO error code: invalid pos definition + return new NumericDataField(name, partType, offset, 2, dt_float, unit, comment, factor/1000.0, 0x8000); // TODO replacement + } + if (strcasecmp(typeStr, "BDA") == 0 || strcasecmp(typeStr, "HDA") == 0) { + if (length == 0) + length = 4; + else if (length != 3 && length != 4) + return NULL; // TODO error code: invalid pos definition + return new StringDataField(name, partType, offset, length, dt_date, unit, comment); // TODO better numeric? + } + if (strcasecmp(typeStr, "BDY") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new StringDataField(name, partType, offset, 1, dt_day, unit, comment); // TODO better numeric? + } + if (strcasecmp(typeStr, "BTI") == 0 || strcasecmp(typeStr, "HTI") == 0) { + if (length != 0 && length != 3) + return NULL; // TODO error code: invalid pos definition + return new StringDataField(name, partType, offset, 3, dt_time, unit, comment); // TODO better numeric? + } + if (strcasecmp(typeStr, "TTM") == 0) { + if (length != 0 && length != 1) + return NULL; // TODO error code: invalid pos definition + return new StringDataField(name, partType, offset, 1, dt_tTime, unit, comment); // TODO better numeric? + } + return NULL; // TODO error code: invalid type definition +} + +const std::string DataField::parseSymbols(SymbolString& masterData, SymbolString& slaveData, bool verbose) +{ + SymbolString& data = m_partType == pt_masterData ? masterData : slaveData; + switch (m_partType) { + case pt_masterData: + break; + case pt_slaveData: + break; + default: + return "invalid part type"; + } + std::ostringstream output; + if (verbose) + output << m_name << "="; + + if (parse(data, output) == false) + return "unable to parse"; + + if (verbose && m_unit.length() > 0) + output << " " << m_unit; + if (verbose && m_comment.length() > 0) + output << " [" << m_comment << "]"; + return output.str(); +} + +bool DataField::formatSymbols(const std::string& value, SymbolString& masterData, SymbolString& slaveData) +{ + SymbolString& data = m_partType == pt_masterData ? masterData : slaveData; + switch (m_partType) { + case pt_masterData: + break; + case pt_slaveData: + break; + default: + return false; // TODO error code + } + std::istringstream input(value); + if (format(data, input) == false) + return false; // TODO error code + return true; +} + + + +bool StringDataField::parse(SymbolString& data, std::ostringstream& output) +{ + size_t start = m_offset, end = m_offset+m_length; + unsigned char ch; + + if (end > data.size()) { + return false; // TODO error not enough data available + } + if (m_dataType == dt_time) { // reverse order + for (size_t i = end-1; i >= start; i--) { + ch = data[i]; + if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) + return false; // invalid BCD + if ((i == end-1 && ch > 0x23) || (i != end-1 && ch > 0x59)) + return false; // invalid time + if (i != end-1) + output << ":"; + output << std::setw(2) << std::setfill('0') << std::hex << static_cast(ch); + } + return true; + } + + for (size_t i = start; i < end; i++) { + ch = data[i]; + switch (m_dataType) { + case dt_hex: + if (i != start) + output << " "; + output << std::nouppercase << std::setw(2) << std::hex + << std::setfill('0') << static_cast(ch); + break; + case dt_date: + if (m_length == 4u && i == m_offset+2u) + break; // skip weekday in between + if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) + return false; // invalid BCD + if (i == end-1) + output << std::hex << (0x2000+ch); + else if (ch < 0x01 || (i == start && ch > 0x31) || (i == start+1 && ch > 0x12)) + return false; // invalid date + else + output << std::setw(2) << std::setfill('0') << std::hex << static_cast(ch) << "."; + break; + case dt_day: + if (ch < 1 || ch > 7) + return false; // invalid day + output << days[ch-1]; + break; + case dt_tTime: + if (ch/6 > 23) + return false; // invalid time + output << std::setw(2) << std::setfill('0') << static_cast(ch/6) << ":" + << std::setw(2) << std::setfill('0') << static_cast((ch%6)*10); + break; + default: + if (ch < 0x20) + ch = 0x20; + output << static_cast(ch); + break; + } + } + + return true; +} + +bool StringDataField::format(SymbolString& data, std::istringstream& input) +{ + size_t start = m_offset, end = m_offset+m_length; + const char* str; + char* strEnd; + unsigned long int value, hours; + unsigned char ch; + + std::string token; + if (m_dataType == dt_time) { // reverse order + for (size_t i = end-1; i >=start; i--) { + if (std::getline(input, token, ':') == 0) + return false; + str = token.c_str(); + strEnd = NULL; + value = strtoul(str, &strEnd, 16); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + if ((value&0xf0) > 0x90 || (value&0x0f) > 0x09) + return false; // invalid BCD + if ((i == end-1 && value > 0x23) || (i != end-1 && value > 0x59)) + return false; // invalid time + data[i] = (unsigned char)value; + } + return true; + } + + for (size_t i = start; i < end; i++) { + switch (m_dataType) { + case dt_hex: + while (input.peek()==' ') + input.get(); + token.clear(); + token.push_back(input.get()); + if (input.eof() == true) + return false; // TODO error code: invalid value + token.push_back(input.get()); + if (input.eof() == true) + return false; // TODO error code: invalid value + + str = token.c_str(); + strEnd = NULL; + value = strtoul(str, &strEnd, 16); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + data[i] = (unsigned char)value; + break; + case dt_date: + if (m_length == 4u && i == m_offset+2u) + break; // skip weekday in between + if (std::getline(input, token, '.') == 0) + return false; + str = token.c_str(); + strEnd = NULL; + value = strtoul(str, &strEnd, 16); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + if ((value&0xf0) > 0x90 || (value&0x0f) > 0x09) + return false; // invalid BCD + if (i == end-1) { + if (value>=0x2000) + value -= 0x2000; + if (value>=0x100) + return false; // invalid year + } + else if (value < 1 || (i == start && value > 0x31) || (i == start+1 && value > 0x12)) + return false; // invalid date + data[i] = (unsigned char)value; + break; + case dt_day: + str = input.str().c_str(); + for (ch = 0; ch < 7; ch++) + if (strcasecmp(days[ch], str) == 0) + break; + if (ch == 7) + return false; // invalid day + data[i] = ch+1; + break; + case dt_tTime: + if (std::getline(input, token, ':') == 0) + return false; + str = token.c_str(); + strEnd = NULL; + hours = strtoul(str, &strEnd, 10); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + if (hours > 23) + return false; // invalid time + + if (std::getline(input, token, ':') == 0) + return false; + str = token.c_str(); + strEnd = NULL; + value = strtoul(str, &strEnd, 10); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + if (value > 59 || (value%10) != 0) + return false; // invalid time + data[i] = (unsigned char)(hours*6 + value/10); + break; + default: + ch = input.get(); + if (input.eof() == true || ch < 0x20) + ch = 0x20; + data[i] = ch; + break; + } + } + + return true; +} + + +bool NumericDataField::parse(SymbolString& data, std::ostringstream& output) +{ + size_t start = m_offset, end = m_offset+m_length; + unsigned int value = 0; + int signedValue; + unsigned char ch; + + if (end > data.size()) + return false; // TODO error not enough data available + + for (size_t i = start, exp = 1; i < end; i++) { + ch = data[i]; + switch (m_dataType) { + case dt_bcd: + if (ch == m_replacement) { + output << "-"; + return true; + } + else if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) + return false; // invalid BCD + else + value |= ((ch>>4)*10 + (ch&0x0f))*exp; + exp = exp*100; + break; + default: + value |= ch*exp; + exp = exp<<8; + break; + } + } + if (value == m_replacement) { + output << "-"; + return true; + } + + switch (m_dataType) { + case dt_schar: + case dt_d1b: + signedValue = (char)value; + break; + case dt_d2b: + case dt_d2c: + case dt_sint: + signedValue = (short)value; + break; + case dt_slong: + signedValue = (int)value; + break; + case dt_ulong: + if (m_factor == 1.0) + output << std::setprecision(0) << std::fixed << static_cast(value); + else + output << std::setprecision(3) << std::fixed << static_cast(value * m_factor); + return true; + default: + signedValue = value; + break; + } + if (m_factor == 1.0) + output << std::setprecision(0) << std::fixed << static_cast(signedValue); + else + output << std::setprecision(3) << std::fixed << static_cast(signedValue * m_factor); + + return true; +} + +bool NumericDataField::format(SymbolString& data, std::istringstream& input) +{ + size_t start = m_offset, end = m_offset+m_length; + unsigned int value; + unsigned char ch; + + const char* str = input.str().c_str(); + if (strcasecmp(str, "-") == 0) + // replacement value + value = m_replacement; + else { + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + dvalue /= m_factor; + switch (m_dataType) { + case dt_schar: + case dt_sint: + case dt_slong: + case dt_d1b: + case dt_d2b: + case dt_d2c: + if (dvalue < -(1LL<<(8*m_length)) || dvalue >= (1LL<<(8*m_length))) + return false; // TODO error code: invalid value + break; + default: + // no special handling + if (dvalue < 0.0 || dvalue >= (1LL<<(8*m_length))) + return false; // TODO error code: invalid value + break; + } + value = (unsigned int)dvalue; + } + + for (size_t i = start, exp = 1; i < end; i++) { + switch (m_dataType) { + case dt_bcd: + if (value == m_replacement) + ch = m_replacement; + else { + ch = (value/exp)%100; + ch = ((ch/10)<<4) | (ch%10); + } + exp = exp*100; + break; + default: + ch = (value/exp)&0xff; + exp = exp<<8; + break; + } + data[i] = ch; + } + + return true; +} + + +} //namespace + diff --git a/src/libebus/data.h b/src/libebus/data.h new file mode 100644 index 00000000..756e584a --- /dev/null +++ b/src/libebus/data.h @@ -0,0 +1,226 @@ +/* + * 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 LIBEBUS_DATA_H_ +#define LIBEBUS_DATA_H_ + +#include "symbol.h" +#include +#include + +namespace libebus +{ + + +/** the message part in which a data field is stored. */ +enum PartType { + pt_masterData, // stored in master data + //pt_slaveAck, // stored in slave acknowledge // TODO implement if reasonable + pt_slaveData, // stored in slave data + //pt_masterAck // stored in master acknowledge // TODO implement if reasonable + }; + +/** the available data types. */ +enum DataType { + dt_string, // string of characters with fixed length>0, filled up with space + dt_hex, // string of hex digits with fixed length>0, separated by space + dt_bcd, // length: 1 byte, binary coded number, string format: "%d" + dt_uchar, + dt_schar, + dt_uint, + dt_sint, + dt_ulong, + dt_slong, + dt_float, + dt_d1b, + dt_d1c, + dt_d2b, + dt_d2c, + dt_date, // length: 4 bytes (with skipped weekday) or 3 bytes (without weekday), string format: "dd.mm.yyyy" + dt_day, // length: 1 byte, string format: "www" (3 character weekday name) + dt_time, // length: 3 bytes, string format: "hh:mm" + dt_tTime // length: 1 byte, string format: "hh:mm" + }; + + +/** + * @brief Base class for all kinds of data fields. + */ +class DataField +{ +public: + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param partType the message part in which the field is stored. + * @param offset the offset to the first symbol in the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param dataType the data type. + * @param unit the value unit. + * @param comment the field comment. + */ + DataField(const std::string name, const PartType partType, + const unsigned char offset, const unsigned char length, + const DataType dataType, const std::string unit, + const std::string comment) + : m_name(name), m_partType(partType), m_offset(offset), + m_length(length), m_dataType(dataType), m_unit(unit), + m_comment(comment) {} + /** + * @brief Destructor. + */ + virtual ~DataField() {} + + /** + * @brief Factory method for creating a new instance. + * @param dstAddress the destination bus address. + * @param isSetMessage whther the field is part of a set message. + * @param it the iterator to traverse for the definition parts. + * @param end the iterator pointing to the end of the definition parts. + */ + static DataField* create(const unsigned char dstAddress, const bool isSetMessage, + std::vector::iterator& it, const std::vector::iterator end); + + /** + * @brief Parses the value from the master or slave @a SymbolString. + * @param masterData the unescaped master data @a SymbolString to parse from. + * @param slaveData the unescaped slave data @a SymbolString to parse from. + * @return the parsed value as string. + */ + const std::string parseSymbols(SymbolString& masterData, SymbolString& slaveData, bool verbose=false); + /** + * @brief Formats the value to the master or slave @a SymbolString. + * @param masterData the unescaped master data @a SymbolString to format to. + * @param slaveData the unescaped slave data @a SymbolString to format to. + * @param value the value as string. + */ + bool formatSymbols(const std::string& value, SymbolString& masterData, SymbolString& slaveData); + +protected: + /** + * @brief Internal method doing the actual parse for the individual data type. + * @param data the unescaped data SymbolString to parse from. + * @param output the ostringstream to append to. + * @return true if the value was parsed successfully. + */ + virtual bool parse(SymbolString& data, std::ostringstream& output) = 0; + /** + * @brief Internal method doing the actual format for the individual data type. + * @param data the unescaped data SymbolString to format to. + * @param input the istringstream to interpret. + * @return true if the value was formatted successfully. + */ + virtual bool format(SymbolString& data, std::istringstream& input) = 0; + + /** the field name. */ + const std::string m_name; + /** the message part in which the field is stored. */ + const PartType m_partType; + /** the offset to the first symbol in the message part in which the field is stored. */ + const unsigned char m_offset; + /** the number of symbols in the message part in which the field is stored. */ + const unsigned char m_length; + /** the data type. */ + const DataType m_dataType; + //std::vector m_valid;//TODO add list of possible values + /** the value unit. */ + const std::string m_unit; + /** the field comment. */ + const std::string m_comment; +}; + + +/** + * @brief Base class for all string based data fields. + */ +class StringDataField : public DataField +{ +public: + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param partType the message part in which the field is stored. + * @param offset the offset to the first symbol in the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param dataType the data type. + * @param unit the value unit. + * @param comment the field comment. + */ + StringDataField(const std::string name, const PartType partType, + const unsigned char offset, const unsigned char length, + const DataType dataType, const std::string unit, + const std::string comment) + : DataField(name, partType, offset, length, dataType, unit, comment) {} + /** + * @brief Destructor. + */ + virtual ~StringDataField() {} + +protected: + virtual bool parse(SymbolString& data, std::ostringstream& output); + virtual bool format(SymbolString& data, std::istringstream& input); + +}; + + +/** + * @brief Base class for all numeric data fields. + */ +class NumericDataField : public DataField +{ +public: + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param partType the message part in which the field is stored. + * @param offset the offset to the first symbol in the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param dataType the data type. + * @param comment the field comment. + * @param unit the value unit. + * @param factor the factor to apply on the value. + * @param replacement the (binary) replacement value to use if the value is not set. + */ + NumericDataField(const std::string name, const PartType partType, + const unsigned char offset, const unsigned char length, + const DataType dataType, const std::string unit, + const std::string comment, const float factor, + const unsigned int replacement) + : DataField(name, partType, offset, length, dataType, unit, comment), + m_factor(factor), m_replacement(replacement) {} + /** + * @brief Destructor. + */ + virtual ~NumericDataField() {} + +protected: + virtual bool parse(SymbolString& data, std::ostringstream& output); + virtual bool format(SymbolString& data, std::istringstream& input); + + /** the factor to apply on the value. */ + const float m_factor; + /** the (binary) replacement value to use if the value is not set. */ + const unsigned int m_replacement; + +}; + + +} //namespace + +#endif // LIBEBUS_DATA_H_ diff --git a/src/test/Makefile.am b/src/test/Makefile.am old mode 100644 new mode 100755 index b2befdc6..8d9c0746 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -5,6 +5,7 @@ AM_CXXFLAGS = -fpic \ noinst_PROGRAMS = test_port \ test_symbol \ + test_data \ test_bus \ test_commands \ test_configfile \ @@ -17,6 +18,9 @@ test_port_LDADD = $(top_srcdir)/src/libebus/libebus.a test_symbol_SOURCES = test_symbol.cpp test_symbol_LDADD = $(top_srcdir)/src/libebus/libebus.a +test_data_SOURCES = test_data.cpp +test_data_LDADD = $(top_srcdir)/src/libebus/libebus.a + test_bus_SOURCES = test_bus.cpp test_bus_LDADD = $(top_srcdir)/src/libebus/libebus.a diff --git a/src/test/test_data.cpp b/src/test/test_data.cpp new file mode 100644 index 00000000..367a45c0 --- /dev/null +++ b/src/test/test_data.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) John Baier 2014 + * + * This file is part of libebus. + * + * libebus 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. + * + * libebus 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 libebus. If not, see http://www.gnu.org/licenses/. + */ + +#include "data.h" +#include +#include + +using namespace libebus; + +int main () +{ + //TODO dt_float, + //TODO dt_d1b, + //TODO dt_d1c, + //TODO dt_d2c, + std::string checks[][4] = { + //name;position(s);type;factor;unit;comment +// {"temp;1;d2b;;°C;Aussentemperatur","temp=18.004 °C [Aussentemperatur]","10fe070009019258042126100714cc", "00"}, +// {"zeit;1;ttm;2;Uhr;","zeit=22:40 Uhr","10feffff0188", "00"}, + {"hex;1-10;hex","53 70 65 69 63 68 65 72 20 20", "10fe07000a53706569636865722020", "00"}, + {"zeit;1;bti","21:04:58","10fe070009580421", "00"}, + {"datum;1;bda","26.10.2014","10fe07000926100714", "00"}, + {"datum;1-3;bda","26.10.2014","10fe070003261014", "00"}, + {"tag;1;bdy","Sun","10fe07000307", "00"}, + {"temp;1;d2b","18.004","10fe0700090112", "00"}, + {"zeit;1;ttm","22:40","10feffff0188", "00"}, + {"bcd;1;bcd","26","10feffff0126", "00"}, + {"bcd;1;bcd","-","10feffff01ff", "00"}, + {"uch;1;uch","38","10feffff0126", "00"}, + {"sch;1;sch","-90","10feffff01a6", "00"}, + {"uin;1;uin","38","10feffff022600", "00"}, + {"sin;1;sin","-90","10feffff02a6ff", "00"}, + {"ulg;1;ulg","38","10feffff0426000000", "00"}, + {"slg;1;slg","-90","10feffff04a6ffffff", "00"}, + {"str;1-9;str","hallo Du!","10feffff0868616c6c6f20447521", "00"}, + {"str;1-9;str","hallo Du ","10feffff0868616c6c6f20447500", "00"}, + }; + for (size_t i = 0; i < sizeof(checks)/sizeof(checks[0]); i++) { + std::istringstream isstr(checks[i][0]); + std::string expectStr = checks[i][1]; + SymbolString mstr = SymbolString(checks[i][2], false); + SymbolString sstr = SymbolString(checks[i][3], false); + std::string item; + std::vector entries; + + while (std::getline(isstr, item, ';') != 0) + entries.push_back(item); + + std::vector::iterator it = entries.begin(); + DataField* field = DataField::create(mstr[1], false, it, entries.end()); + + if (field == NULL) { + std::cout << "create \"" << checks[i][0] << "\" invalid: null" << std::endl; + return 1; + } + std::cout << "create \"" << checks[i][0] << "\" successful" << std::endl; + + + std::string gotStr = field->parseSymbols(mstr, sstr); + + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "parse successful: " << gotStr << std::endl; + else + std::cout << "parse invalid: got " << gotStr + << ", expected " << expectStr << std::endl; + + SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); + SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); + if (field->formatSymbols(gotStr, writeMstr, writeSstr) == false) + std::cout << "format failed" << std::endl; + else { + if (mstr == writeMstr && sstr == writeSstr) + std::cout << "format successful" << std::endl; + else { + std::cout << "format invalid: "; + if (mstr == writeMstr) + std::cout << "master OK"; + else + std::cout << "master got " << writeMstr.getDataStr() << ", expected " << mstr.getDataStr(); + + if (sstr == writeSstr) + std::cout << ", slave OK"; + else + std::cout << ", slave got " << writeSstr.getDataStr() << ", expected " << sstr.getDataStr(); + std::cout << std::endl; + } + } + delete field; + } + return 0; + +} From 45ce9f180aa9e874d9f3d4183e007a270b3f0faa Mon Sep 17 00:00:00 2001 From: john30 Date: Wed, 29 Oct 2014 23:32:19 +0100 Subject: [PATCH 05/10] file mode --- src/libebus/Makefile.am | 0 src/test/Makefile.am | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/libebus/Makefile.am mode change 100755 => 100644 src/test/Makefile.am diff --git a/src/libebus/Makefile.am b/src/libebus/Makefile.am old mode 100755 new mode 100644 diff --git a/src/test/Makefile.am b/src/test/Makefile.am old mode 100755 new mode 100644 From 2525c4fe00eb04f0a40212f97baaaf673ce46a07 Mon Sep 17 00:00:00 2001 From: john30 Date: Fri, 31 Oct 2014 20:33:51 +0100 Subject: [PATCH 06/10] code style --- src/libebus/bus.cpp | 6 +++--- src/libebus/bus.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libebus/bus.cpp b/src/libebus/bus.cpp index c9fd742f..f0da3073 100644 --- a/src/libebus/bus.cpp +++ b/src/libebus/bus.cpp @@ -358,10 +358,10 @@ int Bus::recvSlaveDataAndCRC(SymbolString& result) byte_recv = recvByte(); retval = result.push_back(byte_recv, true, updateCrc); - if (retval<0) + if (retval < 0) return retval; - if (retval==RESULT_IN_ESC) + if (retval == RESULT_IN_ESC) needed++; else if (result.size() == 1) { // NN received NN = result[0]; @@ -374,7 +374,7 @@ int Bus::recvSlaveDataAndCRC(SymbolString& result) } } - if (retval==RESULT_IN_ESC) + if (retval == RESULT_IN_ESC) return RESULT_ERR_ESC; if (updateCrc || crc_calc != result[result.size()-1]) diff --git a/src/libebus/bus.h b/src/libebus/bus.h index 59fde9ed..d752dbd7 100644 --- a/src/libebus/bus.h +++ b/src/libebus/bus.h @@ -86,4 +86,4 @@ private: } //namespace -#endif // LIBEBUS_BUS_HPP_ +#endif // LIBEBUS_BUS_H_ From cb695ca13226fa06d26b312ec30e5c196f39b8d1 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Nov 2014 22:13:01 +0100 Subject: [PATCH 07/10] initialze appended symbols with 0 --- src/libebus/symbol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 src/libebus/symbol.h diff --git a/src/libebus/symbol.h b/src/libebus/symbol.h old mode 100644 new mode 100755 index f4199a63..02288978 --- a/src/libebus/symbol.h +++ b/src/libebus/symbol.h @@ -69,7 +69,7 @@ public: * @param index the index of the symbol to return. * @return the reference to the symbol at the specified index. */ - unsigned char& operator[](const size_t index) { if (index >= m_data.size()) m_data.resize(index+1); return m_data[index]; } + unsigned char& operator[](const size_t index) { if (index >= m_data.size()) m_data.resize(index+1, 0); return m_data[index]; } /** * @brief Returns the symbol at the specified index. * @param index the index of the symbol to return. From b5b5a49c4ea4fb85d7d51f36a65745828af6d4da Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Nov 2014 22:13:51 +0100 Subject: [PATCH 08/10] added list values, use list of predefined data types instead of coding each one separately --- src/libebus/data.cpp | 686 +++++++++++++++++++++-------------------- src/libebus/data.h | 215 +++++++++---- src/test/test_data.cpp | 42 +-- 3 files changed, 528 insertions(+), 415 deletions(-) mode change 100644 => 100755 src/libebus/data.cpp mode change 100644 => 100755 src/libebus/data.h mode change 100644 => 100755 src/test/test_data.cpp diff --git a/src/libebus/data.cpp b/src/libebus/data.cpp old mode 100644 new mode 100755 index e300d0a0..a23a1be6 --- a/src/libebus/data.cpp +++ b/src/libebus/data.cpp @@ -30,8 +30,34 @@ namespace libebus { +/** the known data field types. */ +static const dataType_t dataTypes[] = { + {"STR",16, bt_str, ADJ,' ', 1, 16, 0}, // >= 1 byte character string filled up with space + {"HEX",16, bt_hexstr,ADJ, 0, 2, 47, 0}, // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d + {"BDA", 4, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) + {"BDA", 3, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) + //{"HDA", 3, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) + //{"HDA", 4, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x0101WW00 - 0x3112WW99, WW is ignored weekday) + {"BTI", 3, bt_time,BCD|REV,0, 8, 8, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) + {"TTM", 1, bt_time, 0, 0, 5, 5, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) + {"BDY", 1, bt_list,BCD|DAY,0, 0, 6, 0}, // weekday, "Mon" - "Sun" + {"HDY", 1, bt_list,BCD|DAY,0, 1, 7, 0}, // weekday, "Mon" - "Sun" // TODO different range + {"BCD", 1, bt_number,BCD|LST,0xff, 0, 0x99, 1}, // unsigned decimal in BCD, 0 - 99 + {"UCH", 1, bt_number, LST, 0xff, 0, 0xff, 1}, // unsigned integer, 0 - 255 + {"SCH", 1, bt_number, SIG, 0x80, 0x80, 0x7f, 1}, // signed integer, -128 - +127 + {"D1B", 1, bt_number, SIG, 0x80, 0x81, 0x7f, 1}, // signed integer, -127 - +127 + {"D1C", 1, bt_number, 0, 0xff, 0x00, 0xc8, 2}, // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff) + {"UIN", 2, bt_number, LST, 0xffff, 0, 0xffff, 1}, // unsigned integer, 0 - 65535 + {"SIN", 2, bt_number, SIG, 0x8000, 0x8000, 0x7fff, 1}, // signed integer, -32768 - +32767 + {"FLT", 2, bt_number, SIG, 0x8000, 0x8000, 0x7fff, 1000}, // signed number (fraction 1/1000), -32.768 - +32.767 + {"D2B", 2, bt_number, SIG, 0x8000, 0x8001, 0x7fff, 256}, // signed number (fraction 1/256), -127.99 - +127.99 + {"D2C", 2, bt_number, SIG, 0x8000, 0x8001, 0x7fff, 16}, // signed number (fraction 1/16), -2047.9 - +2047.9 + {"ULG", 4, bt_number, LST, 0xffffffff, 0, 0xffffffff, 1}, // unsigned integer, 0 - 4294967295 + {"SLG", 4, bt_number, SIG, 0x80000000, 0x80000000, 0xffffffff, 1}, // signed integer, -2147483648 - +2147483647 +}; -static const char* days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +/** the week day names. */ +static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; DataField* DataField::create(const unsigned char dstAddress, const bool isSetMessage, @@ -44,60 +70,49 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes if (it == end) return NULL; name = *it++; - if (it == end) + if (it == end || name.size() == 0) return NULL; const char* posStr = (*it++).c_str(); if (it == end) return NULL; - if (posStr[0] >= 'a' && posStr[1] == 0) { - if (posStr[0] == 's') { // slave ACK - // QQ ZZ PB SB NN + Dx + CRC - //offset = 5+command.getMasterDataLength()+1; // skip QQ ZZ PB SB NN Dx CRC - } else if (posStr[0] == 'm') { // master ACK - //offset = 5+command.getMasterDataLength()+3+command.getSlaveDataLength()+1; // skip QQ ZZ PB SB NN Dx CRC ACK NN Dx CRC - } else { - return NULL; // TODO error code: invalid pos definition - } + if (dstAddress == BROADCAST + || isMaster(dstAddress) + || (isSetMessage == true && posStr[0] <= '9') + || posStr[0] == 'm') { // master data + partType = pt_masterData; + baseOffset = 5; // skip QQ ZZ PB SB NN + //len = command.getMasterDataLength(); + if (posStr[0] == 'm') + posStr++; + } else if ((isSetMessage == false && posStr[0] <= '9') + || posStr[0] == 's') { // slave data + baseOffset = 1; + //offset = 5+command.getMasterDataLength()+3; // skip QQ ZZ PB SB NN Dx CRC ACK NN + //len = command.getSlaveDataLength(); + if (posStr[0] == 's') + posStr++; } else { - if (dstAddress == BROADCAST - || isMaster(dstAddress) - || (isSetMessage == true && posStr[0] <= '9') - || posStr[0] == 'm') { // master data - partType = pt_masterData; - baseOffset = 5; // skip QQ ZZ PB SB NN - //len = command.getMasterDataLength(); - if (posStr[0] == 'm') - posStr++; - } else if ((isSetMessage == false && posStr[0] <= '9') - || posStr[0] == 's') { // slave data - baseOffset = 1; - //offset = 5+command.getMasterDataLength()+3; // skip QQ ZZ PB SB NN Dx CRC ACK NN - //len = command.getSlaveDataLength(); - if (posStr[0] == 's') - posStr++; - } else { + return NULL; // TODO error code: invalid pos definition + } + std::string token; + std::istringstream stream(posStr); + while (std::getline(stream, token, '-') != 0) { + if (++offsetCnt > 2) return NULL; // TODO error code: invalid pos definition - } - std::string token; - std::istringstream stream(posStr); - while (std::getline(stream, token, '-') != 0) { - if (++offsetCnt > 2) - return NULL; // TODO error code: invalid pos definition - const char* start = token.c_str(); - char* end = NULL; - int pos = strtoul(start, &end, 10)-1; // 1-based - if (end != start+strlen(start)) - return NULL; // TODO error code: invalid pos definition - if (pos < 0 || baseOffset+pos > maxPos) - return NULL; // TODO error code: invalid pos definition - else if (offsetCnt==1) - offset = baseOffset+pos; - else if (baseOffset+pos >= offset) - length = baseOffset+pos+1-offset; - else { // wrong order e.g. 4-3 - length = offset-(baseOffset+pos+1); - offset = baseOffset+pos; - } + const char* start = token.c_str(); + char* end = NULL; + unsigned int pos = strtoul(start, &end, 10)-1; // 1-based + if (end != start+strlen(start)) + return NULL; // TODO error code: invalid pos definition + if (baseOffset+pos > maxPos) + return NULL; // TODO error code: invalid pos definition + else if (offsetCnt==1) + offset = baseOffset+pos; + else if (baseOffset+pos >= offset) + length = baseOffset+pos+1-offset; + else { // wrong order e.g. 4-3 + length = offset-(baseOffset+pos+1); + offset = baseOffset+pos; } } @@ -130,104 +145,66 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes comment.clear(); } - if (strcasecmp(typeStr, "STR") == 0) { - if (length == 0) - length = 1; - return new StringDataField(name, partType, offset, length, dt_string, unit, comment); + std::map values; + if (it != end) { + std::istringstream stream(*it++); + while (std::getline(stream, token, ',') != 0) { + const char* start = token.c_str(); + char* end = NULL; + unsigned int id = strtoul(start, &end, 10)-1; // 1-based + if (end == NULL || end == start || *end != '=') + return NULL; // TODO error code: invalid values definition + values[id] = std::string(end+1); + } } - if (strcasecmp(typeStr, "HEX") == 0) { - if (length == 0) - length = 1; - return new StringDataField(name, partType, offset, length, dt_hex, unit, comment); - } - if (strcasecmp(typeStr, "UCH") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 1, dt_uchar, unit, comment, factor, 0xff); - } - if (strcasecmp(typeStr, "SCH") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 1, dt_schar, unit, comment, factor, 0x80); - } - if (strcasecmp(typeStr, "BCD") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 1, dt_bcd, unit, comment, factor, 0xff); // TODO max value 99 - } - if (strcasecmp(typeStr, "D1B") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 1, dt_d1b, unit, comment, factor, 0x80); - } - if (strcasecmp(typeStr, "D1C") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 1, dt_d1c, unit, comment, factor*0.5, 0xff); // TODO max value 100 - } - if (strcasecmp(typeStr, "UIN") == 0) { - if (length != 0 && length != 2) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 2, dt_uint, unit, comment, factor, 0xffff); - } - if (strcasecmp(typeStr, "SIN") == 0) { - if (length != 0 && length != 2) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 2, dt_sint, unit, comment, factor, 0x8000); - } - if (strcasecmp(typeStr, "D2B") == 0) { - if (length != 0 && length != 2) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 2, dt_d2b, unit, comment, factor/256.0, 0x8000); - } - if (strcasecmp(typeStr, "D2C") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 2, dt_d2c, unit, comment, factor/16.0, 0x8000); - } - if (strcasecmp(typeStr, "ULG") == 0) { - if (length != 0 && length != 4) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 4, dt_ulong, unit, comment, factor, 0xffffffff); - } - if (strcasecmp(typeStr, "SLG") == 0) { - if (length != 0 && length != 4) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 4, dt_slong, unit, comment, factor, 0x80000000); - } - if (strcasecmp(typeStr, "FLT") == 0) { - if (length != 0 && length != 2) - return NULL; // TODO error code: invalid pos definition - return new NumericDataField(name, partType, offset, 2, dt_float, unit, comment, factor/1000.0, 0x8000); // TODO replacement - } - if (strcasecmp(typeStr, "BDA") == 0 || strcasecmp(typeStr, "HDA") == 0) { - if (length == 0) - length = 4; - else if (length != 3 && length != 4) - return NULL; // TODO error code: invalid pos definition - return new StringDataField(name, partType, offset, length, dt_date, unit, comment); // TODO better numeric? - } - if (strcasecmp(typeStr, "BDY") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new StringDataField(name, partType, offset, 1, dt_day, unit, comment); // TODO better numeric? - } - if (strcasecmp(typeStr, "BTI") == 0 || strcasecmp(typeStr, "HTI") == 0) { - if (length != 0 && length != 3) - return NULL; // TODO error code: invalid pos definition - return new StringDataField(name, partType, offset, 3, dt_time, unit, comment); // TODO better numeric? - } - if (strcasecmp(typeStr, "TTM") == 0) { - if (length != 0 && length != 1) - return NULL; // TODO error code: invalid pos definition - return new StringDataField(name, partType, offset, 1, dt_tTime, unit, comment); // TODO better numeric? + + for (size_t i = 0; i < sizeof(dataTypes)/sizeof(dataTypes[0]); i++) { + dataType_t dataType = dataTypes[i]; + if (strcasecmp(typeStr, dataType.name) == 0) { + if ((dataType.flags&ADJ) != 0) { + if (length == 0) + length = 1; // minimum length defaults to 1 + else if (length > dataType.numBytes) + return NULL; // invalid length + } + else if (length == 0) + length = dataType.numBytes; + else if (length != dataType.numBytes) + continue; // check for another one with same name but different length + + switch (dataType.type) { + case bt_str: + case bt_hexstr: + case bt_date: // TODO better numeric? + case bt_time: // TODO better numeric? + return new StringDataField(name, partType, offset, length, dataType, unit, comment); + case bt_list: + if (values.empty() == false) { + if (values.begin()->first < dataType.minValueOrLength) + return NULL; // invalid value id + std::map::iterator end = values.end(); + end--; + if (end->first > dataType.maxValueOrLength) + return NULL; // invalid value id + } + else if ((dataType.flags&DAY) != 0) { + for (unsigned int i=0; i 0) @@ -250,82 +227,80 @@ const std::string DataField::parseSymbols(SymbolString& masterData, SymbolString return output.str(); } -bool DataField::formatSymbols(const std::string& value, SymbolString& masterData, SymbolString& slaveData) +bool DataField::write(const std::string& value, SymbolString& masterData, SymbolString& slaveData) { - SymbolString& data = m_partType == pt_masterData ? masterData : slaveData; + SymbolString& output = m_partType == pt_masterData ? masterData : slaveData; switch (m_partType) { case pt_masterData: - break; case pt_slaveData: break; default: return false; // TODO error code } std::istringstream input(value); - if (format(data, input) == false) + if (writeSymbols(input, output) == false) return false; // TODO error code return true; } -bool StringDataField::parse(SymbolString& data, std::ostringstream& output) +bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& output) { - size_t start = m_offset, end = m_offset+m_length; + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; unsigned char ch; - if (end > data.size()) { + if (end > input.size()) return false; // TODO error not enough data available - } - if (m_dataType == dt_time) { // reverse order - for (size_t i = end-1; i >= start; i--) { - ch = data[i]; - if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) - return false; // invalid BCD - if ((i == end-1 && ch > 0x23) || (i != end-1 && ch > 0x59)) - return false; // invalid time - if (i != end-1) - output << ":"; - output << std::setw(2) << std::setfill('0') << std::hex << static_cast(ch); - } - return true; + + if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) + end = start - 1; + start = m_offset + m_length - 1; + incr = -1; } - for (size_t i = start; i < end; i++) { - ch = data[i]; - switch (m_dataType) { - case dt_hex: - if (i != start) - output << " "; + for (size_t pos = start, i = 0; pos != end; pos += incr, i++) { + if (m_length == 4 && i == 2 && m_dataType.type == bt_date) + continue; // skip weekday in between + ch = input[pos]; + if ((m_dataType.flags & BCD) != 0) { + if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) + return false; // invalid BCD + ch = (ch >> 4) * 10 + (ch & 0x0f); + } + switch (m_dataType.type) { + case bt_hexstr: + if (i > 0) + output << ' '; output << std::nouppercase << std::setw(2) << std::hex << std::setfill('0') << static_cast(ch); break; - case dt_date: - if (m_length == 4u && i == m_offset+2u) - break; // skip weekday in between - if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) - return false; // invalid BCD - if (i == end-1) - output << std::hex << (0x2000+ch); - else if (ch < 0x01 || (i == start && ch > 0x31) || (i == start+1 && ch > 0x12)) + case bt_date: + if (i + 1 == m_length) + output << (2000+ch); + else if (ch < 1 || (i == 0 && ch > 31) || (i == 1 && ch > 12)) return false; // invalid date else - output << std::setw(2) << std::setfill('0') << std::hex << static_cast(ch) << "."; + output << std::setw(2) << std::setfill('0') << static_cast(ch) << "."; break; - case dt_day: - if (ch < 1 || ch > 7) - return false; // invalid day - output << days[ch-1]; - break; - case dt_tTime: - if (ch/6 > 23) + case bt_time: + if (m_length == 1) { // truncated time + if (ch > 24*6) + return false; // invalid time + output << std::setw(2) << std::setfill('0') << static_cast(ch/6) << ":" + << std::setw(2) << std::setfill('0') << static_cast((ch%6)*10); + break; + } + if (i > 0) + output << ":"; + if ((i == 0 && ch > 23) || (i > 0 && ch > 59)) return false; // invalid time - output << std::setw(2) << std::setfill('0') << static_cast(ch/6) << ":" - << std::setw(2) << std::setfill('0') << static_cast((ch%6)*10); + output << std::setw(2) << std::setfill('0') << static_cast(ch); break; default: if (ch < 0x20) - ch = 0x20; + ch = m_dataType.replacement; output << static_cast(ch); break; } @@ -334,36 +309,24 @@ bool StringDataField::parse(SymbolString& data, std::ostringstream& output) return true; } -bool StringDataField::format(SymbolString& data, std::istringstream& input) +bool StringDataField::writeSymbols(std::istringstream& input, SymbolString& output) { - size_t start = m_offset, end = m_offset+m_length; + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; const char* str; char* strEnd; - unsigned long int value, hours; - unsigned char ch; - + unsigned long int value = 0, minutes = 0; std::string token; - if (m_dataType == dt_time) { // reverse order - for (size_t i = end-1; i >=start; i--) { - if (std::getline(input, token, ':') == 0) - return false; - str = token.c_str(); - strEnd = NULL; - value = strtoul(str, &strEnd, 16); - if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - if ((value&0xf0) > 0x90 || (value&0x0f) > 0x09) - return false; // invalid BCD - if ((i == end-1 && value > 0x23) || (i != end-1 && value > 0x59)) - return false; // invalid time - data[i] = (unsigned char)value; - } - return true; + + if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) + end = start - 1; + start = m_offset + m_length - 1; + incr = -1; } - for (size_t i = start; i < end; i++) { - switch (m_dataType) { - case dt_hex: + for (size_t pos = start, i = 0; pos != end; pos += incr, i++) { + switch (m_dataType.type) { + case bt_hexstr: while (input.peek()==' ') input.get(); token.clear(); @@ -379,50 +342,23 @@ bool StringDataField::format(SymbolString& data, std::istringstream& input) value = strtoul(str, &strEnd, 16); if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - data[i] = (unsigned char)value; break; - case dt_date: - if (m_length == 4u && i == m_offset+2u) - break; // skip weekday in between + case bt_date: + if (m_length == 4 && i == 2) + continue; // skip weekday in between if (std::getline(input, token, '.') == 0) return false; str = token.c_str(); strEnd = NULL; - value = strtoul(str, &strEnd, 16); + value = strtoul(str, &strEnd, 10); if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - if ((value&0xf0) > 0x90 || (value&0x0f) > 0x09) - return false; // invalid BCD - if (i == end-1) { - if (value>=0x2000) - value -= 0x2000; - if (value>=0x100) - return false; // invalid year - } - else if (value < 1 || (i == start && value > 0x31) || (i == start+1 && value > 0x12)) + if (i + 1 == m_length && value >= 2000) + value -= 2000; + else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) return false; // invalid date - data[i] = (unsigned char)value; break; - case dt_day: - str = input.str().c_str(); - for (ch = 0; ch < 7; ch++) - if (strcasecmp(days[ch], str) == 0) - break; - if (ch == 7) - return false; // invalid day - data[i] = ch+1; - break; - case dt_tTime: - if (std::getline(input, token, ':') == 0) - return false; - str = token.c_str(); - strEnd = NULL; - hours = strtoul(str, &strEnd, 10); - if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - if (hours > 23) - return false; // invalid time - + case bt_time: if (std::getline(input, token, ':') == 0) return false; str = token.c_str(); @@ -430,146 +366,234 @@ bool StringDataField::format(SymbolString& data, std::istringstream& input) value = strtoul(str, &strEnd, 10); if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - if (value > 59 || (value%10) != 0) + if (m_length == 1) { // truncated time + if (std::getline(input, token, ':') == 0) + return false; + str = token.c_str(); + strEnd = NULL; + minutes = strtoul(str, &strEnd, 10); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + if ((minutes % 10) != 0) + return false; // invalid time + value = value*6 + (minutes / 10); + if (value > 24*6) + return false; // invalid time + break; + } + if ((i == 0 && value > 23) || (i > 0 && value > 59)) return false; // invalid time - data[i] = (unsigned char)(hours*6 + value/10); break; default: - ch = input.get(); - if (input.eof() == true || ch < 0x20) - ch = 0x20; - data[i] = ch; + value = input.get(); + if (input.eof() == true || value < 0x20) + value = m_dataType.replacement; break; } + if ((m_dataType.flags & BCD) != 0) { + if (value > 99) + return false; // invalid BCD + value = (value/10)<<4 | (value%10); + } + if (value > 0xff) + return false; + output[pos] = (unsigned char)value; } return true; } -bool NumericDataField::parse(SymbolString& data, std::ostringstream& output) +bool NumericDataField::readRawValue(SymbolString& input, unsigned int& value) { - size_t start = m_offset, end = m_offset+m_length; - unsigned int value = 0; - int signedValue; + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; unsigned char ch; - if (end > data.size()) + if (end > input.size()) return false; // TODO error not enough data available - for (size_t i = start, exp = 1; i < end; i++) { - ch = data[i]; - switch (m_dataType) { - case dt_bcd: - if (ch == m_replacement) { - output << "-"; + if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) + end = start - 1; + start = m_offset + m_length - 1; + incr = -1; + } + + value = 0; + for (size_t pos = start, exp = 1; pos != end; pos += incr) { + ch = input[pos]; + if ((m_dataType.flags & BCD) != 0) { + if (ch == m_dataType.replacement) { + value = m_dataType.replacement; return true; } - else if ((ch&0xf0) > 0x90 || (ch&0x0f) > 0x09) + if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) return false; // invalid BCD - else - value |= ((ch>>4)*10 + (ch&0x0f))*exp; + + ch = (ch >> 4) * 10 + (ch & 0x0f); + value += ch*exp; exp = exp*100; - break; - default: + } + else { value |= ch*exp; exp = exp<<8; - break; } } - if (value == m_replacement) { + return true; +} + +bool NumericDataField::writeRawValue(unsigned int value, SymbolString& output) +{ + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; + unsigned char ch; + + if ((m_dataType.flags&REV) != 0) { // reverted binary representation (most significant byte first) + end = start - 1; + start = m_offset + m_length - 1; + incr = -1; + } + + for (size_t pos = start, exp = 1; pos != end; pos += incr) { + if ((m_dataType.flags & BCD) != 0) { + if (value == m_dataType.replacement) + ch = m_dataType.replacement; + else { + ch = (value/exp)%100; + ch = ((ch/10)<<4) | (ch%10); + } + exp = exp*100; + } + else { + ch = (value/exp)&0xff; + exp = exp<<8; + } + output[pos] = ch; + } + + return true; +} + + +bool NumberDataField::readSymbols(SymbolString& input, std::ostringstream& output) +{ + unsigned int value = 0; + int signedValue; + + if (readRawValue(input, value) == false) + return false; + + if (value == m_dataType.replacement) { output << "-"; return true; } - switch (m_dataType) { - case dt_schar: - case dt_d1b: - signedValue = (char)value; - break; - case dt_d2b: - case dt_d2c: - case dt_sint: - signedValue = (short)value; - break; - case dt_slong: - signedValue = (int)value; - break; - case dt_ulong: - if (m_factor == 1.0) - output << std::setprecision(0) << std::fixed << static_cast(value); + if ((m_dataType.flags&SIG) != 0 && (value & (1 << (m_dataType.numBytes*8 - 1))) != 0) // negative signed value + if (m_dataType.numBytes == 4) + signedValue = (int)value; else - output << std::setprecision(3) << std::fixed << static_cast(value * m_factor); - return true; - default: - signedValue = value; - break; + signedValue = (int)value - (1 << (m_dataType.numBytes*8)); + else { + if (m_dataType.numBytes == 4) { + if (m_factor == 1.0) + output << static_cast(value); + else + output << std::setprecision(3) << std::fixed << static_cast(value * m_factor); + return true; + } + + signedValue = (int)value; } + if (m_factor == 1.0) - output << std::setprecision(0) << std::fixed << static_cast(signedValue); + output << static_cast(signedValue); else output << std::setprecision(3) << std::fixed << static_cast(signedValue * m_factor); return true; } -bool NumericDataField::format(SymbolString& data, std::istringstream& input) +bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& output) { - size_t start = m_offset, end = m_offset+m_length; unsigned int value; - unsigned char ch; const char* str = input.str().c_str(); if (strcasecmp(str, "-") == 0) // replacement value - value = m_replacement; + value = m_dataType.replacement; else { char* strEnd = NULL; - double dvalue = strtod(str, &strEnd); - if (strEnd != str+strlen(str)) - return false; // TODO error code: invalid value - dvalue /= m_factor; - switch (m_dataType) { - case dt_schar: - case dt_sint: - case dt_slong: - case dt_d1b: - case dt_d2b: - case dt_d2c: - if (dvalue < -(1LL<<(8*m_length)) || dvalue >= (1LL<<(8*m_length))) + if (m_factor == 1.0) { + if ((m_dataType.flags&SIG) != 0) { + int signedValue = strtol(str, &strEnd, 10); + if (signedValue < 0 && m_dataType.numBytes != 4) + value = (unsigned int)(signedValue + (1<<(m_dataType.numBytes*8))); + else + value = (unsigned int)signedValue; + } + else + value = strtoul(str, &strEnd, 10); + if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - break; - default: - // no special handling - if (dvalue < 0.0 || dvalue >= (1LL<<(8*m_length))) + } + else { + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - break; - } - value = (unsigned int)dvalue; - } - - for (size_t i = start, exp = 1; i < end; i++) { - switch (m_dataType) { - case dt_bcd: - if (value == m_replacement) - ch = m_replacement; + dvalue /= m_factor; + if ((m_dataType.flags&SIG) != 0) { + if (dvalue < -(1LL<<(8*m_length)) || dvalue >= (1LL<<(8*m_length))) + return false; // TODO error code: invalid value + if (dvalue < 0 && m_dataType.numBytes != 4) + value = (unsigned int)(dvalue + (1<<(m_dataType.numBytes*8))); + else + value = (unsigned int)dvalue; + } else { - ch = (value/exp)%100; - ch = ((ch/10)<<4) | (ch%10); + if (dvalue < 0.0 || dvalue >= (1LL<<(8*m_length))) + return false; // TODO error code: invalid value + value = (unsigned int)dvalue; } - exp = exp*100; - break; - default: - ch = (value/exp)&0xff; - exp = exp<<8; - break; } - data[i] = ch; } + return writeRawValue(value, output); +} + + +bool ValueListDataField::readSymbols(SymbolString& input, std::ostringstream& output) +{ + unsigned int value = 0; + + if (readRawValue(input, value) == false) + return false; + + if (value == m_dataType.replacement) { + output << "-"; + return true; + } + + std::map::iterator it = m_values.find(value); + if (it == m_values.end()) + return false; + + output << it->second; return true; } +bool ValueListDataField::writeSymbols(std::istringstream& input, SymbolString& output) +{ + std::string str; + input >> str; + + for (std::map::iterator it = m_values.begin(); it != m_values.end(); it++) + if (it->second.compare(str) == 0) + return writeRawValue(it->first, output); + + return false; +} + } //namespace diff --git a/src/libebus/data.h b/src/libebus/data.h old mode 100644 new mode 100755 index 756e584a..bd5d40c1 --- a/src/libebus/data.h +++ b/src/libebus/data.h @@ -23,6 +23,7 @@ #include "symbol.h" #include #include +#include namespace libebus { @@ -30,33 +31,39 @@ namespace libebus /** the message part in which a data field is stored. */ enum PartType { - pt_masterData, // stored in master data - //pt_slaveAck, // stored in slave acknowledge // TODO implement if reasonable - pt_slaveData, // stored in slave data - //pt_masterAck // stored in master acknowledge // TODO implement if reasonable - }; + pt_masterData, // stored in master data + pt_slaveData, // stored in slave data + }; -/** the available data types. */ -enum DataType { - dt_string, // string of characters with fixed length>0, filled up with space - dt_hex, // string of hex digits with fixed length>0, separated by space - dt_bcd, // length: 1 byte, binary coded number, string format: "%d" - dt_uchar, - dt_schar, - dt_uint, - dt_sint, - dt_ulong, - dt_slong, - dt_float, - dt_d1b, - dt_d1c, - dt_d2b, - dt_d2c, - dt_date, // length: 4 bytes (with skipped weekday) or 3 bytes (without weekday), string format: "dd.mm.yyyy" - dt_day, // length: 1 byte, string format: "www" (3 character weekday name) - dt_time, // length: 3 bytes, string format: "hh:mm" - dt_tTime // length: 1 byte, string format: "hh:mm" - }; +/** the available base data types. */ +enum BaseType { + bt_str, // text string in a StringDataField + bt_hexstr, // hex digit string in a StringDataField + bt_date, // date in a StringDataField + bt_time, // time in a StringDataField + bt_list, // numeric list value in a ValueListDataField + bt_number // number value in a NumberDataField +}; + +/** flags for dataType_t. */ +const unsigned int ADJ = 0x01; // adjustable length, numBytes is maximum length +const unsigned int BCD = 0x02; // binary representation is BCD +const unsigned int REV = 0x04; // reverted binary representation (most significant byte first) +const unsigned int SIG = 0x08; // signed value +const unsigned int LST = 0x10; // value list is possible (without applied factor) +const unsigned int DAY = 0x20; // default value list is week days + +/** the structure for defining field types with their properties. */ +typedef struct { + const char* name; // field identifier + const unsigned int numBytes; // number of bytes (maximum length if ADJ flag is set) + const BaseType type; // base data type + const unsigned int flags; // flags (e.g. BCD) + const unsigned int replacement; // replacement value (fill-up value for bt_str/bt_hexstr) + const unsigned int minValueOrLength; // minimum binary value (minimum length of string for StringDataField) + const unsigned int maxValueOrLength; // maximum binary value (maximum length of string for StringDataField) + const unsigned int divisor; // divisor for bt_number values (or 0 for non-numeric) +} dataType_t; /** @@ -71,13 +78,13 @@ public: * @param partType the message part in which the field is stored. * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. - * @param dataType the data type. + * @param dataType the data type definition. * @param unit the value unit. * @param comment the field comment. */ DataField(const std::string name, const PartType partType, const unsigned char offset, const unsigned char length, - const DataType dataType, const std::string unit, + const dataType_t dataType, const std::string unit, const std::string comment) : m_name(name), m_partType(partType), m_offset(offset), m_length(length), m_dataType(dataType), m_unit(unit), @@ -98,35 +105,35 @@ public: std::vector::iterator& it, const std::vector::iterator end); /** - * @brief Parses the value from the master or slave @a SymbolString. - * @param masterData the unescaped master data @a SymbolString to parse from. - * @param slaveData the unescaped slave data @a SymbolString to parse from. - * @return the parsed value as string. + * @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. + * @return the formatted value as string. */ - const std::string parseSymbols(SymbolString& masterData, SymbolString& slaveData, bool verbose=false); + const std::string read(SymbolString& masterData, SymbolString& slaveData, bool verbose=false); /** - * @brief Formats the value to the master or slave @a SymbolString. - * @param masterData the unescaped master data @a SymbolString to format to. - * @param slaveData the unescaped slave data @a SymbolString to format to. - * @param value the value as string. + * @brief Writes the value to the master or slave @a SymbolString. + * @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 value the formatted value as string. */ - bool formatSymbols(const std::string& value, SymbolString& masterData, SymbolString& slaveData); + bool write(const std::string& value, SymbolString& masterData, SymbolString& slaveData); protected: /** - * @brief Internal method doing the actual parse for the individual data type. - * @param data the unescaped data SymbolString to parse from. - * @param output the ostringstream to append to. + * @brief Internal method for reading the field from a @a SymbolString. + * @param input the unescaped @a SymbolString to read the binary value from. + * @param output the ostringstream to append the formatted value to. * @return true if the value was parsed successfully. */ - virtual bool parse(SymbolString& data, std::ostringstream& output) = 0; + virtual bool readSymbols(SymbolString& input, std::ostringstream& output) = 0; /** - * @brief Internal method doing the actual format for the individual data type. - * @param data the unescaped data SymbolString to format to. - * @param input the istringstream to interpret. + * @brief Internal method for writing the field to a @a SymbolString. + * @param input the istringstream to parse the formatted value from. + * @param output the unescaped @a SymbolString to write the binary value to. * @return true if the value was formatted successfully. */ - virtual bool format(SymbolString& data, std::istringstream& input) = 0; + virtual bool writeSymbols(std::istringstream& input, SymbolString& output) = 0; /** the field name. */ const std::string m_name; @@ -136,9 +143,8 @@ protected: const unsigned char m_offset; /** the number of symbols in the message part in which the field is stored. */ const unsigned char m_length; - /** the data type. */ - const DataType m_dataType; - //std::vector m_valid;//TODO add list of possible values + /** the data type definition. */ + const dataType_t m_dataType; /** the value unit. */ const std::string m_unit; /** the field comment. */ @@ -158,13 +164,13 @@ public: * @param partType the message part in which the field is stored. * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. - * @param dataType the data type. + * @param dataType the data type definition. * @param unit the value unit. * @param comment the field comment. */ StringDataField(const std::string name, const PartType partType, const unsigned char offset, const unsigned char length, - const DataType dataType, const std::string unit, + const dataType_t dataType, const std::string unit, const std::string comment) : DataField(name, partType, offset, length, dataType, unit, comment) {} /** @@ -173,8 +179,8 @@ public: virtual ~StringDataField() {} protected: - virtual bool parse(SymbolString& data, std::ostringstream& output); - virtual bool format(SymbolString& data, std::istringstream& input); + virtual bool readSymbols(SymbolString& input, std::ostringstream& output); + virtual bool writeSymbols(std::istringstream& input, SymbolString& output); }; @@ -191,32 +197,113 @@ public: * @param partType the message part in which the field is stored. * @param offset the offset to the first symbol in the message part in which the field is stored. * @param length the number of symbols in the message part in which the field is stored. - * @param dataType the data type. + * @param dataType the data type definition. * @param comment the field comment. * @param unit the value unit. - * @param factor the factor to apply on the value. * @param replacement the (binary) replacement value to use if the value is not set. */ NumericDataField(const std::string name, const PartType partType, const unsigned char offset, const unsigned char length, - const DataType dataType, const std::string unit, - const std::string comment, const float factor, - const unsigned int replacement) - : DataField(name, partType, offset, length, dataType, unit, comment), - m_factor(factor), m_replacement(replacement) {} + const dataType_t dataType, const std::string unit, + const std::string comment) + : DataField(name, partType, offset, length, dataType, unit, comment) {} /** * @brief Destructor. */ virtual ~NumericDataField() {} protected: - virtual bool parse(SymbolString& data, std::ostringstream& output); - virtual bool format(SymbolString& data, std::istringstream& input); + /** + * @brief Internal method for reading the raw value from a @a SymbolString. + * @param input the unescaped @a SymbolString to read the binary value from. + * @param value the variable in which to store the raw value. + * @return true if the value was read successfully. + */ + bool readRawValue(SymbolString& input, unsigned int& value); + /** + * @brief Internal method for writing the raw value to a @a SymbolString. + * @param value the raw value to write. + * @param output the unescaped @a SymbolString to write the binary value to. + * @return true if the value was written successfully. + */ + bool writeRawValue(unsigned int value, SymbolString& output); + +}; + +/** + * @brief Base class for all numeric data fields with a number representation. + */ +class NumberDataField : public NumericDataField +{ +public: + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param partType the message part in which the field is stored. + * @param offset the offset to the first symbol in the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param dataType the data type definition. + * @param comment the field comment. + * @param unit the value unit. + * @param replacement the (binary) replacement value to use if the value is not set. + * @param factor the factor to apply on the value. + */ + NumberDataField(const std::string name, const PartType partType, + const unsigned char offset, const unsigned char length, + const dataType_t dataType, const std::string unit, + const std::string comment, const float factor) + : NumericDataField(name, partType, offset, length, dataType, unit, comment), + m_factor(factor / dataType.divisor) {} + /** + * @brief Destructor. + */ + virtual ~NumberDataField() {} + +protected: + virtual bool readSymbols(SymbolString& input, std::ostringstream& output); + virtual bool writeSymbols(std::istringstream& input, SymbolString& output); /** the factor to apply on the value. */ const float m_factor; - /** the (binary) replacement value to use if the value is not set. */ - const unsigned int m_replacement; + +}; + +/** + * @brief A numeric data field with a list of value=text assignments and a string representation. + */ +class ValueListDataField : public NumericDataField +{ +public: + /** + * @brief Constructs a new instance. + * @param name the field name. + * @param partType the message part in which the field is stored. + * @param offset the offset to the first symbol in the message part in which the field is stored. + * @param length the number of symbols in the message part in which the field is stored. + * @param dataType the data type definition. + * @param comment the field comment. + * @param unit the value unit. + * @param factor the factor to apply on the value. + * @param replacement the (binary) replacement value to use if the value is not set. + * @param values the value=text assignments. + */ + ValueListDataField(const std::string name, const PartType partType, + const unsigned char offset, const unsigned char length, + const dataType_t dataType, const std::string unit, + const std::string comment, const std::map values) + : NumericDataField(name, partType, offset, length, dataType, unit, comment), + m_values(values) {} + /** + * @brief Destructor. + */ + virtual ~ValueListDataField() {} + +protected: + virtual bool readSymbols(SymbolString& input, std::ostringstream& output); + virtual bool writeSymbols(std::istringstream& input, SymbolString& output); + + /** the value=text assignments. */ + std::map m_values; }; diff --git a/src/test/test_data.cpp b/src/test/test_data.cpp old mode 100644 new mode 100755 index 367a45c0..8c8cfae4 --- a/src/test/test_data.cpp +++ b/src/test/test_data.cpp @@ -25,7 +25,6 @@ using namespace libebus; int main () { - //TODO dt_float, //TODO dt_d1b, //TODO dt_d1c, //TODO dt_d2c, @@ -33,23 +32,26 @@ int main () //name;position(s);type;factor;unit;comment // {"temp;1;d2b;;°C;Aussentemperatur","temp=18.004 °C [Aussentemperatur]","10fe070009019258042126100714cc", "00"}, // {"zeit;1;ttm;2;Uhr;","zeit=22:40 Uhr","10feffff0188", "00"}, - {"hex;1-10;hex","53 70 65 69 63 68 65 72 20 20", "10fe07000a53706569636865722020", "00"}, - {"zeit;1;bti","21:04:58","10fe070009580421", "00"}, - {"datum;1;bda","26.10.2014","10fe07000926100714", "00"}, - {"datum;1-3;bda","26.10.2014","10fe070003261014", "00"}, - {"tag;1;bdy","Sun","10fe07000307", "00"}, - {"temp;1;d2b","18.004","10fe0700090112", "00"}, - {"zeit;1;ttm","22:40","10feffff0188", "00"}, - {"bcd;1;bcd","26","10feffff0126", "00"}, - {"bcd;1;bcd","-","10feffff01ff", "00"}, - {"uch;1;uch","38","10feffff0126", "00"}, - {"sch;1;sch","-90","10feffff01a6", "00"}, - {"uin;1;uin","38","10feffff022600", "00"}, - {"sin;1;sin","-90","10feffff02a6ff", "00"}, - {"ulg;1;ulg","38","10feffff0426000000", "00"}, - {"slg;1;slg","-90","10feffff04a6ffffff", "00"}, - {"str;1-9;str","hallo Du!","10feffff0868616c6c6f20447521", "00"}, - {"str;1-9;str","hallo Du ","10feffff0868616c6c6f20447500", "00"}, + {"x;1-10;hex","53 70 65 69 63 68 65 72 20 20", "10fe07000a53706569636865722020", "00"}, + {"x;1;bti","21:04:58","10fe070009580421", "00"}, + {"x;1;bda","26.10.2014","10fe07000926100714", "00"}, + {"x;1-3;bda","26.10.2014","10fe070003261014", "00"}, + {"x;1;hdy","Sun","10fe07000307", "00"}, + {"x;1;bdy","Sun","10fe07000306", "00"}, + {"x;1;d2b","18.004","10fe0700090112", "00"}, + {"x;1;ttm","22:40","10feffff0188", "00"}, + {"x;1;bcd","26","10feffff0126", "00"}, + {"x;1;bcd","-","10feffff01ff", "00"}, + {"x;1;uch","38","10feffff0126", "00"}, + {"x;1;sch","-90","10feffff01a6", "00"}, + {"x;1;d1b","-90","10feffff01a6", "00"}, + {"x;1;uin","38","10feffff022600", "00"}, + {"x;1;sin","-90","10feffff02a6ff", "00"}, + {"x;1;ulg","38","10feffff0426000000", "00"}, + {"x;1;slg","-90","10feffff04a6ffffff", "00"}, + {"x;1;flt","-0.090","10feffff02a6ff", "00"}, + {"x;1-9;str","hallo Du!","10feffff0868616c6c6f20447521", "00"}, + {"x;1-9;str","hallo Du ","10feffff0868616c6c6f20447520", "00"}, }; for (size_t i = 0; i < sizeof(checks)/sizeof(checks[0]); i++) { std::istringstream isstr(checks[i][0]); @@ -72,7 +74,7 @@ int main () std::cout << "create \"" << checks[i][0] << "\" successful" << std::endl; - std::string gotStr = field->parseSymbols(mstr, sstr); + std::string gotStr = field->read(mstr, sstr); if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) std::cout << "parse successful: " << gotStr << std::endl; @@ -82,7 +84,7 @@ int main () SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); - if (field->formatSymbols(gotStr, writeMstr, writeSstr) == false) + if (field->write(gotStr, writeMstr, writeSstr) == false) std::cout << "format failed" << std::endl; else { if (mstr == writeMstr && sstr == writeSstr) From e7e50497c6ed612f68a438d18be37c6bdbb15383 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Nov 2014 22:15:06 +0100 Subject: [PATCH 09/10] file mode --- src/libebus/data.cpp | 0 src/libebus/data.h | 0 src/libebus/symbol.h | 0 src/test/test_data.cpp | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/libebus/data.cpp mode change 100755 => 100644 src/libebus/data.h mode change 100755 => 100644 src/libebus/symbol.h mode change 100755 => 100644 src/test/test_data.cpp diff --git a/src/libebus/data.cpp b/src/libebus/data.cpp old mode 100755 new mode 100644 diff --git a/src/libebus/data.h b/src/libebus/data.h old mode 100755 new mode 100644 diff --git a/src/libebus/symbol.h b/src/libebus/symbol.h old mode 100755 new mode 100644 diff --git a/src/test/test_data.cpp b/src/test/test_data.cpp old mode 100755 new mode 100644 From 38d68cc94da03b49b43f62f538305ac84a7035b6 Mon Sep 17 00:00:00 2001 From: john30 Date: Sun, 2 Nov 2014 22:44:44 +0100 Subject: [PATCH 10/10] added missing tests, use factorStr instead of separate column for key=value definition of lists --- src/libebus/data.cpp | 42 +++++++++++++++++++++--------------------- src/test/test_data.cpp | 16 ++++++++-------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/libebus/data.cpp b/src/libebus/data.cpp index a23a1be6..c2065727 100644 --- a/src/libebus/data.cpp +++ b/src/libebus/data.cpp @@ -36,12 +36,12 @@ static const dataType_t dataTypes[] = { {"HEX",16, bt_hexstr,ADJ, 0, 2, 47, 0}, // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d {"BDA", 4, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) {"BDA", 3, bt_date, BCD, 0, 10, 10, 0}, // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) - //{"HDA", 3, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) - //{"HDA", 4, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x0101WW00 - 0x3112WW99, WW is ignored weekday) + {"HDA", 4, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99, WW is ignored weekday) // TODO remove duplicate of BDA + {"HDA", 3, bt_date, 0, 0, 10, 10, 0}, // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99) // TODO remove duplicate of BDA {"BTI", 3, bt_time,BCD|REV,0, 8, 8, 0}, // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23) {"TTM", 1, bt_time, 0, 0, 5, 5, 0}, // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer) {"BDY", 1, bt_list,BCD|DAY,0, 0, 6, 0}, // weekday, "Mon" - "Sun" - {"HDY", 1, bt_list,BCD|DAY,0, 1, 7, 0}, // weekday, "Mon" - "Sun" // TODO different range + {"HDY", 1, bt_list,BCD|DAY,0, 1, 7, 0}, // weekday, "Mon" - "Sun" {"BCD", 1, bt_number,BCD|LST,0xff, 0, 0x99, 1}, // unsigned decimal in BCD, 0 - 99 {"UCH", 1, bt_number, LST, 0xff, 0, 0xff, 1}, // unsigned integer, 0 - 255 {"SCH", 1, bt_number, SIG, 0x80, 0x80, 0x7f, 1}, // signed integer, -128 - +127 @@ -54,7 +54,7 @@ static const dataType_t dataTypes[] = { {"D2C", 2, bt_number, SIG, 0x8000, 0x8001, 0x7fff, 16}, // signed number (fraction 1/16), -2047.9 - +2047.9 {"ULG", 4, bt_number, LST, 0xffffffff, 0, 0xffffffff, 1}, // unsigned integer, 0 - 4294967295 {"SLG", 4, bt_number, SIG, 0x80000000, 0x80000000, 0xffffffff, 1}, // signed integer, -2147483648 - +2147483647 -}; +}; // TODO check value range for numberdf /** the week day names. */ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; @@ -118,14 +118,27 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes const char* typeStr = (*it++).c_str(); + std::map values; if (it == end) factor = 1.0; else { std::string factorStr = *it++; - if (factorStr.length() > 0 && factorStr.find_first_not_of("0123456789.") == std::string::npos) - factor = static_cast(strtod(factorStr.c_str(), NULL)); - else + if (factorStr.empty()) factor = 1.0; + else if (factorStr.find_first_not_of("0123456789.") == std::string::npos) + factor = static_cast(strtod(factorStr.c_str(), NULL)); + else { + factor = 1.0; + std::istringstream stream(factorStr); + while (std::getline(stream, token, ',') != 0) { + const char* start = token.c_str(); + char* end = NULL; + unsigned int id = strtoul(start, &end, 10); + if (end == NULL || end == start || *end != '=') + return NULL; // TODO error code: invalid values definition + values[id] = std::string(end+1); + } + } } if (it == end) @@ -145,19 +158,6 @@ DataField* DataField::create(const unsigned char dstAddress, const bool isSetMes comment.clear(); } - std::map values; - if (it != end) { - std::istringstream stream(*it++); - while (std::getline(stream, token, ',') != 0) { - const char* start = token.c_str(); - char* end = NULL; - unsigned int id = strtoul(start, &end, 10)-1; // 1-based - if (end == NULL || end == start || *end != '=') - return NULL; // TODO error code: invalid values definition - values[id] = std::string(end+1); - } - } - for (size_t i = 0; i < sizeof(dataTypes)/sizeof(dataTypes[0]); i++) { dataType_t dataType = dataTypes[i]; if (strcasecmp(typeStr, dataType.name) == 0) { @@ -541,7 +541,7 @@ bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& outp double dvalue = strtod(str, &strEnd); if (strEnd != str+strlen(str)) return false; // TODO error code: invalid value - dvalue /= m_factor; + dvalue = dvalue / m_factor + 0.5; // round if ((m_dataType.flags&SIG) != 0) { if (dvalue < -(1LL<<(8*m_length)) || dvalue >= (1LL<<(8*m_length))) return false; // TODO error code: invalid value diff --git a/src/test/test_data.cpp b/src/test/test_data.cpp index 8c8cfae4..19310cc2 100644 --- a/src/test/test_data.cpp +++ b/src/test/test_data.cpp @@ -25,9 +25,6 @@ using namespace libebus; int main () { - //TODO dt_d1b, - //TODO dt_d1c, - //TODO dt_d2c, std::string checks[][4] = { //name;position(s);type;factor;unit;comment // {"temp;1;d2b;;°C;Aussentemperatur","temp=18.004 °C [Aussentemperatur]","10fe070009019258042126100714cc", "00"}, @@ -39,12 +36,14 @@ int main () {"x;1;hdy","Sun","10fe07000307", "00"}, {"x;1;bdy","Sun","10fe07000306", "00"}, {"x;1;d2b","18.004","10fe0700090112", "00"}, + {"x;1;d2c","288.062","10fe0700090112", "00"}, {"x;1;ttm","22:40","10feffff0188", "00"}, {"x;1;bcd","26","10feffff0126", "00"}, {"x;1;bcd","-","10feffff01ff", "00"}, {"x;1;uch","38","10feffff0126", "00"}, {"x;1;sch","-90","10feffff01a6", "00"}, {"x;1;d1b","-90","10feffff01a6", "00"}, + {"x;1;d1c","19.500","10feffff0127", "00"}, {"x;1;uin","38","10feffff022600", "00"}, {"x;1;sin","-90","10feffff02a6ff", "00"}, {"x;1;ulg","38","10feffff0426000000", "00"}, @@ -52,6 +51,7 @@ int main () {"x;1;flt","-0.090","10feffff02a6ff", "00"}, {"x;1-9;str","hallo Du!","10feffff0868616c6c6f20447521", "00"}, {"x;1-9;str","hallo Du ","10feffff0868616c6c6f20447520", "00"}, + {"new;1;uch;1=test,2=high,3=off,4=on","on","10feffff0104", "00"}, }; for (size_t i = 0; i < sizeof(checks)/sizeof(checks[0]); i++) { std::istringstream isstr(checks[i][0]); @@ -77,20 +77,20 @@ int main () std::string gotStr = field->read(mstr, sstr); if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) - std::cout << "parse successful: " << gotStr << std::endl; + std::cout << "read successful: " << gotStr << std::endl; else - std::cout << "parse invalid: got " << gotStr + std::cout << "read invalid: got " << gotStr << ", expected " << expectStr << std::endl; SymbolString writeMstr = SymbolString(mstr.getDataStr().substr(0, 10), false); SymbolString writeSstr = SymbolString(sstr.getDataStr().substr(0, 2), false); if (field->write(gotStr, writeMstr, writeSstr) == false) - std::cout << "format failed" << std::endl; + std::cout << "write failed" << std::endl; else { if (mstr == writeMstr && sstr == writeSstr) - std::cout << "format successful" << std::endl; + std::cout << "write successful" << std::endl; else { - std::cout << "format invalid: "; + std::cout << "write invalid: "; if (mstr == writeMstr) std::cout << "master OK"; else