diff --git a/.gitignore b/.gitignore index ec26f947..b7906a5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /INSTALL -/Makefile -/Makefile.in +Makefile +Makefile.in /build/* /aclocal.m4 /autom4te.cache/ diff --git a/Makefile.am b/Makefile.am index 1220e389..7146e27c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,32 +1,9 @@ -AUTOMAKE_OPTIONS = subdir-objects - -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} - -AM_CXXFLAGS = $(LIBEBUS_CFLAGS) -I$(top_srcdir)/lib -fpic -Wall -Wextra - -bin_PROGRAMS = ebusd \ - ebusctl - -ebusd_SOURCES = lib/appl.cpp \ - lib/daemon.cpp \ - lib/tcpsocket.cpp \ - lib/thread.cpp \ - lib/logger.cpp \ - src/connection.cpp \ - src/network.cpp \ - src/ebusloop.cpp \ - src/baseloop.cpp \ - src/main.cpp - -ebusd_LDADD = $(LIBEBUS_LIBS) \ - -lpthread - -dist_noinst_SCRIPTS = autogen.sh - - -ebusctl_SOURCES = lib/appl.cpp \ - lib/tcpsocket.cpp \ - tools/ebusctl.cpp - -ebusctl_LDADD = $(LIBEBUS_LIBS) +SUBDIRS = src/libebus src/test src/libcore src/ebusd src/tools +distclean-local: + -rm -rf autom4te.cache + -rm -f aclocal.m4 + -rm -f config.h.in + -rm -f configure + -rm -f Makefile.in + -rm -rf build diff --git a/autogen.sh b/autogen.sh index 046ee7d9..7b7afa89 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,7 +2,6 @@ test -n "$srcdir" || srcdir=`dirname "$0"` test -n "$srcdir" || srcdir=. -mkdir -p "$srcdir/m4" mkdir -p "$srcdir/build" autoreconf --force --install --verbose "$srcdir" diff --git a/configure.ac b/configure.ac index ef46a0ce..360b26cc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,17 +1,21 @@ AC_PREREQ([2.65]) AC_INIT([ebusd], [0.4.0], [roland.jax@liwest.at], [ebusd], [https://github.com/yuhu-/ebusd]) -AC_CONFIG_SRCDIR([src/main.cpp]) -AC_CONFIG_AUX_DIR([build]) -AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign]) - -AC_CONFIG_MACRO_DIR([m4]) - -AC_CONFIG_HEADERS([config.h]) - AC_PROG_CXX -PKG_CHECK_MODULES([LIBEBUS], [libebus-0.2 >= 0.2.0]) +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_SRCDIR([src/ebusd/main.cpp]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile + src/libebus/Makefile + src/test/Makefile + src/libcore/Makefile + src/ebusd/Makefile + src/tools/Makefile]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign]) + +AM_PROG_AR +AC_PROG_RANLIB -AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/src/ebusd/Makefile.am b/src/ebusd/Makefile.am new file mode 100644 index 00000000..30b115e5 --- /dev/null +++ b/src/ebusd/Makefile.am @@ -0,0 +1,22 @@ +AM_CXXFLAGS = -fpic \ + -Wall \ + -Wextra \ + -I$(top_srcdir)/src/libcore \ + -I$(top_srcdir)/src/libebus + +bin_PROGRAMS = ebusd + +ebusd_SOURCES = connection.cpp \ + network.cpp \ + ebusloop.cpp \ + baseloop.cpp \ + main.cpp + +ebusd_LDADD = $(top_srcdir)/src/libcore/libcore.a \ + $(top_srcdir)/src/libebus/libebus.a \ + -lpthread + +distclean-local: + -rm -f Makefile.in + -rm -rf .libs + diff --git a/src/baseloop.cpp b/src/ebusd/baseloop.cpp similarity index 99% rename from src/baseloop.cpp rename to src/ebusd/baseloop.cpp index f0042543..d3a4426d 100644 --- a/src/baseloop.cpp +++ b/src/ebusd/baseloop.cpp @@ -18,6 +18,7 @@ */ #include "baseloop.h" +#include "configfile.h" #include "logger.h" #include "appl.h" diff --git a/src/baseloop.h b/src/ebusd/baseloop.h similarity index 98% rename from src/baseloop.h rename to src/ebusd/baseloop.h index 495e9fff..b2de5b7c 100644 --- a/src/baseloop.h +++ b/src/ebusd/baseloop.h @@ -20,7 +20,7 @@ #ifndef BASELOOP_H_ #define BASELOOP_H_ -#include "libebus.h" +#include "commands.h" #include "network.h" #include "ebusloop.h" diff --git a/src/connection.cpp b/src/ebusd/connection.cpp similarity index 100% rename from src/connection.cpp rename to src/ebusd/connection.cpp diff --git a/src/connection.h b/src/ebusd/connection.h similarity index 100% rename from src/connection.h rename to src/ebusd/connection.h index b861ce89..4abf2da9 100644 --- a/src/connection.h +++ b/src/ebusd/connection.h @@ -22,9 +22,9 @@ #include "tcpsocket.h" #include "wqueue.h" -#include "message.h" #include "notify.h" #include "thread.h" +#include "message.h" class Connection : public Thread { diff --git a/src/ebusloop.cpp b/src/ebusd/ebusloop.cpp similarity index 100% rename from src/ebusloop.cpp rename to src/ebusd/ebusloop.cpp diff --git a/src/ebusloop.h b/src/ebusd/ebusloop.h similarity index 97% rename from src/ebusloop.h rename to src/ebusd/ebusloop.h index d65c4b27..7ae993ae 100644 --- a/src/ebusloop.h +++ b/src/ebusd/ebusloop.h @@ -20,7 +20,8 @@ #ifndef EBUSLOOP_H_ #define EBUSLOOP_H_ -#include "libebus.h" +#include "bus.h" +#include "commands.h" #include "wqueue.h" #include "thread.h" diff --git a/src/main.cpp b/src/ebusd/main.cpp similarity index 99% rename from src/main.cpp rename to src/ebusd/main.cpp index 9ad1056e..303a40ef 100644 --- a/src/main.cpp +++ b/src/ebusd/main.cpp @@ -17,7 +17,6 @@ * along with ebusd. If not, see http://www.gnu.org/licenses/. */ -#include "libebus.h" #include "logger.h" #include "daemon.h" #include "appl.h" diff --git a/src/message.h b/src/ebusd/message.h similarity index 100% rename from src/message.h rename to src/ebusd/message.h diff --git a/src/network.cpp b/src/ebusd/network.cpp similarity index 100% rename from src/network.cpp rename to src/ebusd/network.cpp diff --git a/src/network.h b/src/ebusd/network.h similarity index 100% rename from src/network.h rename to src/ebusd/network.h diff --git a/src/libcore/Makefile.am b/src/libcore/Makefile.am new file mode 100644 index 00000000..f7497681 --- /dev/null +++ b/src/libcore/Makefile.am @@ -0,0 +1,14 @@ +AM_CXXFLAGS = -fpic \ + -Wall \ + -Wextra + +noinst_LIBRARIES = libcore.a + +libcore_a_SOURCES = appl.cpp \ + daemon.cpp \ + logger.cpp \ + thread.cpp \ + tcpsocket.cpp + +distclean-local: + -rm -f Makefile.in diff --git a/lib/appl.cpp b/src/libcore/appl.cpp similarity index 100% rename from lib/appl.cpp rename to src/libcore/appl.cpp diff --git a/lib/appl.h b/src/libcore/appl.h similarity index 100% rename from lib/appl.h rename to src/libcore/appl.h diff --git a/lib/daemon.cpp b/src/libcore/daemon.cpp similarity index 100% rename from lib/daemon.cpp rename to src/libcore/daemon.cpp diff --git a/lib/daemon.h b/src/libcore/daemon.h similarity index 100% rename from lib/daemon.h rename to src/libcore/daemon.h diff --git a/lib/logger.cpp b/src/libcore/logger.cpp similarity index 100% rename from lib/logger.cpp rename to src/libcore/logger.cpp diff --git a/lib/logger.h b/src/libcore/logger.h similarity index 100% rename from lib/logger.h rename to src/libcore/logger.h diff --git a/lib/notify.h b/src/libcore/notify.h similarity index 100% rename from lib/notify.h rename to src/libcore/notify.h diff --git a/lib/tcpsocket.cpp b/src/libcore/tcpsocket.cpp similarity index 100% rename from lib/tcpsocket.cpp rename to src/libcore/tcpsocket.cpp diff --git a/lib/tcpsocket.h b/src/libcore/tcpsocket.h similarity index 100% rename from lib/tcpsocket.h rename to src/libcore/tcpsocket.h diff --git a/lib/thread.cpp b/src/libcore/thread.cpp similarity index 100% rename from lib/thread.cpp rename to src/libcore/thread.cpp diff --git a/lib/thread.h b/src/libcore/thread.h similarity index 100% rename from lib/thread.h rename to src/libcore/thread.h diff --git a/lib/wqueue.h b/src/libcore/wqueue.h similarity index 100% rename from lib/wqueue.h rename to src/libcore/wqueue.h diff --git a/src/libebus/Makefile.am b/src/libebus/Makefile.am new file mode 100644 index 00000000..7cf2761b --- /dev/null +++ b/src/libebus/Makefile.am @@ -0,0 +1,19 @@ +AM_CXXFLAGS = -fpic \ + -Wall \ + -Wextra + +noinst_LIBRARIES = libebus.a + +libebus_a_SOURCES = result.cpp \ + symbol.cpp \ + port.cpp \ + bus.cpp \ + command.cpp \ + commands.cpp \ + configfile.cpp \ + dump.cpp \ + decode.cpp \ + encode.cpp + +distclean-local: + -rm -f Makefile.in diff --git a/src/libebus/bus.cpp b/src/libebus/bus.cpp new file mode 100644 index 00000000..3c83ed2a --- /dev/null +++ b/src/libebus/bus.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) Roland Jax 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 "bus.h" +#include +#include + +namespace libebus +{ + + +BusCommand::BusCommand(const std::string commandStr, const bool isPoll) + : m_isPoll(isPoll), m_command(commandStr), m_resultCode(RESULT_OK) +{ + unsigned char dstAddress = m_command[1]; + + if (dstAddress == BROADCAST) + m_type = broadcast; + else if (isMaster(dstAddress) == true) + m_type = masterMaster; + else + m_type = masterSlave; + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); +} + +BusCommand::~BusCommand() +{ + pthread_mutex_destroy(&m_mutex); + pthread_cond_destroy(&m_cond); +} + +const char* BusCommand::getResultCodeCStr() +{ + return libebus::getResultCodeCStr(m_resultCode); +} + +const std::string BusCommand::getMessageStr() +{ + std::string result; + + if (m_resultCode >= 0) { + if (m_type == masterSlave) { + result = m_command.getDataStr(true); + result += "00"; + result += m_result.getDataStr(); + result += "00"; + } else { + result = "success"; + } + } + else + result = "error"; + + return result; +} + + +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_busLocked(false), m_busPriorRetry(false) +{ + m_port = new Port(deviceName, noDeviceCheck); + m_dump = new Dump(dumpFile, dumpSize); +} + +Bus::~Bus() +{ + if (isConnected() == true) + disconnect(); + + delete m_port; + delete m_dump; +} + +void Bus::printBytes() const +{ + unsigned char byte; + ssize_t bytes_read; + + bytes_read = m_port->recv(0); + + for (int i = 0; i < bytes_read; i++) { + byte = m_port->byte(); + std::cout << std::nouppercase << std::hex << std::setw(2) + << std::setfill('0') << static_cast(byte); + if (byte == SYN) + std::cout << std::endl; + } +} + +int Bus::proceed() +{ + unsigned char byte_recv; + ssize_t bytes_recv; + + // fetch new message and get bus + if (m_sendBuffer.size() != 0 && m_sstr.size() == 0) { + BusCommand* busCommand = m_sendBuffer.front(); + return getBus(busCommand->getCommand()[0]); + } + + // wait for new data + bytes_recv = m_port->recv(0); + + if (bytes_recv < 0) + return RESULT_ERR_DEVICE; + + for (int i = 0; i < bytes_recv; i++) { + + // fetch next byte + byte_recv = recvByte(); + + // store byte + return proceedCycData(byte_recv); // TODO what if more than one byte was received? + } + + return RESULT_SYN; +} + +int Bus::proceedCycData(const unsigned char byte) +{ + if (byte != SYN) { + m_sstr.push_back_unescape(byte, m_previousEscape, 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) + m_busLocked = true; + + m_cycBuffer.push(m_sstr); + m_sstr.clear(); + + if (m_busLocked == true) + return RESULT_BUS_LOCKED; + } + + return RESULT_SYN; +} + +SymbolString Bus::getCycData() +{ + SymbolString data; + + if (m_cycBuffer.empty() == false) { + data = m_cycBuffer.front(); + m_cycBuffer.pop(); + } + + return data; +} + +int Bus::getBus(const unsigned char byte_sent) +{ + unsigned char byte_recv; + ssize_t bytes_sent, bytes_recv; + + // send QQ + bytes_sent = m_port->send(&byte_sent, 1); + if (bytes_sent <= 0) + return RESULT_ERR_SEND; + + // receive 1 byte - must be QQ + bytes_recv = m_port->recv(0, 1); + + if (bytes_recv < 0) + return RESULT_ERR_DEVICE; + + // fetch next byte + byte_recv = recvByte(); + + // compare sent and received byte + if (bytes_recv == 1 && byte_sent == byte_recv) { + m_busPriorRetry = false; + return RESULT_BUS_ACQUIRED; + } + + // store byte + int ret = proceedCycData(byte_recv); + if (ret >= 0) + return ret; +// TODO this needs to be re-designed with above proceedCycData() + // compare prior nibble for retry + if (bytes_recv == 1 && (byte_sent & 0x0F) == (byte_recv & 0x0F)) { + m_busPriorRetry = true; + return RESULT_BUS_PRIOR_RETRY; + } + + m_busLocked = true; + return RESULT_ERR_BUS_LOST; +} + +BusCommand* Bus::sendCommand() +{ + unsigned char byte_recv; + ssize_t bytes_recv; + std::string result; + SymbolString slaveData; + int retval = RESULT_OK; + + BusCommand* busCommand = m_sendBuffer.front(); + m_sendBuffer.pop(); + + // send ZZ PB SB NN Dx CRC + SymbolString command = busCommand->getCommand(); + for (size_t i = 1; i < command.size(); i++) { + retval = sendByte(command[i]); + if (retval < 0) + goto on_exit; + } + + // BC -> send SYN + if (busCommand->getType() == broadcast) { + sendByte(SYN); + goto on_exit; + } + + // receive ACK + bytes_recv = m_port->recv(m_recvTimeout); + if (bytes_recv > 1) { + retval = RESULT_ERR_EXTRA_DATA; + goto on_exit; + } else if (bytes_recv < 0) { + retval = RESULT_ERR_TIMEOUT; + goto on_exit; + } + + byte_recv = recvByte(); + + // is received byte SYN? + if (byte_recv == SYN) { + retval = RESULT_ERR_SYN; + goto on_exit; + } + + // is slave ACK negative? + if (byte_recv == NAK) { + + // send QQ ZZ PB SB NN Dx CRC again + for (size_t i = 0; i < command.size(); i++) { + retval = sendByte(command[i]); + if (retval < 0) + goto on_exit; + } + + // receive ACK + bytes_recv = m_port->recv(m_recvTimeout); + if (bytes_recv > 1) { + retval = RESULT_ERR_EXTRA_DATA; + goto on_exit; + } else if (bytes_recv < 0) { + retval = RESULT_ERR_TIMEOUT; + goto on_exit; + } + + byte_recv = recvByte(); + + // is received byte SYN? + if (byte_recv == SYN) { + retval = RESULT_ERR_SYN; + goto on_exit; + } + + // is slave ACK negative? + if (byte_recv == NAK) { + retval = sendByte(SYN); + if (retval == 0) + retval = RESULT_ERR_NAK; + + goto on_exit; + } + } + + // MM -> send SYN + if (busCommand->getType() == masterMaster) { + sendByte(SYN); + goto on_exit; + } + + // receive NN, Dx, CRC + retval = recvSlaveDataAndCRC(slaveData); + + // are calculated and received CRC equal? + if (retval == RESULT_ERR_CRC) { + + // send NAK + retval = sendByte(NAK); + if (retval < 0) + goto on_exit; + + // receive NN, Dx, CRC + slaveData = SymbolString(); + retval = recvSlaveDataAndCRC(slaveData); + + // are calculated and received CRC equal? + if (retval == RESULT_ERR_CRC) { + + // send NAK + retval = sendByte(NAK); + if (retval >= 0) + retval = RESULT_ERR_CRC; + } + } + + if (retval < 0) + goto on_exit; + + // send ACK + retval = sendByte(ACK); + if (retval == -1) { + retval = RESULT_ERR_ACK; + goto on_exit; + } + + // MS -> send SYN + sendByte(SYN); + +on_exit: + + // empty receive buffer + while (m_port->size() != 0) + byte_recv = recvByte(); + + busCommand->setResult(slaveData, retval); + return busCommand; + +} + +BusCommand* Bus::delCommand() +{ + BusCommand* busCommand = m_sendBuffer.front(); + m_sendBuffer.pop(); + + busCommand->setResult(SymbolString(), RESULT_ERR_BUS_LOST); + return busCommand; +} + +int Bus::sendByte(const unsigned char byte_sent) +{ + unsigned char byte_recv; + ssize_t bytes_sent, bytes_recv; + + bytes_sent = m_port->send(&byte_sent, 1); + + // receive 1 byte - must be equal + bytes_recv = m_port->recv(RECV_TIMEOUT); + if (bytes_sent != bytes_recv) + return RESULT_ERR_EXTRA_DATA; + + byte_recv = recvByte(); + + if (byte_sent != byte_recv) + return RESULT_ERR_SEND; + + return RESULT_OK; +} + +unsigned char Bus::recvByte() +{ + unsigned char byte_recv; + + // fetch byte + byte_recv = m_port->byte(); + + if (m_dumpState == true) + m_dump->write((const char*) &byte_recv); + + return byte_recv; +} + +int Bus::recvSlaveDataAndCRC(SymbolString& result) +{ + unsigned char byte_recv; + ssize_t bytes_recv; + bool previousEscape = false; + + // 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) { + 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; + } + + 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) + 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) + return RESULT_ERR_CRC; + + return RESULT_OK; +} + + +} //namespace + diff --git a/src/libebus/bus.h b/src/libebus/bus.h new file mode 100644 index 00000000..39d8a8b6 --- /dev/null +++ b/src/libebus/bus.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_BUS_H_ +#define LIBEBUS_BUS_H_ + +#include "symbol.h" +#include "result.h" +#include "port.h" +#include "dump.h" +#include +#include +#include +#include + +namespace libebus +{ + + +// the maximum time allowed for retrieving a byte from an addressed slave +#define RECV_TIMEOUT 10000 + + +enum CommandType { invalid, broadcast, masterMaster, masterSlave }; + + +class BusCommand +{ + +public: + BusCommand(const std::string commandStr, const bool isPoll); + ~BusCommand(); + + CommandType getType() const { return m_type; } + bool isPoll() const { return m_isPoll; } + SymbolString getCommand() const { return m_command; } + bool isErrorResult() const { return m_resultCode < 0; } + const char* getResultCodeCStr(); + SymbolString getResult() const { return m_result; } + void setResult(const SymbolString result, const int resultCode) { m_result = result; m_resultCode = resultCode; } + const std::string getMessageStr(); + void waitSignal() { pthread_cond_wait(&m_cond, &m_mutex); } // TODO timeout + void sendSignal() { pthread_cond_signal(&m_cond); } + +private: + CommandType m_type; + bool m_isPoll; + SymbolString m_command; + SymbolString m_result; + int m_resultCode; + + pthread_mutex_t m_mutex; + pthread_cond_t m_cond; +}; + +class Bus +{ + +public: + Bus(const std::string deviceName, const bool noDeviceCheck, const long recvTimeout, + const std::string dumpFile, const long dumpSize, const bool dumpState); + ~Bus(); + + void connect() { m_port->open(); } + void disconnect() { if (m_port->isOpen() == true) m_port->close(); } + bool isConnected() { return m_port->isOpen(); } + + void printBytes() const; + + int proceed(); + SymbolString getCycData(); + + void addCommand(BusCommand* busCommand) { m_sendBuffer.push(busCommand); } + + int getBus(const unsigned char byte); + BusCommand* sendCommand(); + BusCommand* delCommand(); + + void setDumpState(const bool dumpState) { m_dumpState = dumpState; } + +private: + Port* m_port; + bool m_previousEscape; + SymbolString m_sstr; + std::queue m_cycBuffer; + std::queue m_sendBuffer; + + const long m_recvTimeout; + + Dump* m_dump; + bool m_dumpState; + + bool m_busLocked; + bool m_busPriorRetry; + + int proceedCycData(const unsigned char byte); + int sendByte(const unsigned char byte_sent); + unsigned char recvByte(); + int recvSlaveDataAndCRC(SymbolString& result); + +}; + + +} //namespace + +#endif // LIBEBUS_BUS_H_ diff --git a/src/libebus/command.cpp b/src/libebus/command.cpp new file mode 100644 index 00000000..786435e5 --- /dev/null +++ b/src/libebus/command.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) Roland Jax 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 "command.h" +#include "decode.h" +#include "encode.h" +#include +#include +#include +#include +#include +#include + +namespace libebus +{ + + +std::string Command::calcData() +{ + // encode - only first entry will be encoded + // ToDo: if more parts are needed, they will be implemented + encode(m_data, m_command[13], m_command[14]); + + if (m_error.length() > 0) + m_result = m_error; + + return m_result; +} + +std::string Command::calcResult(const cmd_t& cmd) +{ + int elements = strtol(m_command[9].c_str(), NULL, 10); + + if (cmd.size() > 3) { + bool found = false; + + for (size_t i = 3; i < cmd.size(); i++) { + int j; + + for (j = 0; j < elements; j++) { + if (m_command[10 + j*8] == cmd[i]) { + found = true; + break; + } + } + + if (found == true) { + found = false; + + // decode + calcSub(m_command[11 + j*8], m_command[12 + j*8], + m_command[13 + j*8], m_command[14 + j*8]); + } + + } + + } else { + for (int j = 0; j < elements; j++) { + + // decode + calcSub(m_command[11 + j*8], m_command[12 + j*8], + m_command[13 + j*8], m_command[14 + j*8]); + } + } + + if (m_error.length() > 0) + m_result = m_error; + + return m_result; +} + +void Command::calcSub(const std::string& part, const std::string& position, + const std::string& type, const std::string& factor) +{ + std::string data; + + // Master Data + if (strcasecmp(part.c_str(), "MD") == 0) { + // QQ ZZ PB SB NN + int md_pos = 10; + int md_len = strtol(m_command[7].c_str(), NULL, 10)*2; + data = m_data.substr(md_pos, md_len); + } + + // Slave Acknowledge + else if (strcasecmp(part.c_str(), "SA") == 0) { + // QQ ZZ PB SB NN + Dx + CRC + int sa_pos = 10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 2; + int sa_len = 2; + data = m_data.substr(sa_pos, sa_len); + } + + // Slave Data + else if (strcasecmp(part.c_str(), "SD") == 0) { + // QQ ZZ PB SB NN + Dx + CRC ACK NN + int sd_pos = 10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 6; + int sd_len = m_data.length() - (10 + (strtol(m_command[7].c_str(), NULL, 10)*2) + 6) - 4; + data = m_data.substr(sd_pos, sd_len); + } + + // Master Acknowledge + else if (strcasecmp(part.c_str(), "MA") == 0) { + // QQ ZZ PB SB NN + Dx + CRC ACK NN + Dx + int ma_pos = m_data.length() - 2; + int ma_len = 2; + data = m_data.substr(ma_pos, ma_len); + } + + decode(data, position, type, factor); +} + +void Command::decode(const std::string& data, const std::string& position, + const std::string& type, const std::string& factor) +{ + std::ostringstream result, value; + Decode* help = NULL; + + // prepare position + std::string token; + std::istringstream stream(position); + std::vector pos; + + while (std::getline(stream, token, ',') != 0) + pos.push_back(strtol(token.c_str(), NULL, 10)); + + if (strcasecmp(type.c_str(), "HEX") == 0) { + if (pos.size() <= 1 || pos[1] < pos[0]) + pos[1] = pos[0]; + + value << data.substr((pos[0]-1)*2, (pos[1]-pos[0]+1)*2); + help = new DecodeHEX(value.str()); + } + else if (strcasecmp(type.c_str(), "UCH") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeUCH(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "SCH") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeSCH(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "UIN") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); + help = new DecodeUIN(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "SIN") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); + help = new DecodeSIN(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "ULG") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2) << data.substr((pos[3]-1)*2, 2); + help = new DecodeULG(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "SLG") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2) << data.substr((pos[3]-1)*2, 2); + help = new DecodeSLG(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "FLT") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); + help = new DecodeFLT(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "STR") == 0) { + if (pos.size() <= 1 || pos[1] < pos[0]) + pos[1] = pos[0]; + + value << data.substr((pos[0]-1)*2, (pos[1]-pos[0]+1)*2); + help = new DecodeSTR(value.str()); + } + else if (strcasecmp(type.c_str(), "BCD") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeBCD(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "D1B") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeD1B(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "D1C") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeD1C(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "D2B") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); + help = new DecodeD2B(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "D2C") == 0) { + value << data.substr((pos[0]-1)*2, 2) << data.substr((pos[1]-1)*2, 2); + help = new DecodeD2C(value.str(), factor); + } + else if (strcasecmp(type.c_str(), "BDA") == 0) { + value << data.substr((pos[0]-1)*2, 2) + << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2); + help = new DecodeBDA(value.str()); + } + else if (strcasecmp(type.c_str(), "HDA") == 0) { + value << data.substr((pos[0]-1)*2, 2) + << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2); + help = new DecodeHDA(value.str()); + } + else if (strcasecmp(type.c_str(), "BTI") == 0) { + value << data.substr((pos[0]-1)*2, 2) + << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2); + help = new DecodeBTI(value.str()); + } + else if (strcasecmp(type.c_str(), "HTI") == 0) { + value << data.substr((pos[0]-1)*2, 2) + << data.substr((pos[1]-1)*2, 2) + << data.substr((pos[2]-1)*2, 2); + help = new DecodeHTI(value.str()); + } + else if (strcasecmp(type.c_str(), "BDY") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeBDY(value.str()); + } + else if (strcasecmp(type.c_str(), "HDY") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeHDY(value.str()); + } + else if (strcasecmp(type.c_str(), "TTM") == 0) { + value << data.substr((pos[0]-1)*2, 2); + help = new DecodeTTM(value.str()); + } + + if (help == NULL) { + result << "type '" << type.c_str() << "' not implemented!"; + m_error = result.str(); + } else { + result << help->decode(); + + if (m_result.length() > 0) + m_result += " "; + + m_result += result.str(); + } + + delete help; +} + +void Command::encode(const std::string& data, const std::string& type, + const std::string& factor) +{ + std::ostringstream result; + Encode* help = NULL; + + if (strcasecmp(type.c_str(), "HEX") == 0) { + help = new EncodeHEX(data); + } + else if (strcasecmp(type.c_str(), "UCH") == 0) { + help = new EncodeUCH(data, factor); + } + else if (strcasecmp(type.c_str(), "SCH") == 0) { + help = new EncodeSCH(data, factor); + } + else if (strcasecmp(type.c_str(), "UIN") == 0) { + help = new EncodeUIN(data, factor); + } + else if (strcasecmp(type.c_str(), "SIN") == 0) { + help = new EncodeSIN(data, factor); + } + else if (strcasecmp(type.c_str(), "ULG") == 0) { + help = new EncodeULG(data, factor); + } + else if (strcasecmp(type.c_str(), "SLG") == 0) { + help = new EncodeSLG(data, factor); + } + else if (strcasecmp(type.c_str(), "FLT") == 0) { + help = new EncodeSLG(data, factor); + } + else if (strcasecmp(type.c_str(), "STR") == 0) { + help = new EncodeSTR(data); + } + else if (strcasecmp(type.c_str(), "BCD") == 0) { + help = new EncodeBCD(data, factor); + } + else if (strcasecmp(type.c_str(), "D1B") == 0) { + help = new EncodeD1B(data, factor); + } + else if (strcasecmp(type.c_str(), "D1C") == 0) { + help = new EncodeD1C(data, factor); + } + else if (strcasecmp(type.c_str(), "D2B") == 0) { + help = new EncodeD2B(data, factor); + } + else if (strcasecmp(type.c_str(), "D2C") == 0) { + help = new EncodeD2C(data, factor); + } + else if (strcasecmp(type.c_str(), "BDA") == 0) { + help = new EncodeBDA(data); + } + else if (strcasecmp(type.c_str(), "HDA") == 0) { + help = new EncodeHDA(data); + } + else if (strcasecmp(type.c_str(), "BTI") == 0) { + help = new EncodeBTI(data); + } + else if (strcasecmp(type.c_str(), "HTI") == 0) { + help = new EncodeHTI(data); + } + else if (strcasecmp(type.c_str(), "BDY") == 0) { + help = new EncodeBDY(data); + } + else if (strcasecmp(type.c_str(), "HDY") == 0) { + help = new EncodeHDY(data); + } + else if (strcasecmp(type.c_str(), "TTM") == 0) { + help = new EncodeTTM(data); + } + + if (help == NULL) { + result << "type '" << type.c_str() << "' not implemented!"; + m_error = result.str(); + } else { + result << help->encode(); + + if (m_result.length() > 0) + m_result += " "; + + m_result += result.str(); + } + + delete help; +} + + +} //namespace + diff --git a/src/libebus/command.h b/src/libebus/command.h new file mode 100644 index 00000000..a948c1af --- /dev/null +++ b/src/libebus/command.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_COMMAND_H_ +#define LIBEBUS_COMMAND_H_ + +#include +#include + +namespace libebus +{ + + +typedef std::vector cmd_t; +typedef cmd_t::const_iterator cmdCI_t; + +class Command +{ + +public: + Command(int index, cmd_t command) : m_index(index), m_command(command) {} + Command(int index, cmd_t command, std::string data) + : m_index(index), m_command(command), m_data(data) {} + + cmd_t getCommand() const { return m_command; } + void setData(const std::string& data) { m_data = data; } + std::string getData() const { return m_data; } + std::string calcData(); + + std::string calcResult(const cmd_t& cmd); + +private: + int m_index; + cmd_t m_command; + std::string m_data; + std::string m_result; + std::string m_error; + + void calcSub(const std::string& part, const std::string& position, + const std::string& type, const std::string& factor); + + void decode(const std::string& data, const std::string& position, + const std::string& type, const std::string& factor); + + void encode(const std::string& data, const std::string& type, + const std::string& factor); + +}; + + +} //namespace + +#endif // LIBEBUS_COMMAND_H_ diff --git a/src/libebus/commands.cpp b/src/libebus/commands.cpp new file mode 100644 index 00000000..2e6ab330 --- /dev/null +++ b/src/libebus/commands.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) Roland Jax 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 "commands.h" +#include +#include +#include +#include +#include +#include + +namespace libebus +{ + + +Commands::~Commands() +{ + for (mapCI_t iter = m_polDB.begin(); iter != m_polDB.end(); ++iter) + delete iter->second; + + m_polDB.clear(); + + for (mapCI_t iter = m_cycDB.begin(); iter != m_cycDB.end(); ++iter) + delete iter->second; + + m_cycDB.clear(); + + m_cmdDB.clear(); +} + +void Commands::addCommand(const cmd_t& command) +{ + m_cmdDB.push_back(command); + + if (strcasecmp(command[0].c_str(),"C") == 0) { + Command* cmd = new Command(m_cmdDB.size()-1, command); + m_cycDB.insert(pair_t(m_cmdDB.size()-1, cmd)); + } + + if (strcasecmp(command[0].c_str(),"P") == 0) { + Command* cmd = new Command(m_cmdDB.size()-1, command); + m_polDB.insert(pair_t(m_cmdDB.size()-1, cmd)); + } +} + +void Commands::printCommands() const +{ + if (m_cmdDB.size() == 0) + return; + + for (cmdDBCI_t i = m_cmdDB.begin(); i != m_cmdDB.end(); i++) { + printCommand(*i); + std::cout << std::endl; + } +} + +int Commands::findCommand(const std::string& data) const +{ + // no commands definend + if (m_cmdDB.size() == 0) + return -2; + + // preapre string for searching command + std::string token; + std::istringstream isstr(data); + std::vector cmd; + + // split stream + while (std::getline(isstr, token, ' ') != 0) + cmd.push_back(token); + + std::size_t index; + cmdDBCI_t i = m_cmdDB.begin(); + + // walk through commands - GET + if (strcasecmp(cmd[0].c_str(), "GET") == 0) { + for (index = 0; i != m_cmdDB.end(); i++, index++) { + + // empty line + if ((*i).size() == 0) + continue; + + if (((strcasecmp((*i)[0].c_str(), "R") == 0) + || (strcasecmp((*i)[0].c_str(), "P") == 0)) + && (strcasecmp((*i)[1].c_str(), cmd[1].c_str()) == 0) + && (strcasecmp((*i)[2].c_str(), cmd[2].c_str()) == 0)) + return index; + } + // walk through commands - SET, CYC + } else { + // correct type + if (strcasecmp(cmd[0].c_str(), "SET") == 0) + cmd[0] = "W"; + else if (strcasecmp(cmd[0].c_str(), "CYC") == 0) + cmd[0] = "C"; + + for (index = 0; i != m_cmdDB.end(); i++, index++) { + + // empty line + if ((*i).size() == 0) + continue; + + if (strcasecmp((*i)[0].c_str(), cmd[0].c_str()) == 0 && + strcasecmp((*i)[1].c_str(), cmd[1].c_str()) == 0 && + strcasecmp((*i)[2].c_str(), cmd[2].c_str()) == 0) + return index; + } + } + + // command not found + return -1; +} + +std::string Commands::getEbusCommand(const int index) const +{ + cmd_t command = m_cmdDB.at(index); + std::string cmd(command[5]); + cmd += command[6]; + std::stringstream sstr; + sstr << std::setw(2) << std::hex << std::setfill('0') << command[7]; + cmd += sstr.str(); + cmd += command[8]; + + return cmd; +} + +int Commands::storeCycData(const std::string& data) const +{ + // no commands defined + if (m_cycDB.size() == 0) + return -2; + + // search skipped - string too short + if (data.length() < 10) + return -3; + + // prepare string for searching command + std::string search(data.substr(2, 8 + strtol(data.substr(8,2).c_str(), NULL, 16) * 2)); + + mapCI_t iter = m_cycDB.begin(); + + // walk through commands + for (; iter != m_cycDB.end(); iter++) { + + std::string command = getEbusCommand(iter->first); + + // skip wrong search string length + if (command.length() > search.length()) + continue; + + if (strcasecmp(command.c_str(), search.substr(0,command.length()).c_str()) == 0) { + iter->second->setData(data); + return iter->first; + } + } + + // command not found + return -1; +} + +std::string Commands::getCycData(int index) const +{ + mapCI_t iter = m_cycDB.find(index); + if (iter != m_cycDB.end()) + return iter->second->getData(); + else + return ""; +} + +int Commands::nextPolCommand() +{ + size_t index = 0; + + m_polIndex++; + + if (m_polIndex == m_polDB.size()) + m_polIndex = 0; + + mapCI_t iter = m_polDB.begin(); + + for (; iter != m_polDB.end(); iter++, index++) + if (index == m_polIndex) + return iter->first; + + return -1; +} + +void Commands::storePolData(const std::string& data) const +{ + // prepare string for searching command + std::string search(data.substr(2, 8 + strtol(data.substr(8,2).c_str(), NULL, 16) * 2)); + + mapCI_t iter = m_polDB.begin(); + + // walk through commands + for (; iter != m_polDB.end(); iter++) { + + std::string command = getEbusCommand(iter->first); + + // skip wrong search string length + if (command.length() > search.length()) + continue; + + if (strcasecmp(command.c_str(), search.substr(0,command.length()).c_str()) == 0) + iter->second->setData(data); + + } +} + +std::string Commands::getPolData(int index) const +{ + mapCI_t iter = m_polDB.find(index); + if (iter != m_polDB.end()) + return iter->second->getData(); + else + return ""; +} + +void Commands::printCommand(const cmd_t& command) const +{ + if (command.size() == 0) + return; + + for (cmdCI_t i = command.begin(); i != command.end(); i++) + std::cout << *i << ';'; +} + + +} //namespace + diff --git a/src/libebus/commands.h b/src/libebus/commands.h new file mode 100644 index 00000000..8f7793a1 --- /dev/null +++ b/src/libebus/commands.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_COMMANDS_H_ +#define LIBEBUS_COMMANDS_H_ + +#include "command.h" +#include +#include +#include + +namespace libebus +{ + + +typedef std::vector cmdDB_t; +typedef cmdDB_t::const_iterator cmdDBCI_t; + +typedef std::map map_t; +typedef map_t::const_iterator mapCI_t; +typedef std::pair pair_t; + +class Commands +{ + +public: + Commands() : m_polIndex(-1) {} + ~Commands(); + + void addCommand(const cmd_t& command); + void printCommands() const; + + std::size_t sizeCmdDB() const { return m_cmdDB.size(); } + std::size_t sizeCycDB() const { return m_cycDB.size(); } + std::size_t sizePolDB() const { return m_polDB.size(); } + + cmd_t const& operator[](const std::size_t& index) const { return m_cmdDB[index]; } + + int findCommand(const std::string& data) const; + + std::string getCmdType(const int index) const { return std::string(m_cmdDB.at(index)[0]); } + std::string getEbusType(const int index) const { return std::string(m_cmdDB.at(index)[4]); } + std::string getEbusCommand(const int index) const; + + int storeCycData(const std::string& data) const; + std::string getCycData(int index) const; + + int nextPolCommand(); + void storePolData(const std::string& data) const; + std::string getPolData(int index) const; + +private: + cmdDB_t m_cmdDB; + map_t m_cycDB; + map_t m_polDB; + size_t m_polIndex; + + void printCommand(const cmd_t& command) const; + +}; + + +} //namespace + +#endif // LIBEBUS_COMMANDS_H_ diff --git a/src/libebus/configfile.cpp b/src/libebus/configfile.cpp new file mode 100644 index 00000000..61652d73 --- /dev/null +++ b/src/libebus/configfile.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) Roland Jax 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 "configfile.h" +#include +#include +#include + +namespace libebus +{ + + +void ConfigFileCSV::readFile(std::istream& is, Commands& commands) +{ + std::string line; + + // read lines + while (std::getline(is, line) != 0) { + cmd_t row; + std::string column; + + std::istringstream isstr(line); + + // walk through columns + while (std::getline(isstr, column, ';') != 0) + row.push_back(column); + + // skip empty and commented rows + if (row.empty() == true || row[0][0] == '#') + continue; + + commands.addCommand(row); + } +}; + + +void ConfigFileXML::readFile(std::istream& is, Commands& commands) +{ + ; // ToDo: Implamantion for xml files +} + + + +ConfigCommands::ConfigCommands(const std::string path, const FileType type) +{ + m_path = path; + m_configfile = NULL; + setType(type); + addFiles(m_path, m_extension); +} + +void ConfigCommands::setType(const FileType type) +{ + if (m_configfile != NULL) + delete m_configfile; + + switch (type) { + case CSV: + m_configfile = new ConfigFileCSV(); + m_extension = "csv"; + break; + case XML: + m_configfile = new ConfigFileXML(); + m_extension = "xml"; + break; + }; +}; + +Commands* ConfigCommands::getCommands() +{ + Commands* commands = new Commands(); + std::vector::const_iterator i = m_files.begin(); + + for(; i != m_files.end(); i++) { + std::fstream file((*i).c_str(), std::ios::in); + + if(file.is_open() == true) { + m_configfile->readFile(file, *commands); + file.close(); + } + } + return commands; +}; + +void ConfigCommands::addFiles(const std::string path, const std::string extension) +{ + DIR* dir = opendir(path.c_str()); + + if (dir == NULL) + return; + + dirent* d = readdir(dir); + + while (d != NULL) { + + if (d->d_type == DT_DIR) { + std::string fn = d->d_name; + + if (fn != "." && fn != "..") { + const std::string p = path + "/" + d->d_name; + addFiles(p, extension); + } + + } else if (d->d_type == DT_REG) { + std::string fn = d->d_name; + + if (fn.find(extension, (fn.length() - extension.length())) != std::string::npos) { + const std::string p = path + "/" + d->d_name; + m_files.push_back(p); + } + } + + d = readdir(dir); + } + + closedir(dir); +}; + + +} //namespace + diff --git a/src/libebus/configfile.h b/src/libebus/configfile.h new file mode 100644 index 00000000..7e6c58b8 --- /dev/null +++ b/src/libebus/configfile.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_CONFIGFILE_H_ +#define LIBEBUS_CONFIGFILE_H_ + +#include "commands.h" +#include +#include + +namespace libebus +{ + +enum FileType { CSV, XML }; + +class ConfigFile +{ + +public: + virtual ~ConfigFile() {} + + virtual void readFile(std::istream& is, Commands& commands) = 0; + +}; + +class ConfigFileCSV : public ConfigFile +{ + +public: + ~ConfigFileCSV() {} + + void readFile(std::istream& is, Commands& commands); + +}; + +class ConfigFileXML : public ConfigFile +{ + +public: + ~ConfigFileXML() {} + + void readFile(std::istream& is, Commands& commands); + +}; + + +class ConfigCommands +{ + +public: + ConfigCommands(const std::string path, const FileType type); + ~ConfigCommands() { delete m_configfile; } + + void setType(const FileType type); + Commands* getCommands(); + +private: + ConfigFile* m_configfile; + std::string m_path; + std::string m_extension; + std::vector m_files; + + void addFiles(const std::string path, const std::string extension); + +}; + + +} //namespace + +#endif // LIBEBUS_CONFIGFILE_H_ diff --git a/src/libebus/decode.cpp b/src/libebus/decode.cpp new file mode 100644 index 00000000..920ab1d1 --- /dev/null +++ b/src/libebus/decode.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (C) Roland Jax 2014 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * 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 "decode.h" +#include +#include +#include +#include +#include +#include +#include + +namespace libebus +{ + + +Decode::Decode(const std::string& data, const std::string& factor) + : m_data(data) +{ + if ((factor.find_first_not_of("0123456789.") == std::string::npos) == true) + m_factor = static_cast(strtod(factor.c_str(), NULL)); + else + m_factor = 1.0; +} + + +std::string DecodeHEX::decode() +{ + std::ostringstream result; + + for (size_t i = 0; i < m_data.length()/2; i++) + result << m_data.substr(i*2, 2) << " "; + + return result.str().substr(0, result.str().length()-1); +} + +std::string DecodeUCH::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned short x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed << static_cast(x * m_factor); + + return result.str(); +} + +std::string DecodeSCH::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned short x; + ss >> x; + + std::ostringstream result; + if ((x & 0x80) == 0x80) + result << std::setprecision(3) << std::fixed + << static_cast(static_cast(- ( ((unsigned char) (~ x)) + 1) ) * m_factor); + else + result << std::setprecision(3) << std::fixed + << static_cast(static_cast(x) * m_factor); + + return result.str(); +} + +std::string DecodeUIN::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned short x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed << static_cast(x * m_factor); + + return result.str(); +} + +std::string DecodeSIN::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned short x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed << static_cast(static_cast(x) * m_factor); + + return result.str(); +} + +std::string DecodeULG::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned int x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed << static_cast(x * m_factor); + + return result.str(); +} + +std::string DecodeSLG::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + unsigned int x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed <(static_cast(x) * m_factor); + + return result.str(); +} + +std::string DecodeFLT::decode() +{ + std::stringstream ss; + ss << std::hex << m_data; + + short x; + ss >> x; + + std::ostringstream result; + result << std::setprecision(3) << std::fixed << static_cast(x / 1000.0 * m_factor); + + return result.str(); +} + +std::string DecodeSTR::decode() +{ + std::ostringstream result; + + for (size_t i = 0; i <= m_data.length()/2; i++) { + char tmp = static_cast(strtol(m_data.substr(i*2, 2).c_str(), NULL, 16)); + if (tmp == 0x00) tmp = 0x20; + result << tmp; + } + + return result.str().substr(0, result.str().length()-1); +} + +std::string DecodeBCD::decode() +{ + std::ostringstream result; + unsigned char src = strtol(m_data.c_str(), NULL, 16); + + if ((src & 0x0F) > 0x09 || ((src >> 4) & 0x0F) > 0x09) + result << static_cast(0xFF); + else + result << static_cast(( ( ((src & 0xF0) >> 4) * 10) + (src & 0x0F) ) * m_factor); + + return result.str(); +} + +std::string DecodeD1B::decode() +{ + std::ostringstream result; + unsigned char src = strtol(m_data.c_str(), NULL, 16); + + if ((src & 0x80) == 0x80) + result << static_cast((- ( ((unsigned char) (~ src)) + 1) ) * m_factor); + else + result << static_cast(src * m_factor); + + return result.str(); +} + +std::string DecodeD1C::decode() +{ + std::ostringstream result; + unsigned char src = strtol(m_data.c_str(), NULL, 16); + + if (src > 0xC8) + result << static_cast(0xFF); + else + result << static_cast((src / 2.0) * m_factor); + + return result.str(); +} + +std::string DecodeD2B::decode() +{ + std::ostringstream result; + unsigned char src_lsb = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); + unsigned char src_msb = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); + + if ((src_msb & 0x80) == 0x80) + result << static_cast + ((- ( ((unsigned char) (~ src_msb)) + + ( ( ((unsigned char) (~ src_lsb)) + 1) / 256.0) ) ) * m_factor); + + else + result << static_cast((src_msb + (src_lsb / 256.0)) * m_factor); + + return result.str(); +} + +std::string DecodeD2C::decode() +{ + std::ostringstream result; + unsigned char src_lsb = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); + unsigned char src_msb = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); + + if ((src_msb & 0x80) == 0x80) + result << static_cast + ((- ( ( ( ((unsigned char) (~ src_msb)) * 16.0) ) + + ( ( ((unsigned char) (~ src_lsb)) & 0xF0) >> 4) + + ( ( ( ((unsigned char) (~ src_lsb)) & 0x0F) +1 ) / 16.0) ) ) * m_factor); + + else + result << static_cast(( (src_msb * 16.0) + ((src_lsb & 0xF0) >> 4) + + ((src_lsb & 0x0F) / 16.0) ) * m_factor); + + return result.str(); +} + +std::string DecodeBDA::decode() +{ + std::ostringstream result; + Decode* decode; + short array[3]; + for (int i = 0; i < 3; i++) { + decode = new DecodeBCD(m_data.substr(i*2, 2), "1.0"); + array[i] = static_cast(strtol(decode->decode().c_str(), NULL, 10)); + delete decode; + } + + result << std::setw(2) << std::setfill('0') << array[0] << "." + << std::setw(2) << std::setfill('0') << array[1] << "." + << array[2] + 2000; + + return result.str(); +} + +std::string DecodeHDA::decode() +{ + std::ostringstream result; + short dd = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); + short mm = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); + short yy = static_cast(strtol(m_data.substr(4, 2).c_str(), NULL, 16)); + + result << std::setw(2) << std::setfill('0') << dd << "." + << std::setw(2) << std::setfill('0') << mm << "." + << yy + 2000; + + return result.str(); +} + +std::string DecodeBTI::decode() +{ + std::ostringstream result; + Decode* decode; + short array[3]; + for (int i = 0; i < 3; i++) { + decode = new DecodeBCD(m_data.substr(i*2, 2), "1.0"); + array[i] = static_cast(strtol(decode->decode().c_str(), NULL, 10)); + delete decode; + } + + result << std::setw(2) << std::setfill('0') << array[0] << ":" + << std::setw(2) << std::setfill('0') << array[1] << ":" + << std::setw(2) << std::setfill('0') << array[2]; + + return result.str(); +} + +std::string DecodeHTI::decode() +{ + std::ostringstream result; + short hh = static_cast(strtol(m_data.substr(0, 2).c_str(), NULL, 16)); + short mm = static_cast(strtol(m_data.substr(2, 2).c_str(), NULL, 16)); + short ss = static_cast(strtol(m_data.substr(4, 2).c_str(), NULL, 16)); + + result << std::setw(2) << std::setfill('0') << hh << ":" + << std::setw(2) << std::setfill('0') << mm << ":" + << std::setw(2) << std::setfill('0') << ss; + + return result.str(); +} + +std::string DecodeBDY::decode() +{ + const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; + + std::ostringstream result; + short day = static_cast(strtol(m_data.c_str(), NULL, 16)); + + if (day < 0 || day > 6) + day = 7; + + result << days[day]; + + return result.str(); +} + +std::string DecodeHDY::decode() +{ + const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; + + std::ostringstream result; + short day = static_cast(strtol(m_data.c_str(), NULL, 16)) - 1; + + if (day < 0 || day > 6) + day = 7; + + result << days[day]; + + return result.str(); +} + +std::string DecodeTTM::decode() +{ + std::ostringstream result; + short hh = static_cast(strtol(m_data.c_str(), NULL, 16)) / 6; + short mm = static_cast(strtol(m_data.c_str(), NULL, 16)) % 6 * 10; + + result << std::setw(2) << std::setfill('0') << hh << ":" + << std::setw(2) << std::setfill('0') << mm; + + return result.str(); +} + + +} //namespace + diff --git a/src/libebus/decode.h b/src/libebus/decode.h new file mode 100644 index 00000000..896702fc --- /dev/null +++ b/src/libebus/decode.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) Roland Jax 2014 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * 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/. + */ + +#ifndef LIBEBUS_DECODE_H_ +#define LIBEBUS_DECODE_H_ + +#include + +namespace libebus +{ + + +class Decode +{ + +public: + Decode(const std::string& data, const std::string& factor = ""); + virtual ~Decode() {} + + virtual std::string decode() = 0; + +protected: + std::string m_data; + float m_factor; + +}; + + +class DecodeHEX : public Decode +{ + +public: + DecodeHEX(const std::string& data) : Decode(data) {} + ~DecodeHEX() {} + + std::string decode(); + +}; + +class DecodeUCH : public Decode +{ + +public: + DecodeUCH(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeUCH() {} + + std::string decode(); + +}; + +class DecodeSCH : public Decode +{ + +public: + DecodeSCH(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeSCH() {} + + std::string decode(); + +}; + +class DecodeUIN : public Decode +{ + +public: + DecodeUIN(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeUIN() {} + + std::string decode(); + +}; + +class DecodeSIN : public Decode +{ + +public: + DecodeSIN(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeSIN() {} + + std::string decode(); + +}; + +class DecodeULG : public Decode +{ + +public: + DecodeULG(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeULG() {} + + std::string decode(); + +}; + +class DecodeSLG : public Decode +{ + +public: + DecodeSLG(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeSLG() {} + + std::string decode(); + +}; + +class DecodeFLT : public Decode +{ + +public: + DecodeFLT(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeFLT() {} + + std::string decode(); + +}; + +class DecodeSTR : public Decode +{ + +public: + DecodeSTR(std::string data) : Decode(data) {} + ~DecodeSTR() {} + + std::string decode(); + +}; + +class DecodeBCD : public Decode +{ + +public: + DecodeBCD(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeBCD() {} + + std::string decode(); + +}; + +class DecodeD1B : public Decode +{ + +public: + DecodeD1B(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeD1B() {} + + std::string decode(); + +}; + +class DecodeD1C : public Decode +{ + +public: + DecodeD1C(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeD1C() {} + + std::string decode(); + +}; + +class DecodeD2B : public Decode +{ + +public: + DecodeD2B(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeD2B() {} + + std::string decode(); + +}; + +class DecodeD2C : public Decode +{ + +public: + DecodeD2C(const std::string& data, const std::string& factor) + : Decode(data, factor) {} + ~DecodeD2C() {} + + std::string decode(); + +}; + +class DecodeBDA : public Decode +{ + +public: + DecodeBDA(const std::string& data) : Decode(data) {} + ~DecodeBDA() {} + + std::string decode(); + +}; + +class DecodeHDA : public Decode +{ + +public: + DecodeHDA(const std::string& data) : Decode(data) {} + ~DecodeHDA() {} + + std::string decode(); + +}; + +class DecodeBTI : public Decode +{ + +public: + DecodeBTI(const std::string& data) : Decode(data) {} + ~DecodeBTI() {} + + std::string decode(); + +}; + +class DecodeHTI : public Decode +{ + +public: + DecodeHTI(const std::string& data) : Decode(data) {} + ~DecodeHTI() {} + + std::string decode(); + +}; + +class DecodeBDY : public Decode +{ + +public: + DecodeBDY(const std::string& data) : Decode(data) {} + ~DecodeBDY() {} + + std::string decode(); + +}; + +class DecodeHDY : public Decode +{ + +public: + DecodeHDY(const std::string& data) : Decode(data) {} + ~DecodeHDY() {} + + std::string decode(); + +}; + +class DecodeTTM : public Decode +{ + +public: + DecodeTTM(const std::string& data) : Decode(data) {} + ~DecodeTTM() {} + + std::string decode(); + +}; + + +} //namespace + +#endif // LIBEBUS_DECODE_H_ diff --git a/src/libebus/dump.cpp b/src/libebus/dump.cpp new file mode 100644 index 00000000..2fa3595f --- /dev/null +++ b/src/libebus/dump.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) Roland Jax 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 "dump.h" +#include +#include + +namespace libebus +{ + +int Dump::write(const char* byte) +{ + int ret = 0; + + std::ofstream fs(m_filename.c_str(), std::ios::out | std::ios::binary | std::ios::app); + + if (fs == 0) + return -1; + + fs.write(byte, 1); + + if (fs.tellp() >= m_filesize * 1024) { + std::string oldfile; + oldfile += m_filename; + oldfile += ".old"; + ret = rename(m_filename.c_str(), oldfile.c_str()); + } + + fs.close(); + + return ret; +} + + + +} //namespace + diff --git a/src/libebus/dump.h b/src/libebus/dump.h new file mode 100644 index 00000000..86940996 --- /dev/null +++ b/src/libebus/dump.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_DUMP_H_ +#define LIBEBUS_DUMP_H_ + +#include + +namespace libebus +{ + + +class Dump +{ + +public: + Dump(std::string filename, long filesize) + : m_filename(filename), m_filesize(filesize) {} + + int write(const char* byte); + + void setFilename(const std::string& filename) { m_filename = filename; } + void setFilesize(const long filesize) { m_filesize = filesize; } + +private: + std::string m_filename; + long m_filesize; + +}; + + +} //namespace + +#endif // LIBEBUS_DUMP_H_ diff --git a/src/libebus/encode.cpp b/src/libebus/encode.cpp new file mode 100644 index 00000000..84a7d56a --- /dev/null +++ b/src/libebus/encode.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (C) Roland Jax 2014 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * 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 "encode.h" +#include +#include +#include +#include +#include +#include +#include + +namespace libebus +{ + + +Encode::Encode(const std::string& data, const std::string& factor) + : m_data(data) +{ + if ((factor.find_first_not_of("0123456789.") == std::string::npos) == true) + m_factor = static_cast(strtod(factor.c_str(), NULL)); + else + m_factor = 1.0; +} + + +std::string EncodeHEX::encode() +{ + m_data.erase(std::remove_if(m_data.begin(), m_data.end(), isspace), m_data.end()); + + return m_data; +} + +std::string EncodeUCH::encode() +{ + std::ostringstream result; + unsigned short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + result << std::setw(2) << std::hex << std::setfill('0') << src; + + return result.str().substr(result.str().length()-2,2); +} + +std::string EncodeSCH::encode() +{ + std::ostringstream result; + short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src < -127 || src > 127) + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x80); + else + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(src); + + return result.str().substr(result.str().length()-2,2); +} + +std::string EncodeUIN::encode() +{ + std::ostringstream result; + unsigned short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + result << std::setw(4) << std::hex << std::setfill('0') << src; + + return result.str().substr(2,2) + result.str().substr(0,2); +} + +std::string EncodeSIN::encode() +{ + std::ostringstream result; + short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + result << std::setw(4) << std::hex << std::setfill('0') << src; + + return result.str().substr(2,2) + result.str().substr(0,2); +} + +std::string EncodeULG::encode() +{ + std::ostringstream result; + unsigned long src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + result << std::setw(8) << std::hex << std::setfill('0') << src; + + return result.str().substr(6,2) + result.str().substr(4,2) + + result.str().substr(2,2) + result.str().substr(0,2); +} + +std::string EncodeSLG::encode() +{ + std::ostringstream result; + int src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + result << std::setw(8) << std::hex << std::setfill('0') << src; + + return result.str().substr(6,2) + result.str().substr(4,2) + + result.str().substr(2,2) + result.str().substr(0,2); +} + +std::string EncodeFLT::encode() +{ + std::ostringstream result; + short src = static_cast(strtod(m_data.c_str(), NULL) * 1000.0 / m_factor); + result << std::setw(4) << std::hex << std::setfill('0') << src; + + return result.str().substr(2,2) + result.str().substr(0,2); +} + +std::string EncodeSTR::encode() +{ + std::ostringstream result; + + for (size_t i = 0; i < m_data.length(); i++) + result << std::setw(2) << std::hex << std::setfill('0') << static_cast(m_data[i]); + + return result.str(); +} + +std::string EncodeBCD::encode() +{ + std::ostringstream result; + short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src > 99) + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0xFF); + else + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast( ((src / 10) << 4) | (src % 10) ); + + return result.str().substr(result.str().length()-2,2); +} + +std::string EncodeD1B::encode() +{ + std::ostringstream result; + short src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src < -127 || src > 127) + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x80); + else + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(src); + + return result.str().substr(result.str().length()-2,2); +} + +std::string EncodeD1C::encode() +{ + std::ostringstream result; + float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src < 0.0 || src > 100.0) + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0xFF); + else + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(src * 2.0); + + return result.str(); +} + +std::string EncodeD2B::encode() +{ + std::ostringstream result; + float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src < -127.999 || src > 127.999) { + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x80) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x00); + } else { + unsigned char tgt_lsb = static_cast((src - ((short) src)) * 256.0); + unsigned char tgt_msb; + + if (src < 0.0 && tgt_lsb != 0x00) + tgt_msb = static_cast((short) src - 1); + else + tgt_msb = static_cast((short) src); + + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(tgt_msb) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(tgt_lsb); + } + + return result.str(); +} + +std::string EncodeD2C::encode() +{ + std::ostringstream result; + float src = static_cast(strtod(m_data.c_str(), NULL) / m_factor); + + if (src < -2047.999 || src > 2047.999) { + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x80) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(0x00); + } else { + unsigned char tgt_lsb = static_cast( + ((unsigned char) ( ((short) src) % 16) << 4) + + ((unsigned char) ( (src - ((short) src)) * 16.0)) ); + + unsigned char tgt_msb; + + if (src < 0.0 && tgt_lsb != 0x00) + tgt_msb = static_cast((short) (src / 16.0) - 1); + else + tgt_msb = static_cast((short) src / 16.0); + + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(tgt_msb) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(tgt_lsb); + } + + return result.str(); +} + +std::string EncodeBDA::encode() +{ + // prepare data + std::string token; + std::istringstream stream(m_data); + std::vector data; + + while (std::getline(stream, token, '.') != 0) + data.push_back(token); + + std::ostringstream result; + result << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[0].c_str(), NULL)) + << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[1].c_str(), NULL)) + << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[2].c_str(), NULL) - 2000); + + return result.str(); +} + +std::string EncodeHDA::encode() +{ + // prepare data + std::string token; + std::istringstream stream(m_data); + std::vector data; + + while (std::getline(stream, token, '.') != 0) + data.push_back(token); + + std::ostringstream result; + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[0].c_str(), NULL)) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[1].c_str(), NULL)) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[2].c_str(), NULL) - 2000); + + return result.str(); +} + +std::string EncodeBTI::encode() +{ + // prepare data + std::string token; + std::istringstream stream(m_data); + std::vector data; + + while (std::getline(stream, token, ':') != 0) + data.push_back(token); + + std::ostringstream result; + result << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[0].c_str(), NULL)) + << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[1].c_str(), NULL)) + << std::setw(2) << std::dec << std::setfill('0') + << static_cast(strtod(data[2].c_str(), NULL)); + + return result.str(); +} + +std::string EncodeHTI::encode() +{ + // prepare data + std::string token; + std::istringstream stream(m_data); + std::vector data; + + while (std::getline(stream, token, ':') != 0) + data.push_back(token); + + std::ostringstream result; + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[0].c_str(), NULL)) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[1].c_str(), NULL)) + << std::setw(2) << std::hex << std::setfill('0') + << static_cast(strtod(data[2].c_str(), NULL)); + + return result.str(); +} + +std::string EncodeBDY::encode() +{ + const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; + short day = 7; + + for (short i = 0; i < 7; i++) + if (strcasecmp(days[i], m_data.c_str()) == 0) + day = i; + + std::ostringstream result; + result << std::setw(2) << std::hex << std::setfill('0') << day; + + return result.str(); +} + +std::string EncodeHDY::encode() +{ + const char *days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Err"}; + short day = 8; + + for (short i = 0; i < 7; i++) + if (strcasecmp(days[i], m_data.c_str()) == 0) + day = i + 1; + + std::ostringstream result; + result << std::setw(2) << std::hex << std::setfill('0') << day; + + return result.str(); +} + +std::string EncodeTTM::encode() +{ + // prepare data + std::string token; + std::istringstream stream(m_data); + std::vector data; + + while (std::getline(stream, token, ':') != 0) + data.push_back(token); + + std::ostringstream result; + result << std::setw(2) << std::hex << std::setfill('0') + << static_cast( (strtod(data[0].c_str(), NULL) * 6) + + (strtod(data[1].c_str(), NULL) / 10) ); + + return result.str(); +} + + +} //namespace + diff --git a/src/libebus/encode.h b/src/libebus/encode.h new file mode 100644 index 00000000..c21e2efe --- /dev/null +++ b/src/libebus/encode.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) Roland Jax 2014 + * crc calculations from http://www.mikrocontroller.net/topic/75698 + * + * 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/. + */ + +#ifndef LIBEBUS_ENCODE_H_ +#define LIBEBUS_ENCODE_H_ + +#include + +namespace libebus +{ + + +class Encode +{ + +public: + Encode(const std::string& data, const std::string& factor = ""); + virtual ~Encode() {} + + virtual std::string encode() = 0; + +protected: + std::string m_data; + float m_factor; + +}; + + +class EncodeHEX : public Encode +{ + +public: + EncodeHEX(const std::string& data) : Encode(data) {} + ~EncodeHEX() {} + + std::string encode(); + +}; + +class EncodeUCH : public Encode +{ + +public: + EncodeUCH(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeUCH() {} + + std::string encode(); + +}; + +class EncodeSCH : public Encode +{ + +public: + EncodeSCH(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeSCH() {} + + std::string encode(); + +}; + +class EncodeUIN : public Encode +{ + +public: + EncodeUIN(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeUIN() {} + + std::string encode(); + +}; + +class EncodeSIN : public Encode +{ + +public: + EncodeSIN(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeSIN() {} + + std::string encode(); + +}; + +class EncodeULG : public Encode +{ + +public: + EncodeULG(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeULG() {} + + std::string encode(); + +}; + +class EncodeSLG : public Encode +{ + +public: + EncodeSLG(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeSLG() {} + + std::string encode(); + +}; + +class EncodeFLT : public Encode +{ + +public: + EncodeFLT(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeFLT() {} + + std::string encode(); + +}; + +class EncodeSTR : public Encode +{ + +public: + EncodeSTR(const std::string& data) : Encode(data) {} + ~EncodeSTR() {} + + std::string encode(); + +}; + +class EncodeBCD : public Encode +{ + +public: + EncodeBCD(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeBCD() {} + + std::string encode(); + +}; + +class EncodeD1B : public Encode +{ + +public: + EncodeD1B(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeD1B() {} + + std::string encode(); + +}; + +class EncodeD1C : public Encode +{ + +public: + EncodeD1C(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeD1C() {} + + std::string encode(); + +}; + +class EncodeD2B : public Encode +{ + +public: + EncodeD2B(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeD2B() {} + + std::string encode(); + +}; + +class EncodeD2C : public Encode +{ + +public: + EncodeD2C(const std::string& data, const std::string& factor) + : Encode(data, factor) {} + ~EncodeD2C() {} + + std::string encode(); + +}; + +class EncodeBDA : public Encode +{ + +public: + EncodeBDA(const std::string& data) : Encode(data) {} + ~EncodeBDA() {} + + std::string encode(); + +}; + +class EncodeHDA : public Encode +{ + +public: + EncodeHDA(const std::string& data) : Encode(data) {} + ~EncodeHDA() {} + + std::string encode(); + +}; + +class EncodeBTI : public Encode +{ + +public: + EncodeBTI(const std::string& data) : Encode(data) {} + ~EncodeBTI() {} + + std::string encode(); + +}; + +class EncodeHTI : public Encode +{ + +public: + EncodeHTI(const std::string& data) : Encode(data) {} + ~EncodeHTI() {} + + std::string encode(); + +}; + +class EncodeBDY : public Encode +{ + +public: + EncodeBDY(const std::string& data) : Encode(data) {} + ~EncodeBDY() {} + + std::string encode(); + +}; + +class EncodeHDY : public Encode +{ + +public: + EncodeHDY(const std::string& data) : Encode(data) {} + ~EncodeHDY() {} + + std::string encode(); + +}; + +class EncodeTTM : public Encode +{ + +public: + EncodeTTM(const std::string& data) : Encode(data) {} + ~EncodeTTM() {} + + std::string encode(); + +}; + + +} //namespace + +#endif // LIBEBUS_ENCODE_H_ diff --git a/src/libebus/port.cpp b/src/libebus/port.cpp new file mode 100644 index 00000000..29189934 --- /dev/null +++ b/src/libebus/port.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (C) Roland Jax 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 "port.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace libebus +{ + + +bool Device::isOpen() +{ + if (isValid() == false) + m_open = false; + + return m_open; +} + +bool Device::isValid() +{ + if (m_noDeviceCheck == false) { + int port; + + if (ioctl(m_fd, TIOCMGET, &port) == -1) { + closeDevice(); + m_open = false; + return false; + } + } + + return true; +} + +ssize_t Device::sendBytes(const unsigned char* buffer, size_t nbytes) +{ + if (isValid() == false) + return -1; + + // write bytes to device + return write(m_fd, buffer, nbytes); +} + +ssize_t Device::recvBytes(const long timeout, size_t maxCount) +{ + if (isValid() == false) + return -1; // TODO RESULT_ERR_DEVICE + + if (timeout > 0) { + fd_set readfds; + struct timeval tdiff; + + // set select timeout + tdiff.tv_sec = 0; + tdiff.tv_usec = timeout; + + FD_ZERO(&readfds); + FD_SET(m_fd, &readfds); + + if (select(m_fd + 1, &readfds, NULL, NULL, &tdiff) != 1) + return -2; // TODO RESULT_ERR_TIMEOUT + } + + ssize_t bytes_read = sizeof(m_buffer); + if (maxCount > sizeof(m_buffer)) + maxCount = sizeof(m_buffer); + + + // read bytes from device + bytes_read = read(m_fd, m_buffer, maxCount); + + for (int i = 0; i < bytes_read; i++) + m_recvBuffer.push(m_buffer[i]); + + return bytes_read; +} + +unsigned char Device::getByte() +{ + unsigned char byte; + + if (m_recvBuffer.empty() == false) { + byte = m_recvBuffer.front(); + m_recvBuffer.pop(); + + return byte; + } + + return 0; +} + + +void DeviceSerial::openDevice(const std::string deviceName, const bool noDeviceCheck) +{ + m_noDeviceCheck = noDeviceCheck; + + termios newSettings; + + m_open = false; + + // open file descriptor from serial device + m_fd = open(deviceName.c_str(), O_RDWR | O_NOCTTY); + + if (m_fd < 0) + return; + + // save current settings of serial device + tcgetattr(m_fd, &m_oldSettings); + + memset(&newSettings, '\0', sizeof(newSettings)); + + newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); + newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + newSettings.c_iflag |= IGNPAR; + newSettings.c_oflag &= ~OPOST; + + newSettings.c_cc[VMIN] = 1; + newSettings.c_cc[VTIME] = 0; + + // empty device buffer + tcflush(m_fd, TCIOFLUSH); + + // activate new settings of serial device + tcsetattr(m_fd, TCSANOW, &newSettings); + + // set serial device into blocking mode + fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); + + m_open = true; + +} + +void DeviceSerial::closeDevice() +{ + if (m_open == true) { + // empty device buffer + tcflush(m_fd, TCIOFLUSH); + + // activate old settings of serial device + tcsetattr(m_fd, TCSANOW, &m_oldSettings); + + // close file descriptor from serial device + close(m_fd); + + m_fd = -1; + m_open = false; + } +} + + +void DeviceNetwork::openDevice(const std::string deviceName, const bool noDeviceCheck) +{ + m_noDeviceCheck = noDeviceCheck; + + struct sockaddr_in sock; + char* hostport; + int ret; + + m_open = false; + + memset((char*) &sock, 0, sizeof(sock)); + + hostport = strdup(deviceName.c_str()); + char* host = strtok(hostport, ":"); + char* port = strtok(NULL, ":"); + + if (inet_addr(host) == INADDR_NONE) { + struct hostent* he; + + he = gethostbyname(host); + if (he == NULL) + return; + + memcpy(&sock.sin_addr, he->h_addr_list[0], he->h_length); + } else { + ret = inet_aton(host, &sock.sin_addr); + if (ret == 0) + return; + } + + sock.sin_family = AF_INET; + sock.sin_port = htons(strtol(port, NULL, 10)); + + m_fd = socket(AF_INET, SOCK_STREAM, 0); + if (m_fd < 0) + return; + + ret = connect(m_fd, (struct sockaddr*) &sock, sizeof(sock)); + if (ret < 0) + return; + + free(hostport); + m_open = true; +} + +void DeviceNetwork::closeDevice() +{ + if (m_open == true) { + // close file descriptor from network device + close(m_fd); + + m_fd = -1; + m_open = false; + } +} + + +Port::Port(const std::string deviceName, const bool noDeviceCheck) + : m_deviceName(deviceName), m_noDeviceCheck(noDeviceCheck) +{ + m_device = NULL; + + if (strchr(deviceName.c_str(), '/') == NULL && + strchr(deviceName.c_str(), ':') != NULL) + setType(NETWORK); + else + setType(SERIAL); +} + +void Port::setType(const DeviceType type) +{ + if (m_device != NULL) + delete m_device; + + switch (type) { + case SERIAL: + m_device = new DeviceSerial(); + break; + case NETWORK: + m_device = new DeviceNetwork(); + break; + }; +}; + + +} //namespace + + + diff --git a/src/libebus/port.h b/src/libebus/port.h new file mode 100644 index 00000000..f01798d0 --- /dev/null +++ b/src/libebus/port.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) Roland Jax 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/. + */ + +#ifndef LIBEBUS_PORT_H_ +#define LIBEBUS_PORT_H_ + +#include +#include +#include +#include + +namespace libebus +{ + +enum DeviceType { SERIAL, NETWORK }; + +#define MAX_READ_SIZE 100 + +class Device +{ + +public: + Device() : m_fd(-1), m_open(false), m_noDeviceCheck(false) {} + virtual ~Device() {} + + virtual void openDevice(const std::string deviceName, const bool noDeviceCheck) = 0; + virtual void closeDevice() = 0; + bool isOpen(); + + ssize_t sendBytes(const unsigned char* buffer, size_t nbytes); + ssize_t recvBytes(const long timeout, size_t maxCount); + + unsigned char getByte(); + ssize_t sizeRecvBuffer() const { return m_recvBuffer.size(); } + +protected: + int m_fd; + bool m_open; + bool m_noDeviceCheck; + std::queue m_recvBuffer; + unsigned char m_buffer[MAX_READ_SIZE]; + +private: + bool isValid(); + +}; + +class DeviceSerial : public Device +{ + +public: + ~DeviceSerial() { closeDevice(); } + + void openDevice(const std::string deviceName, const bool noDeviceCheck); + void closeDevice(); + +private: + termios m_oldSettings; + +}; + +class DeviceNetwork : public Device +{ + +public: + ~DeviceNetwork() { closeDevice(); } + + void openDevice(const std::string deviceName, const bool noDeviceCheck); + void closeDevice(); + +private: + +}; + + +class Port +{ + +public: + Port(const std::string deviceName, const bool noDeviceCheck); + ~Port() { delete m_device; } + + void open() { m_device->openDevice(m_deviceName, m_noDeviceCheck); } + void close() { m_device->closeDevice(); } + bool isOpen() { return m_device->isOpen(); } + + ssize_t send(const unsigned char* buffer, size_t nbytes) + { return m_device->sendBytes(buffer, nbytes); } + ssize_t recv(const long timeout, size_t maxCount=MAX_READ_SIZE) { return m_device->recvBytes(timeout, maxCount); } + + unsigned char byte() { return m_device->getByte(); } + ssize_t size() const { return m_device->sizeRecvBuffer(); } + +private: + std::string m_deviceName; + Device* m_device; + bool m_noDeviceCheck; + + void setType(const DeviceType type); + +}; + + +} //namespace + +#endif // LIBEBUS_PORT_H_ diff --git a/src/libebus/result.cpp b/src/libebus/result.cpp new file mode 100644 index 00000000..b7f574f2 --- /dev/null +++ b/src/libebus/result.cpp @@ -0,0 +1,43 @@ +/* + * 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 "result.h" + +namespace libebus +{ + +const char* getResultCodeCStr(int resultCode) { + switch (resultCode) { + case RESULT_ERR_SEND: return "ERR_SEND: send error"; + case RESULT_ERR_EXTRA_DATA: return "ERR_EXTRA_DATA: received bytes > sent bytes"; + case RESULT_ERR_NAK: return "ERR_NAK: NAK received"; + case RESULT_ERR_CRC: return "ERR_CRC: CRC error"; + case RESULT_ERR_ACK: return "ERR_ACK: ACK error"; + case RESULT_ERR_TIMEOUT: return "ERR_TIMEOUT: read timeout"; + case RESULT_ERR_SYN: return "ERR_SYN: SYN received"; + case RESULT_ERR_BUS_LOST: return "ERR_BUS_LOST: lost bus arbitration"; + case RESULT_ERR_ESC: return "ERR_ESC: invalid escape sequence received"; + case RESULT_ERR_INVALID_ARG: return "ERR_INVALID_ARG: invalid argument specified"; + case RESULT_ERR_DEVICE: return "ERR_DEVICE: generic device error"; + default: return "success"; + } +} + + +} //namespace diff --git a/src/libebus/result.h b/src/libebus/result.h new file mode 100644 index 00000000..9248e204 --- /dev/null +++ b/src/libebus/result.h @@ -0,0 +1,58 @@ +/* + * 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/. + */ + +#ifndef LIBEBUS_RESULT_H_ +#define LIBEBUS_RESULT_H_ + +namespace libebus +{ + + +static const int RESULT_OK = 0; + +static const int RESULT_BUS_ACQUIRED = 1; // bus successfully acquired +static const int RESULT_DATA = 2; // some data received +static const int RESULT_SYN = 3; // regular SYN after message received +static const int RESULT_BUS_LOCKED = 4; // bus is locked for access +static const int RESULT_BUS_PRIOR_RETRY = 5; // retry to access bus + +static const int RESULT_ERR_SEND = -1; // send error +static const int RESULT_ERR_EXTRA_DATA = -2; // received bytes > sent bytes +static const int RESULT_ERR_NAK = -3; // NAK received +static const int RESULT_ERR_CRC = -4; // CRC error +static const int RESULT_ERR_ACK = -5; // ACK error +static const int RESULT_ERR_TIMEOUT = -6; // read timeout +static const int RESULT_ERR_SYN = -7; // SYN received +static const int RESULT_ERR_BUS_LOST = -8; // arbitration lost +static const int RESULT_ERR_ESC = -9; // invalid escape sequence received +static const int RESULT_ERR_INVALID_ARG = -10; // invalid argument +static const int RESULT_ERR_DEVICE = -11; // generic device error (usually fatal) + + +/** + * @brief Return the string corresponding to the result code. + * @param resultCode the result code (see RESULT_ constants). + * @return the string corresponding to the result code. + */ +const char* getResultCodeCStr(int resultCode); + + +} //namespace + +#endif // LIBEBUS_RESULT_H_ diff --git a/src/libebus/symbol.cpp b/src/libebus/symbol.cpp new file mode 100644 index 00000000..f9c571b7 --- /dev/null +++ b/src/libebus/symbol.cpp @@ -0,0 +1,176 @@ +/* + * 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 "symbol.h" +#include +#include + +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[] +{ + 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee, + 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc, + 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a, + 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8, + 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26, + 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14, + 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42, + 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70, + 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5, + 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7, + 0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81, + 0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3, + 0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d, + 0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f, + 0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49, + 0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b, +}; + + +SymbolString::SymbolString(const std::string str) + : 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); + } + // add CRC + escape + push_back_escape(m_crc, false); +} + +SymbolString::SymbolString(const std::string str, bool escaped) + : 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); + } +} + +const std::string SymbolString::getDataStr(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) { + if (value == 0x00) { + sstr << "a9"; // ESC + } + else if (value == 0x01) { + sstr << "aa"; // SYN + } + else { + sstr << "XX"; // invalid escape sequence + } + previousEscape = false; + } + else if (unescape == true && value == ESC) { + previousEscape = true; // escape sequence not yet finished + } + else { + sstr << std::nouppercase << std::setw(2) << std::hex + << std::setfill('0') << static_cast(value); + } + } + + return sstr.str(); +} + +void SymbolString::push_back_escape(const unsigned char value, bool updateCRC) +{ + if (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 { + m_data.push_back(value); + 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; + } + if (value == 0x01) { + m_data.push_back(SYN); + previousEscape = false; + return SYN; + } + return 0; // invalid escape sequence + } + if (value == ESC) { + previousEscape = true; + return 1; // escape sequence not yet finished + } + m_data.push_back(value); + return value; +} + +void SymbolString::addCRC(const unsigned char value) { + m_crc = CRC_LOOKUP_TABLE[m_crc]^value; +} + + + +bool isMaster(unsigned char addr) { + unsigned char addrHi = (addr & 0xF0) >> 4; + unsigned char addrLo = (addr & 0x0F); + + return ((addrHi == 0x0) || (addrHi == 0x1) || (addrHi == 0x3) || (addrHi == 0x7) || (addrHi == 0xF)) + && ((addrLo == 0x0) || (addrLo == 0x1) || (addrLo == 0x3) || (addrLo == 0x7) || (addrLo == 0xF)); +} + +} //namespace diff --git a/src/libebus/symbol.h b/src/libebus/symbol.h new file mode 100644 index 00000000..0498809e --- /dev/null +++ b/src/libebus/symbol.h @@ -0,0 +1,148 @@ +/* + * 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/. + */ + +#ifndef LIBEBUS_SYMBOL_H_ +#define LIBEBUS_SYMBOL_H_ + +#include +#include +#include +#include + +namespace libebus +{ + + +static const unsigned char ESC = 0xA9; // escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA +static const unsigned char SYN = 0xAA; // synchronization symbol +static const unsigned char ACK = 0x00; // positive acknowledge +static const unsigned char NAK = 0xFF; // negative acknowledge +static const unsigned char BROADCAST = 0xFE; // the broadcast destination address + + +/** + * @brief A string of bus symbols. + */ +class SymbolString +{ +public: + /** + * @brief Creates a new empty SymbolString. + */ + SymbolString() : m_crc(0) {} + /** + * @brief Creates a new escaped SymbolString 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. + * @param str the hex string. + */ + SymbolString(const std::string str, bool escaped); + /** + * @brief Returns the symbols as hex string. + * @param escaped whether to unescape the symbols. + * @return the symbols as hex string. + */ + const std::string getDataStr(bool unescape=false); + /** + * @brief Returns 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. + */ + unsigned char at(const size_t index) { return m_data.at(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]; } + /** + * @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) const { return m_data[index]; } + /** + * @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. + * @param value the symbol to append. + * @param updateCrc whether to update the calculated CRC in @a m_crc. + */ + 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); + /** + * @brief Returns the number of symbols in this symbol string. + * @return the number of available symbols. + */ + size_t size() const { return m_data.size(); } + /** + * @brief Returns the calculated CRC. + * @return the calculated CRC. + */ + unsigned char getCRC() const { return m_crc; } + /** + * @brief Clears the symbols. + */ + void clear() { m_crc=0; m_data.clear(); } + +private: + /** + * @brief Updates the calculated CRC in @a m_crc by adding a value. + * @param value the (escaped) value to add to the calculated CRC in @a m_crc. + */ + void addCRC(const unsigned char value); + + /** + * @brief the string of bus symbols. + */ + std::vector m_data; + /** + * @brief the calculated CRC. + */ + unsigned char m_crc; +}; + + +/** + * Returns whether the address is one of the 25 master addresses. + * @param addr the address to check. + * @return true if the specified address is a master address. + */ +bool isMaster(unsigned char addr); + + +} //namespace + +#endif // LIBEBUS_SYMBOL_H_ diff --git a/src/test/Makefile.am b/src/test/Makefile.am new file mode 100644 index 00000000..b2befdc6 --- /dev/null +++ b/src/test/Makefile.am @@ -0,0 +1,37 @@ +AM_CXXFLAGS = -fpic \ + -Wall \ + -Wextra \ + -I$(top_srcdir)/src/libebus + +noinst_PROGRAMS = test_port \ + test_symbol \ + test_bus \ + test_commands \ + test_configfile \ + test_decode \ + test_encode + +test_port_SOURCES = test_port.cpp +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_bus_SOURCES = test_bus.cpp +test_bus_LDADD = $(top_srcdir)/src/libebus/libebus.a + +test_commands_SOURCES = test_commands.cpp +test_commands_LDADD = $(top_srcdir)/src/libebus/libebus.a + +test_configfile_SOURCES = test_configfile.cpp +test_configfile_LDADD = $(top_srcdir)/src/libebus/libebus.a + +test_decode_SOURCES = test_decode.cpp +test_decode_LDADD = $(top_srcdir)/src/libebus/libebus.a + +test_encode_SOURCES = test_encode.cpp +test_encode_LDADD = $(top_srcdir)/src/libebus/libebus.a + +distclean-local: + -rm -f Makefile.in + -rm -rf .libs diff --git a/src/test/command.csv b/src/test/command.csv new file mode 100644 index 00000000..501ccbff --- /dev/null +++ b/src/test/command.csv @@ -0,0 +1,4 @@ +# type;class;cmd;comment;msg type;ZZ;PBSB;NN;D1D2D3;elements;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment;sub;part;position;datatype;factor;unit;valid;comment +c;broad;date_time_temp;Datum, Uhrzeit und Aussentempertur;BR;FE;0700;9;-;4;temp;md;1,2;d2b;1.0;°C;-;Temperatur;day;md;8;bdy;-;-;-;Wochentag;date;md;6,7,9;bda;-;-;-;Datum;time;md;5,4,3;bti;-;-;-;Uhrzeit +g;ci;Password;Kennwort;MS;15;B509;3;0D2C00;4;pin1;sd;1;uch;-;-;-;Pin 1;pin2;sd;2;uch;-;-;-;Pin 2;pin3;sd;3;uch;-;-;-;Pin 3;pin4;sd;4;uch;-;-;-;Pin 4 +s;vwxmk;DesiredTemp;Solltemperatur;MS;50;B509;4;0E3200;1;-;sd;1;d1c;1.0;°C;-;-;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/test/test_bus.cpp b/src/test/test_bus.cpp new file mode 100644 index 00000000..2e165fce --- /dev/null +++ b/src/test/test_bus.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) Roland Jax 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 "bus.h" +#include +#include + +using namespace libebus; + +int main () +{ + Bus bus("/dev/ttyUSB0", true, 15000, "/tmp/dump_bus.bin", 100, false); + + bus.connect(); + + if (bus.isConnected() == true) + std::cout << "connect successful." << std::endl; + + int cout = 0; + + while (cout++ < 1000) { + if (bus.isConnected() == true) { + bus.printBytes(); + } else { + sleep(5); + bus.connect(); + + if (bus.isConnected() == false) + std::cout << "can't open /dev/ttyUSB0" << std::endl; + else + std::cout << "reconnect successful." << std::endl; + } + } + + bus.disconnect(); + + if (bus.isConnected() == false) + std::cout << "disconnect successful." << std::endl; + + return 0; + +} diff --git a/src/test/test_commands.cpp b/src/test/test_commands.cpp new file mode 100644 index 00000000..41eb4a07 --- /dev/null +++ b/src/test/test_commands.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) Roland Jax 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 "configfile.h" +#include "commands.h" +#include +#include +#include +#include +#include + +using namespace libebus; + +// will be part of cfg csv class +void readCSV(std::istream& is, Commands& commands){ + std::string line; + + // read lines + while (std::getline(is, line) != 0) { + std::vector row; + std::string column; + int count; + + count = 0; + + std::istringstream stream(line); + + // walk through columns + while (std::getline(stream, column, ';') != 0) { + row.push_back(column); + count++; + } + + commands.addCommand(row); + } +} + +int main() +{ + Commands* commands = ConfigCommands("test", CSV).getCommands(); + std::cout << "Commands: " << commands->sizeCmdDB() << std::endl; + + //~ std::string data("g ci password pin1"); + std::string data("s vwxmk DesiredTemp"); + + int index = commands->findCommand(data); + std::cout << "found at index: " << index << std::endl; + + // prepare data + std::string token; + std::istringstream stream(data); + std::vector cmd; + + // split stream + while (std::getline(stream, token, ' ') != 0) + cmd.push_back(token); + + //~ Command* command = new Command(index, (*commands)[index], "ff15b509030d2c0035000401000000cf00"); + Command* command = new Command(index, (*commands)[index], "19.0"); + + //~ std::string result = command->calcResult(cmd); + std::string result = command->calcData(); + std::cout << "result: " << result << std::endl; + + delete command; + + return 0; +} + + diff --git a/src/test/test_configfile.cpp b/src/test/test_configfile.cpp new file mode 100644 index 00000000..35cc60e8 --- /dev/null +++ b/src/test/test_configfile.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) Roland Jax 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 "configfile.h" +#include +#include +#include +#include +#include + +using namespace libebus; + + +int main() { + + std::string dir("test"); + ConfigCommands config(dir, CSV); + + Commands* commands = config.getCommands(); + + std::cout << "size: " << commands->sizeCmdDB() << std::endl; + + commands->findCommand("g ci Password"); + + std::cout << (*commands)[0][0] << std::endl; + + delete commands; + + return 0; +} + + diff --git a/src/test/test_decode.cpp b/src/test/test_decode.cpp new file mode 100644 index 00000000..aeb7e8c9 --- /dev/null +++ b/src/test/test_decode.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) Roland Jax 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 "decode.h" +#include +#include + + +using namespace libebus; + + +int main() +{ + Decode* help_dec = NULL; + + std::cout << std::endl; + + // HEX + { + const char* hex[] = {"53706569636865722020"}; + for (size_t i = 0; i < sizeof(hex)/sizeof(hex[0]); i++) { + help_dec = new DecodeHEX(hex[i]); + std::cout << "DecodeHEX: " << std::setw(20) << hex[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // UCH + { + const char* uch[] = {"00", "01", "7f", "80", "fe", "ff", "a1"}; + for (size_t i = 0; i < sizeof(uch)/sizeof(uch[0]); i++) { + help_dec = new DecodeUCH(uch[i], "1.0"); + std::cout << "DecodeUCH: " << std::setw(20) << uch[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // SCH + { + const char* sch[] = {"00", "01", "7f", "80", "fe", "ff", "a1"}; + for (size_t i = 0; i < sizeof(sch)/sizeof(sch[0]); i++) { + help_dec = new DecodeSCH(sch[i], "1.0"); + std::cout << "DecodeSCH: " << std::setw(20) << sch[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // UIN + { + const char* uin[] = {"0000", "0001", "7fff", "8000", "fffe", "ffff", "a1b2"}; + for (size_t i = 0; i < sizeof(uin)/sizeof(uin[0]); i++) { + help_dec = new DecodeUIN(uin[i], "1.0"); + std::cout << "DecodeUIN: " << std::setw(20) << uin[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // SIN + { + const char* sin[] = {"0000", "0001", "7fff", "8000", "fffe", "ffff", "a1b2"}; + for (size_t i = 0; i < sizeof(sin)/sizeof(sin[0]); i++) { + help_dec = new DecodeSIN(sin[i], "1.0"); + std::cout << "DecodeSIN: " << std::setw(20) << sin[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // ULG + { + const char* ulg[] = {"00000000", "00000001", "7fffffff", "80000000", "fffffffe", "ffffffff", "a1b2c3d4"}; + for (size_t i = 0; i < sizeof(ulg)/sizeof(ulg[0]); i++) { + help_dec = new DecodeULG(ulg[i], "1.0"); + std::cout << "DecodeULG: " << std::setw(20) << ulg[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // SLG + { + const char* slg[] = {"00000000", "00000001", "7fffffff", "80000000", "fffffffe", "ffffffff", "a1b2c3d4"}; + for (size_t i = 0; i < sizeof(slg)/sizeof(slg[0]); i++) { + help_dec = new DecodeSLG(slg[i], "1.0"); + std::cout << "DecodeSLG: " << std::setw(20) << slg[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // FLT + { + const char* flt[] = {"0000", "081b", "2532", "2689", "0851"}; + for (size_t i = 0; i < sizeof(flt)/sizeof(flt[0]); i++) { + help_dec = new DecodeFLT(flt[i], "1.0"); + std::cout << "DecodeFLT: " << std::setw(20) << flt[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // STR + { + const char* str[] = {"53706569636865722020", "5644363030" }; + for (size_t i = 0; i < sizeof(str)/sizeof(str[0]); i++) { + help_dec = new DecodeSTR(str[i]); + std::cout << "DecodeSTR: " << std::setw(20) << str[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // BCD + { + const char* bcd[] = {"00", "01", "02", "03", "12", "99"}; + for (size_t i = 0; i < sizeof(bcd)/sizeof(bcd[0]); i++) { + help_dec = new DecodeBCD(bcd[i], "1.0"); + std::cout << "DecodeBCD: " << std::setw(20) << bcd[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // D1B + { + const char* d1b[] = {"00", "01", "7f", "81", "80"}; + for (size_t i = 0; i < sizeof(d1b)/sizeof(d1b[0]); i++) { + help_dec = new DecodeD1B(d1b[i], "1.0"); + std::cout << "DecodeD1B: " << std::setw(20) << d1b[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // D1C + { + const char* d1c[] = {"00", "64", "c8"}; + for (size_t i = 0; i < sizeof(d1c)/sizeof(d1c[0]); i++) { + help_dec = new DecodeD1C(d1c[i], "1.0"); + std::cout << "DecodeD1C: " << std::setw(20) << d1c[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // D2B + { + const char* d2b[] = {"0000", "0100", "ffff", "00ff", "0080", "0180", "ff7f"}; + for (size_t i = 0; i < sizeof(d2b)/sizeof(d2b[0]); i++) { + help_dec = new DecodeD2B(d2b[i], "1.0"); + std::cout << "DecodeD2B: " << std::setw(20) << d2b[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // D2C + { + const char* d2c[] = {"0000", "0100", "ffff", "f0ff", "0080", "0180", "ff7f"}; + for (size_t i = 0; i < sizeof(d2c)/sizeof(d2c[0]); i++) { + help_dec = new DecodeD2C(d2c[i], "1.0"); + std::cout << "DecodeD2C: " << std::setw(20) << d2c[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // BDA + { + const char* bda[] = {"171113", "220901"}; + for (size_t i = 0; i < sizeof(bda)/sizeof(bda[0]); i++) { + help_dec = new DecodeBDA(bda[i]); + std::cout << "DecodeBDA: " << std::setw(20) << bda[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // HDA + { + const char* hda[] = {"010101", "1f0c1b"}; + for (size_t i = 0; i < sizeof(hda)/sizeof(hda[0]); i++) { + help_dec = new DecodeHDA(hda[i]); + std::cout << "DecodeHDA: " << std::setw(20) << hda[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // BTI + { + const char* bti[] = {"010101", "174209", "235959"}; + for (size_t i = 0; i < sizeof(bti)/sizeof(bti[0]); i++) { + help_dec = new DecodeBTI(bti[i]); + std::cout << "DecodeBTI: " << std::setw(20) << bti[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // HTI + { + const char* hti[] = {"010101", "112a09", "173b3b"}; + for (size_t i = 0; i < sizeof(hti)/sizeof(hti[0]); i++) { + help_dec = new DecodeHTI(hti[i]); + std::cout << "DecodeHTI: " << std::setw(20) << hti[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // BDY + { + const char* bdy[] = {"01", "03", "06", "07"}; + for (size_t i = 0; i < sizeof(bdy)/sizeof(bdy[0]); i++) { + help_dec = new DecodeBDY(bdy[i]); + std::cout << "DecodeBDY: " << std::setw(20) << bdy[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // HDY + { + const char* hdy[] = {"01", "03", "07", "08"}; + for (size_t i = 0; i < sizeof(hdy)/sizeof(hdy[0]); i++) { + help_dec = new DecodeHDY(hdy[i]); + std::cout << "DecodeHDY: " << std::setw(20) << hdy[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + // TTM + { + const char* ttm[] = {"00", "23", "4f", "90"}; + for (size_t i = 0; i < sizeof(ttm)/sizeof(ttm[0]); i++) { + help_dec = new DecodeTTM(ttm[i]); + std::cout << "DecodeTTM: " << std::setw(20) << ttm[i] << " = " << help_dec->decode() << std::endl; + + delete help_dec; + } + + std::cout << std::endl; + } + + return 0; +} + + diff --git a/src/test/test_encode.cpp b/src/test/test_encode.cpp new file mode 100644 index 00000000..5977057b --- /dev/null +++ b/src/test/test_encode.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) Roland Jax 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 "encode.h" +#include +#include + + +using namespace libebus; + + +int main() +{ + Encode* help_enc = NULL; + + std::cout << std::endl; + + // HEX + { + const char* hex[] = {"53 70 65 69 63 68 65 72 20 20"}; + for (size_t i = 0; i < sizeof(hex)/sizeof(hex[0]); i++) { + help_enc = new EncodeHEX(hex[i]); + std::cout << "EncodeHEX: " << std::setw(20) << hex[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // UCH + { + const char* uch[] = {"0", "1", "127", "128", "254", "255", "161"}; + for (size_t i = 0; i < sizeof(uch)/sizeof(uch[0]); i++) { + help_enc = new EncodeUCH(uch[i], "1.0"); + std::cout << "EncodeUCH: " << std::setw(20) << uch[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // SCH + { + const char* sch[] = {"0", "1", "127", "-128", "-2", "-1", "-95"}; + for (size_t i = 0; i < sizeof(sch)/sizeof(sch[0]); i++) { + help_enc = new EncodeSCH(sch[i], "1.0"); + std::cout << "EncodeSCH: " << std::setw(20) << sch[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // UIN + { + const char* uin[] = {"0", "1", "32767", "32768", "65534", "65535", "41394"}; + for (size_t i = 0; i < sizeof(uin)/sizeof(uin[0]); i++) { + help_enc = new EncodeUIN(uin[i], "1.0"); + std::cout << "EncodeUIN: " << std::setw(20) << uin[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // SIN + { + const char* sin[] = {"0", "1", "32767", "-32768", "-2", "-1", "-24142"}; + for (size_t i = 0; i < sizeof(sin)/sizeof(sin[0]); i++) { + help_enc = new EncodeSIN(sin[i], "1.0"); + std::cout << "EncodeSIN: " << std::setw(20) << sin[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // ULG + { + const char* ulg[] = {"0", "1", "2147483647", "2147483648", "4294967294", "4294967295", "2712847316"}; + for (size_t i = 0; i < sizeof(ulg)/sizeof(ulg[0]); i++) { + help_enc = new EncodeULG(ulg[i], "1.0"); + std::cout << "EncodeULG: " << std::setw(20) << ulg[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // SLG + { + const char* slg[] = {"0", "1", "2147483647", "-2147483648", "-2", "-1", "-1582119980"}; + for (size_t i = 0; i < sizeof(slg)/sizeof(slg[0]); i++) { + help_enc = new EncodeSLG(slg[i], "1.0"); + std::cout << "EncodeSLG: " << std::setw(20) << slg[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // FLT + { + const char* flt[] = {"0.000", "2.075", "9.522", "9.865", "2.129"}; + for (size_t i = 0; i < sizeof(flt)/sizeof(flt[0]); i++) { + help_enc = new EncodeFLT(flt[i], "1.0"); + std::cout << "EncodeFLT: " << std::setw(20) << flt[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // STR + { + const char* str[] = {"Speicher ", "VD600" }; + for (size_t i = 0; i < sizeof(str)/sizeof(str[0]); i++) { + help_enc = new EncodeSTR(str[i]); + std::cout << "EncodeSTR: " << std::setw(20) << str[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // BCD + { + const char* bcd[] = {"0", "1", "2", "3", "12", "99"}; + for (size_t i = 0; i < sizeof(bcd)/sizeof(bcd[0]); i++) { + help_enc = new EncodeBCD(bcd[i], "1.0"); + std::cout << "EncodeBCD: " << std::setw(20) << bcd[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // D1B + { + const char* d1b[] = {"00", "01", "127", "-127", "-128"}; + for (size_t i = 0; i < sizeof(d1b)/sizeof(d1b[0]); i++) { + help_enc = new EncodeD1B(d1b[i], "1.0"); + std::cout << "EncodeD1B: " << std::setw(20) << d1b[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // D1C + { + const char* d1c[] = {"0", "50", "100"}; + for (size_t i = 0; i < sizeof(d1c)/sizeof(d1c[0]); i++) { + help_enc = new EncodeD1C(d1c[i], "1.0"); + std::cout << "EncodeD1C: " << std::setw(20) << d1c[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // D2B + { + const char* d2b[] = {"0", "0.00390625", "-0.00390625", "-1", "-128", "-127.99609375", "127.99609375"}; + for (size_t i = 0; i < sizeof(d2b)/sizeof(d2b[0]); i++) { + help_enc = new EncodeD2B(d2b[i], "1.0"); + std::cout << "EncodeD2B: " << std::setw(20) << d2b[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // D2C + { + const char* d2c[] = {"0", "0.0625", "-0.0625", "-1", "-2048", "-2047.9375", "2047.9375"}; + for (size_t i = 0; i < sizeof(d2c)/sizeof(d2c[0]); i++) { + help_enc = new EncodeD2C(d2c[i], "1.0"); + std::cout << "EncodeD2C: " << std::setw(20) << d2c[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // BDA + { + const char* bda[] = {"17.11.2013", "22.09.2001"}; + for (size_t i = 0; i < sizeof(bda)/sizeof(bda[0]); i++) { + help_enc = new EncodeBDA(bda[i]); + std::cout << "EncodeBDA: " << std::setw(20) << bda[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // HDA + { + const char* hda[] = {"01.01.2001", "31.12.2027"}; + for (size_t i = 0; i < sizeof(hda)/sizeof(hda[0]); i++) { + help_enc = new EncodeHDA(hda[i]); + std::cout << "EncodeHDA: " << std::setw(20) << hda[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // BTI + { + const char* bti[] = {"01:01:01", "17:42:09", "23:59:59"}; + for (size_t i = 0; i < sizeof(bti)/sizeof(bti[0]); i++) { + help_enc = new EncodeBTI(bti[i]); + std::cout << "EncodeBTI: " << std::setw(20) << bti[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // HTI + { + const char* hti[] = {"01:01:01", "17:42:09", "23:59:59"}; + for (size_t i = 0; i < sizeof(hti)/sizeof(hti[0]); i++) { + help_enc = new EncodeHTI(hti[i]); + std::cout << "EncodeHTI: " << std::setw(20) << hti[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // BDY + { + const char* bdy[] = {"Tue", "Thu", "Sun", "Err"}; + for (size_t i = 0; i < sizeof(bdy)/sizeof(bdy[0]); i++) { + help_enc = new EncodeBDY(bdy[i]); + std::cout << "EncodeBDY: " << std::setw(20) << bdy[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + + // HDY + { + const char* hdy[] = {"Mon", "Wed", "Sun", "Err"}; + for (size_t i = 0; i < sizeof(hdy)/sizeof(hdy[0]); i++) { + help_enc = new EncodeHDY(hdy[i]); + std::cout << "EncodeHDY: " << std::setw(20) << hdy[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + // TTM + { + const char* ttm[] = {"00:00", "05:50", "13:10", "24:00"}; + for (size_t i = 0; i < sizeof(ttm)/sizeof(ttm[0]); i++) { + help_enc = new EncodeTTM(ttm[i]); + std::cout << "EncodeTTM: " << std::setw(20) << ttm[i] << " = " << help_enc->encode() << std::endl; + + delete help_enc; + } + + std::cout << std::endl; + } + + return 0; +} + + diff --git a/src/test/test_port.cpp b/src/test/test_port.cpp new file mode 100644 index 00000000..27640f3c --- /dev/null +++ b/src/test/test_port.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) Roland Jax 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 "port.h" +#include +#include + +using namespace libebus; + +int main () +{ + std::string dev("/dev/ttyUSB20"); + Port port(dev, true); + + port.open(); + + if(port.isOpen() == true) + std::cout << "openPort successful." << std::endl; + + int count = 0; + + while (1) { + ssize_t bytes_read; + unsigned char byte = 0; + bytes_read = port.recv(0); + + for (int i = 0; i < bytes_read; i++) + byte = port.byte(); + std::cout << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte) << std::endl; + + bytes_read = 0; + + count++; + } + + port.close(); + + if(port.isOpen() == false) + std::cout << "closePort successful." << std::endl; + + return 0; + +} diff --git a/src/test/test_symbol.cpp b/src/test/test_symbol.cpp new file mode 100644 index 00000000..d261f43d --- /dev/null +++ b/src/test/test_symbol.cpp @@ -0,0 +1,75 @@ +/* + * 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 "symbol.h" +#include +#include + +using namespace libebus; + +int main () +{ + SymbolString sstr("10feb5050427a915aa"); + + std::stringstream out; + for (size_t i = 0; i(sstr[i]); + } + + std::string gotStr = out.str(), expectStr = "10feb5050427a90015a90177"; + + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "ctor escaped successful." << std::endl; + else + std::cout << "ctor escaped invalid: got " << gotStr + << ", expected " << expectStr << std::endl; + + unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; + + if (gotCrc == expectCrc) + std::cout << "CRC successful." << std::endl; + else + std::cout << "CRC invalid: got 0x" + << std::nouppercase << std::setw(2) << std::hex + << std::setfill('0') << static_cast(gotCrc) + << ", expected 0x" + << std::nouppercase << std::setw(2) << std::hex + << std::setfill('0') << static_cast(expectCrc) + << std::endl; + + gotStr = sstr.getDataStr(true), expectStr = "10feb5050427a915aa77"; + + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "unescape successful." << std::endl; + else + std::cout << "unescape invalid: got " << gotStr + << ", expected " << expectStr << std::endl; + + sstr = SymbolString("10feb5050427a90015a90177", true); + gotStr = sstr.getDataStr(false); + if (strcasecmp(gotStr.c_str(), expectStr.c_str()) == 0) + std::cout << "ctor unescaped successful." << std::endl; + else + std::cout << "ctor unescaped invalid: got " << gotStr + << ", expected " << expectStr << std::endl; + + return 0; + +} diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am new file mode 100644 index 00000000..c9b83ddf --- /dev/null +++ b/src/tools/Makefile.am @@ -0,0 +1,17 @@ +AM_CXXFLAGS = -fpic \ + -Wall \ + -Wextra \ + -I$(top_srcdir)/src/libcore \ + -I$(top_srcdir)/src/libebus + +bin_PROGRAMS = ebusctl + +ebusctl_SOURCES = ebusctl.cpp + +ebusctl_LDADD = $(top_srcdir)/src/libcore/libcore.a \ + $(top_srcdir)/src/libebus/libebus.a + +distclean-local: + -rm -f Makefile.in + -rm -rf .libs + diff --git a/tools/ebusctl.cpp b/src/tools/ebusctl.cpp similarity index 99% rename from tools/ebusctl.cpp rename to src/tools/ebusctl.cpp index d9b4eb43..6eb62833 100644 --- a/tools/ebusctl.cpp +++ b/src/tools/ebusctl.cpp @@ -17,7 +17,8 @@ * along with ebusd. If not, see http://www.gnu.org/licenses/. */ -#include "libebus.h" +#include "port.h" +#include "decode.h" #include "appl.h" #include "tcpsocket.h" #include