Merge pull request #18 from john30/master; first version supporting get, set, and cyc again.

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