diff --git a/.gitignore b/.gitignore index 119040b6..ef3fe657 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ Makefile.in /src/test/test_decode /src/test/test_encode /src/test/test_symbol +/src/test/test_data diff --git a/src/ebusd/ebusloop.cpp b/src/ebusd/ebusloop.cpp index d6d8a26b..6832d7b7 100644 --- a/src/ebusd/ebusloop.cpp +++ b/src/ebusd/ebusloop.cpp @@ -114,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; diff --git a/src/libebus/Makefile.am b/src/libebus/Makefile.am index e5451b7b..e78d40bc 100644 --- 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/bus.cpp b/src/libebus/bus.cpp index 5fa3fcc9..e12d9ac2 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..d752dbd7 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; diff --git a/src/libebus/buscommand.cpp b/src/libebus/buscommand.cpp index 0a09c52e..e7c35dd0 100644 --- a/src/libebus/buscommand.cpp +++ b/src/libebus/buscommand.cpp @@ -22,9 +22,8 @@ namespace libebus { - -BusCommand::BusCommand(const std::string command, const bool isPoll) - : m_isPoll(isPoll), m_command(command), m_resultCode(RESULT_OK) +BusCommand::BusCommand(const std::string commandStr, const bool isPoll) + : m_isPoll(isPoll), m_command(commandStr), m_result(), m_resultCode(RESULT_OK) { unsigned char dstAddress = m_command[1]; @@ -51,16 +50,16 @@ 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"; } else { result = "success"; } - } else { - result = "error"; } + else + result = "error: "+std::string(getResultCodeCStr()); return result; } diff --git a/src/libebus/data.cpp b/src/libebus/data.cpp new file mode 100644 index 00000000..c2065727 --- /dev/null +++ b/src/libebus/data.cpp @@ -0,0 +1,599 @@ +/* + * 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 +{ + +/** 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", 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" + {"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 +}; // TODO check value range for numberdf + +/** 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, + 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 || name.size() == 0) + return NULL; + const char* posStr = (*it++).c_str(); + if (it == end) + return NULL; + 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; + 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; + } + } + + const char* typeStr = (*it++).c_str(); + + std::map values; + if (it == end) + factor = 1.0; + else { + std::string factorStr = *it++; + 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) + 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(); + } + + 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) + output << " " << m_unit; + if (verbose && m_comment.length() > 0) + output << " [" << m_comment << "]"; + return output.str(); +} + +bool DataField::write(const std::string& value, SymbolString& masterData, SymbolString& slaveData) +{ + SymbolString& output = m_partType == pt_masterData ? masterData : slaveData; + switch (m_partType) { + case pt_masterData: + case pt_slaveData: + break; + default: + return false; // TODO error code + } + std::istringstream input(value); + if (writeSymbols(input, output) == false) + return false; // TODO error code + return true; +} + + + +bool StringDataField::readSymbols(SymbolString& input, std::ostringstream& output) +{ + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; + unsigned char ch; + + if (end > input.size()) + return false; // TODO error not enough data available + + 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, 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 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') << static_cast(ch) << "."; + break; + 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); + break; + default: + if (ch < 0x20) + ch = m_dataType.replacement; + output << static_cast(ch); + break; + } + } + + return true; +} + +bool StringDataField::writeSymbols(std::istringstream& input, SymbolString& output) +{ + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; + const char* str; + char* strEnd; + unsigned long int value = 0, minutes = 0; + std::string token; + + 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, i = 0; pos != end; pos += incr, i++) { + switch (m_dataType.type) { + case bt_hexstr: + 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 + break; + 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, 10); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + 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 + break; + case bt_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 (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 + break; + default: + 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::readRawValue(SymbolString& input, unsigned int& value) +{ + size_t start = m_offset, end = m_offset + m_length; + int incr = 1; + unsigned char ch; + + if (end > input.size()) + return false; // TODO error not enough data available + + 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; + } + if ((ch & 0xf0) > 0x90 || (ch & 0x0f) > 0x09) + return false; // invalid BCD + + ch = (ch >> 4) * 10 + (ch & 0x0f); + value += ch*exp; + exp = exp*100; + } + else { + value |= ch*exp; + exp = exp<<8; + } + } + 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; + } + + 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 + 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 << static_cast(signedValue); + else + output << std::setprecision(3) << std::fixed << static_cast(signedValue * m_factor); + + return true; +} + +bool NumberDataField::writeSymbols(std::istringstream& input, SymbolString& output) +{ + unsigned int value; + + const char* str = input.str().c_str(); + if (strcasecmp(str, "-") == 0) + // replacement value + value = m_dataType.replacement; + else { + char* strEnd = NULL; + 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 + } + else { + char* strEnd = NULL; + double dvalue = strtod(str, &strEnd); + if (strEnd != str+strlen(str)) + return false; // TODO error code: invalid value + 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 + if (dvalue < 0 && m_dataType.numBytes != 4) + value = (unsigned int)(dvalue + (1<<(m_dataType.numBytes*8))); + else + value = (unsigned int)dvalue; + } + else { + if (dvalue < 0.0 || dvalue >= (1LL<<(8*m_length))) + return false; // TODO error code: invalid value + value = (unsigned int)dvalue; + } + } + } + + 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 new file mode 100644 index 00000000..bd5d40c1 --- /dev/null +++ b/src/libebus/data.h @@ -0,0 +1,313 @@ +/* + * 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 +#include + +namespace libebus +{ + + +/** the message part in which a data field is stored. */ +enum PartType { + pt_masterData, // stored in master data + pt_slaveData, // stored in slave data + }; + +/** 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; + + +/** + * @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 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_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), + 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 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 read(SymbolString& masterData, SymbolString& slaveData, bool verbose=false); + /** + * @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 write(const std::string& value, SymbolString& masterData, SymbolString& slaveData); + +protected: + /** + * @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 readSymbols(SymbolString& input, std::ostringstream& output) = 0; + /** + * @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 writeSymbols(std::istringstream& input, SymbolString& output) = 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 definition. */ + const dataType_t m_dataType; + /** 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 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_t dataType, const std::string unit, + const std::string comment) + : DataField(name, partType, offset, length, dataType, unit, comment) {} + /** + * @brief Destructor. + */ + virtual ~StringDataField() {} + +protected: + virtual bool readSymbols(SymbolString& input, std::ostringstream& output); + virtual bool writeSymbols(std::istringstream& input, SymbolString& output); + +}; + + +/** + * @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 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. + */ + NumericDataField(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) + : DataField(name, partType, offset, length, dataType, unit, comment) {} + /** + * @brief Destructor. + */ + virtual ~NumericDataField() {} + +protected: + /** + * @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; + +}; + +/** + * @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; + +}; + + +} //namespace + +#endif // LIBEBUS_DATA_H_ diff --git a/src/libebus/result.h b/src/libebus/result.h index dd669e8f..7a439382 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 2d35aa19..a1110157 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 9fd31508..833e1c1e 100644 --- a/src/libebus/symbol.h +++ b/src/libebus/symbol.h @@ -37,72 +37,62 @@ 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, 0); 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. @@ -116,7 +106,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: /** @@ -129,6 +119,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/Makefile.am b/src/test/Makefile.am index b2befdc6..8d9c0746 100644 --- 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..19310cc2 --- /dev/null +++ b/src/test/test_data.cpp @@ -0,0 +1,110 @@ +/* + * 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 () +{ + 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"}, + {"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;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"}, + {"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"}, + {"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]); + 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->read(mstr, sstr); + + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "read successful: " << gotStr << std::endl; + else + 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 << "write failed" << std::endl; + else { + if (mstr == writeMstr && sstr == writeSstr) + std::cout << "write successful" << std::endl; + else { + std::cout << "write 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; + +} diff --git a/src/test/test_symbol.cpp b/src/test/test_symbol.cpp index 625e93cc..b68ef9df 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