Merge pull request #18 from john30/master; first version supporting get, set, and cyc again.
This commit is contained in:
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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 << ';';
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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_
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user