use spaces instead of tab

This commit is contained in:
john30
2017-01-14 18:01:37 +01:00
parent 917414a6f8
commit ebcb260d5f
47 changed files with 13575 additions and 13575 deletions
+980 -980
View File
File diff suppressed because it is too large Load Diff
+427 -427
View File
File diff suppressed because it is too large Load Diff
+20 -20
View File
@@ -17,14 +17,14 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "datahandler.h" #include "datahandler.h"
#include <list> #include <list>
#include <string> #include <string>
#ifdef HAVE_MQTT #ifdef HAVE_MQTT
# include "mqtthandler.h" # include "mqtthandler.h"
#endif #endif
namespace ebusd { namespace ebusd {
@@ -37,38 +37,38 @@ static const struct argp_child g_last_argp_child = {NULL, 0, NULL, 0};
/** the list of @a argp_child structures. */ /** the list of @a argp_child structures. */
static struct argp_child g_argp_children[ static struct argp_child g_argp_children[
#ifdef HAVE_MQTT #ifdef HAVE_MQTT
1 1
#endif #endif
+1 +1
]; ];
const struct argp_child* datahandler_getargs() { const struct argp_child* datahandler_getargs() {
size_t count = 0; size_t count = 0;
#ifdef HAVE_MQTT #ifdef HAVE_MQTT
g_argp_children[count++] = *mqtthandler_getargs(); g_argp_children[count++] = *mqtthandler_getargs();
#endif #endif
if (count > 0) { if (count > 0) {
g_argp_children[count] = g_last_argp_child; g_argp_children[count] = g_last_argp_child;
return g_argp_children; return g_argp_children;
} }
return NULL; return NULL;
} }
bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<DataHandler*>& handlers) { bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<DataHandler*>& handlers) {
bool success = true; bool success = true;
#ifdef HAVE_MQTT #ifdef HAVE_MQTT
DataHandler* handler = mqtthandler_register(busHandler, messages); DataHandler* handler = mqtthandler_register(busHandler, messages);
if (handler) { if (handler) {
handlers.push_back(handler); handlers.push_back(handler);
} else { } else {
success = false; success = false;
} }
#endif #endif
return success; return success;
} }
void DataSink::notifyUpdate(Message* message) { void DataSink::notifyUpdate(Message* message) {
m_updatedMessages[message]++; m_updatedMessages[message]++;
} }
} // namespace ebusd } // namespace ebusd
+58 -58
View File
@@ -58,33 +58,33 @@ bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<Dat
* Base class for all kinds of data handlers. * Base class for all kinds of data handlers.
*/ */
class DataHandler { class DataHandler {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
DataHandler() {} DataHandler() {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DataHandler() {} virtual ~DataHandler() {}
/** /**
* Called to start the @a DataHandler. * Called to start the @a DataHandler.
*/ */
virtual void start() = 0; virtual void start() = 0;
/** /**
* Return whether this is a @a DataSink instance. * Return whether this is a @a DataSink instance.
* @return whether this is a @a DataSink instance. * @return whether this is a @a DataSink instance.
*/ */
virtual bool isDataSink() { return false; } virtual bool isDataSink() { return false; }
/** /**
* Return whether this is a @a DataSource instance. * Return whether this is a @a DataSource instance.
* @return whether this is a @a DataSource instance. * @return whether this is a @a DataSource instance.
*/ */
virtual bool isDataSource() { return false; } virtual bool isDataSource() { return false; }
}; };
@@ -92,30 +92,30 @@ class DataHandler {
* Base class for listening to data updates. * Base class for listening to data updates.
*/ */
class DataSink : virtual public DataHandler { class DataSink : virtual public DataHandler {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
DataSink() {} DataSink() {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DataSink() {} virtual ~DataSink() {}
/** /**
* Notify the sink of an updated @a Message. * Notify the sink of an updated @a Message.
* @param message the updated @a Message. * @param message the updated @a Message.
*/ */
virtual void notifyUpdate(Message* message); virtual void notifyUpdate(Message* message);
// @copydoc // @copydoc
virtual bool isDataSink() { return true; } virtual bool isDataSink() { return true; }
protected: protected:
/** a map of updated @p Message instances. */ /** a map of updated @p Message instances. */
map<Message*, int> m_updatedMessages; map<Message*, int> m_updatedMessages;
}; };
@@ -123,26 +123,26 @@ class DataSink : virtual public DataHandler {
* Base class providing data to be sent on the bus. * Base class providing data to be sent on the bus.
*/ */
class DataSource : virtual public DataHandler { class DataSource : virtual public DataHandler {
public: public:
/** /**
* Constructor. * Constructor.
* @param busHandler the @a BusHandler instance. * @param busHandler the @a BusHandler instance.
*/ */
explicit DataSource(BusHandler* busHandler) explicit DataSource(BusHandler* busHandler)
: m_busHandler(busHandler) {} : m_busHandler(busHandler) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DataSource() {} virtual ~DataSource() {}
// @copydoc // @copydoc
virtual bool isDataSource() { return true; } virtual bool isDataSource() { return true; }
protected: protected:
/** the @a BusHandler instance. */ /** the @a BusHandler instance. */
BusHandler* m_busHandler; BusHandler* m_busHandler;
}; };
} // namespace ebusd } // namespace ebusd
+768 -768
View File
File diff suppressed because it is too large Load Diff
+32 -32
View File
@@ -31,44 +31,44 @@ namespace ebusd {
/** A structure holding all program options. */ /** A structure holding all program options. */
struct options { struct options {
const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0] const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0]
bool noDeviceCheck; //!< skip serial eBUS device test bool noDeviceCheck; //!< skip serial eBUS device test
bool readOnly; //!< read-only access to the device bool readOnly; //!< read-only access to the device
bool initialSend; //!< send an initial escape symbol after connecting device bool initialSend; //!< send an initial escape symbol after connecting device
int latency; //!< transfer latency in us [0 for USB, 10000 for IP] int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
const char* configPath; //!< path to CSV configuration files [/etc/ebusd] const char* configPath; //!< path to CSV configuration files [/etc/ebusd]
bool scanConfig; //!< pick configuration files matching initial scan bool scanConfig; //!< pick configuration files matching initial scan
unsigned char initialScan; //!< the initial address to scan for scanconfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address) unsigned char initialScan; //!< the initial address to scan for scanconfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address)
int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop
int pollInterval; //!< poll interval in seconds, 0 to disable [5] int pollInterval; //!< poll interval in seconds, 0 to disable [5]
unsigned char address; //!< own bus address [31] unsigned char address; //!< own bus address [31]
bool answer; //!< answer to requests from other masters bool answer; //!< answer to requests from other masters
int acquireTimeout; //!< bus acquisition timeout in us [9400] int acquireTimeout; //!< bus acquisition timeout in us [9400]
int acquireRetries; //!< number of retries for bus acquisition [3] int acquireRetries; //!< number of retries for bus acquisition [3]
int sendRetries; //!< number of retries for failed sends [2] int sendRetries; //!< number of retries for failed sends [2]
int receiveTimeout; //!< timeout for receiving answer from slave in us [25000] int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
int masterCount; //!< expected number of masters for arbitration [0] int masterCount; //!< expected number of masters for arbitration [0]
bool generateSyn; //!< enable AUTO-SYN symbol generation bool generateSyn; //!< enable AUTO-SYN symbol generation
bool foreground; //!< run in foreground bool foreground; //!< run in foreground
bool enableHex; //!< enable hex command bool enableHex; //!< enable hex command
const char* pidFile; //!< PID file name [/var/run/ebusd.pid] const char* pidFile; //!< PID file name [/var/run/ebusd.pid]
uint16_t port; //!< port to listen for command line connections [8888] uint16_t port; //!< port to listen for command line connections [8888]
bool localOnly; //!< listen on 127.0.0.1 interface only bool localOnly; //!< listen on 127.0.0.1 interface only
uint16_t httpPort; //!< optional port to listen for HTTP connections, 0 to disable [0] uint16_t httpPort; //!< optional port to listen for HTTP connections, 0 to disable [0]
const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html] const char* htmlPath; //!< path for HTML files served by the HTTP port [/var/ebusd/html]
const char* logFile; //!< log file name [/var/log/ebusd.log] const char* logFile; //!< log file name [/var/log/ebusd.log]
bool logRaw; //!< raw log each received/sent byte on the bus bool logRaw; //!< raw log each received/sent byte on the bus
const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log] const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log]
unsigned int logRawSize; //!< maximum size of raw log file in kB [100] unsigned int logRawSize; //!< maximum size of raw log file in kB [100]
bool dump; //!< binary dump received bytes bool dump; //!< binary dump received bytes
const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin] const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin]
unsigned int dumpSize; //!< maximum size of dump file in kB [100] unsigned int dumpSize; //!< maximum size of dump file in kB [100]
}; };
/** /**
+1388 -1388
View File
File diff suppressed because it is too large Load Diff
+174 -174
View File
@@ -36,220 +36,220 @@ namespace ebusd {
* The main loop handling requests from connected clients. * The main loop handling requests from connected clients.
*/ */
class MainLoop : public Thread, DeviceListener { class MainLoop : public Thread, DeviceListener {
public: public:
/** /**
* Construct the main loop and create network and bus handling components. * Construct the main loop and create network and bus handling components.
* @param opt the program options. * @param opt the program options.
* @param device the @a Device instance. * @param device the @a Device instance.
* @param messages the @a MessageMap instance. * @param messages the @a MessageMap instance.
*/ */
MainLoop(const struct options opt, Device *device, MessageMap* messages); MainLoop(const struct options opt, Device *device, MessageMap* messages);
/** /**
* Destructor. * Destructor.
*/ */
~MainLoop(); ~MainLoop();
/** /**
* Get the @a BusHandler instance. * Get the @a BusHandler instance.
* @return the created @a BusHandler instance. * @return the created @a BusHandler instance.
*/ */
BusHandler* getBusHandler() { return m_busHandler; } BusHandler* getBusHandler() { return m_busHandler; }
/** /**
* Add a client @a NetMessage to the queue. * Add a client @a NetMessage to the queue.
* @param message the client @a NetMessage to handle. * @param message the client @a NetMessage to handle.
*/ */
void addMessage(NetMessage* message) { m_netQueue.push(message); } void addMessage(NetMessage* message) { m_netQueue.push(message); }
// @copydoc // @copydoc
virtual void notifyDeviceData(const unsigned char byte, bool received); virtual void notifyDeviceData(const unsigned char byte, bool received);
protected: protected:
// @copydoc // @copydoc
virtual void run(); virtual void run();
private: private:
/** /**
* Decode and execute client message. * Decode and execute client message.
* @param data the data string to decode (may be empty). * @param data the data string to decode (may be empty).
* @param connected set to false when the client connection shall be closed. * @param connected set to false when the client connection shall be closed.
* @param isHttp true for HTTP message. * @param isHttp true for HTTP message.
* @param listening set to true when the client is in listening mode. * @param listening set to true when the client is in listening mode.
* @param reload set to true when the configuration files were reloaded. * @param reload set to true when the configuration files were reloaded.
* @return result string to send back to the client. * @return result string to send back to the client.
*/ */
string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload); string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload);
/** /**
* Parse the hex master message from the remaining arguments. * Parse the hex master message from the remaining arguments.
* @param args the arguments passed to the command. * @param args the arguments passed to the command.
* @param argPos the index of the first argument to parse. * @param argPos the index of the first argument to parse.
* @param master the master @a SymbolString to write the data to. * @param master the master @a SymbolString to write the data to.
* @return the result from parsing the arguments. * @return the result from parsing the arguments.
*/ */
result_t parseHexMaster(vector<string> &args, size_t argPos, SymbolString& master); result_t parseHexMaster(vector<string> &args, size_t argPos, SymbolString& master);
/** /**
* Execute the read command. * Execute the read command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeRead(vector<string> &args); string executeRead(vector<string> &args);
/** /**
* Execute the write command. * Execute the write command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeWrite(vector<string> &args); string executeWrite(vector<string> &args);
/** /**
* Execute the hex command. * Execute the hex command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeHex(vector<string> &args); string executeHex(vector<string> &args);
/** /**
* Execute the find command. * Execute the find command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeFind(vector<string> &args); string executeFind(vector<string> &args);
/** /**
* Execute the listen command. * Execute the listen command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @param listening set to true when the client is in listening mode. * @param listening set to true when the client is in listening mode.
* @return the result string. * @return the result string.
*/ */
string executeListen(vector<string> &args, bool& listening); string executeListen(vector<string> &args, bool& listening);
/** /**
* Execute the state command. * Execute the state command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeState(vector<string> &args); string executeState(vector<string> &args);
/** /**
* Execute the grab command. * Execute the grab command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeGrab(vector<string> &args); string executeGrab(vector<string> &args);
/** /**
* Execute the scan command. * Execute the scan command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeScan(vector<string> &args); string executeScan(vector<string> &args);
/** /**
* Execute the log command. * Execute the log command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeLog(vector<string> &args); string executeLog(vector<string> &args);
/** /**
* Execute the raw command. * Execute the raw command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeRaw(vector<string> &args); string executeRaw(vector<string> &args);
/** /**
* Execute the dump command. * Execute the dump command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeDump(vector<string> &args); string executeDump(vector<string> &args);
/** /**
* Execute the reload command. * Execute the reload command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeReload(vector<string> &args); string executeReload(vector<string> &args);
/** /**
* Execute the info command. * Execute the info command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @return the result string. * @return the result string.
*/ */
string executeInfo(vector<string> &args); string executeInfo(vector<string> &args);
/** /**
* Execute the quit command. * Execute the quit command.
* @param args the arguments passed to the command (starting with the command itself), or empty for help. * @param args the arguments passed to the command (starting with the command itself), or empty for help.
* @param connected set to false when the client connection shall be closed. * @param connected set to false when the client connection shall be closed.
* @return the result string. * @return the result string.
*/ */
string executeQuit(vector<string> &args, bool& connected); string executeQuit(vector<string> &args, bool& connected);
/** /**
* Execute the help command. * Execute the help command.
* @return the result string. * @return the result string.
*/ */
string executeHelp(); string executeHelp();
/** /**
* Execute the HTTP GET command. * Execute the HTTP GET command.
* @param args the arguments passed to the command (starting with the command itself). * @param args the arguments passed to the command (starting with the command itself).
* @param connected set to false when the client connection shall be closed. * @param connected set to false when the client connection shall be closed.
* @return the result string. * @return the result string.
*/ */
string executeGet(vector<string> &args, bool& connected); string executeGet(vector<string> &args, bool& connected);
/** the @a Device instance. */ /** the @a Device instance. */
Device* m_device; Device* m_device;
/** the number of reconnects requested from the @a Device. */ /** the number of reconnects requested from the @a Device. */
unsigned int m_reconnectCount; unsigned int m_reconnectCount;
/** the @a RotateFile for writing sent/received bytes in log format, or NULL. */ /** the @a RotateFile for writing sent/received bytes in log format, or NULL. */
RotateFile* m_logRawFile; RotateFile* m_logRawFile;
/** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */ /** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */
bool m_logRawEnabled; bool m_logRawEnabled;
/** the @a RotateFile for dumping received data, or NULL. */ /** the @a RotateFile for dumping received data, or NULL. */
RotateFile* m_dumpFile; RotateFile* m_dumpFile;
/** the @a MessageMap instance. */ /** the @a MessageMap instance. */
MessageMap* m_messages; MessageMap* m_messages;
/** the own master address for sending on the bus. */ /** the own master address for sending on the bus. */
const unsigned char m_address; const unsigned char m_address;
/** whether to pick configuration files matching initial scan. */ /** whether to pick configuration files matching initial scan. */
const bool m_scanConfig; const bool m_scanConfig;
/** the initial address to scan for @a m_scanConfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */ /** the initial address to scan for @a m_scanConfig (@a ESC=none, 0xfe=broadcast ident, @a SYN=full scan, else: single slave address). */
const unsigned char m_initialScan; const unsigned char m_initialScan;
/** whether to enable the hex command. */ /** whether to enable the hex command. */
const bool m_enableHex; const bool m_enableHex;
/** the created @a BusHandler instance. */ /** the created @a BusHandler instance. */
BusHandler* m_busHandler; BusHandler* m_busHandler;
/** the created @a Network instance. */ /** the created @a Network instance. */
Network* m_network; Network* m_network;
/** the @a NetMessage @a Queue. */ /** the @a NetMessage @a Queue. */
Queue<NetMessage*> m_netQueue; Queue<NetMessage*> m_netQueue;
/** the path for HTML files served by the HTTP port. */ /** the path for HTML files served by the HTTP port. */
string m_htmlPath; string m_htmlPath;
/** the registered @a DataHandler instances. */ /** the registered @a DataHandler instances. */
list<DataHandler*> m_dataHandlers; list<DataHandler*> m_dataHandlers;
}; };
} // namespace ebusd } // namespace ebusd
+337 -337
View File
@@ -17,7 +17,7 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "mqtthandler.h" #include "mqtthandler.h"
@@ -29,12 +29,12 @@ using std::dec;
/** the definition of the MQTT arguments. */ /** the definition of the MQTT arguments. */
static const struct argp_option g_mqtt_argp_options[] = { static const struct argp_option g_mqtt_argp_options[] = {
{NULL, 0, NULL, 0, "MQTT options:", 1 }, {NULL, 0, NULL, 0, "MQTT options:", 1 },
{"mqtthost", 1, "HOST", 0, "Connect to MQTT broker on HOST [localhost]", 0 }, {"mqtthost", 1, "HOST", 0, "Connect to MQTT broker on HOST [localhost]", 0 },
{"mqttport", 2, "PORT", 0, "Connect to MQTT broker on PORT (usually 1883), 0 to disable [0]", 0 }, {"mqttport", 2, "PORT", 0, "Connect to MQTT broker on PORT (usually 1883), 0 to disable [0]", 0 },
{"mqtttopic", 3, "TOPIC", 0, "Use MQTT TOPIC (prefix before /%circuit/%name or complete format) [ebusd]", 0 }, {"mqtttopic", 3, "TOPIC", 0, "Use MQTT TOPIC (prefix before /%circuit/%name or complete format) [ebusd]", 0 },
{NULL, 0, NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0, NULL, 0 },
}; };
static const char* g_host = "localhost"; //!< MQTT Host to use [localhost] static const char* g_host = "localhost"; //!< MQTT Host to use [localhost]
@@ -49,61 +49,61 @@ static const char* g_topic = PACKAGE; //!< MQTT topic to use (prefix if without
* @param state the parsing state. * @param state the parsing state.
*/ */
static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) { static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
result_t result = RESULT_OK; result_t result = RESULT_OK;
switch (key) { switch (key) {
case 1: // --mqtthost=localhost case 1: // --mqtthost=localhost
if (arg == NULL || arg[0] == 0) { if (arg == NULL || arg[0] == 0) {
argp_error(state, "invalid mqtthost"); argp_error(state, "invalid mqtthost");
return EINVAL; return EINVAL;
} }
g_host = arg; g_host = arg;
break; break;
case 2: // --mqttport=1883 case 2: // --mqttport=1883
g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result); g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result);
if (result != RESULT_OK) { if (result != RESULT_OK) {
argp_error(state, "invalid mqttport"); argp_error(state, "invalid mqttport");
return EINVAL; return EINVAL;
} }
break; break;
case 3: // --mqtttopic=ebusd case 3: // --mqtttopic=ebusd
if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') { if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') {
argp_error(state, "invalid mqtttopic"); argp_error(state, "invalid mqtttopic");
return EINVAL; return EINVAL;
} }
g_topic = arg; g_topic = arg;
break; break;
default: default:
return ARGP_ERR_UNKNOWN; return ARGP_ERR_UNKNOWN;
} }
return 0; return 0;
} }
static const struct argp g_mqtt_argp = { g_mqtt_argp_options, mqtt_parse_opt, NULL, NULL, NULL, NULL, NULL }; static const struct argp g_mqtt_argp = { g_mqtt_argp_options, mqtt_parse_opt, NULL, NULL, NULL, NULL, NULL };
static const struct argp_child g_mqtt_argp_child = {&g_mqtt_argp, 0, "", 1}; static const struct argp_child g_mqtt_argp_child = {&g_mqtt_argp, 0, "", 1};
const struct argp_child* mqtthandler_getargs() { const struct argp_child* mqtthandler_getargs() {
return &g_mqtt_argp_child; return &g_mqtt_argp_child;
} }
DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages) { DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages) {
return new MqttHandler(busHandler, messages); return new MqttHandler(busHandler, messages);
} }
/** the known topic column names. */ /** the known topic column names. */
static const char* columnNames[] = { static const char* columnNames[] = {
"circuit", "circuit",
"name", "name",
"field", "field",
}; };
/** the known topic column IDs. */ /** the known topic column IDs. */
static const size_t columnIds[] = { static const size_t columnIds[] = {
COLUMN_CIRCUIT, COLUMN_CIRCUIT,
COLUMN_NAME, COLUMN_NAME,
COLUMN_FIELDS, COLUMN_FIELDS,
}; };
/** the number of known column names. */ /** the number of known column names. */
@@ -118,344 +118,344 @@ static const size_t columnCount = sizeof(columnNames) / sizeof(char*);
* @return true on success, false on malformed topic template. * @return true on success, false on malformed topic template.
*/ */
bool parseTopic(const string topic, vector<string> &strs, vector<size_t> &cols) { bool parseTopic(const string topic, vector<string> &strs, vector<size_t> &cols) {
size_t lastpos = 0; size_t lastpos = 0;
size_t end = topic.length(); size_t end = topic.length();
vector<string> columns; vector<string> columns;
for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) { for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) {
size_t col = columnCount; size_t col = columnCount;
size_t len = 0; size_t len = 0;
for (size_t i = 0; i < columnCount; i++) { for (size_t i = 0; i < columnCount; i++) {
len = strlen(columnNames[i]); len = strlen(columnNames[i]);
if (topic.substr(pos+1, len) == columnNames[i]) { if (topic.substr(pos+1, len) == columnNames[i]) {
col = columnIds[i]; col = columnIds[i];
break; break;
} }
} }
if (col == columnCount) { if (col == columnCount) {
return false; return false;
} }
for (vector<size_t>::iterator it=cols.begin(); it != cols.end(); it++) { for (vector<size_t>::iterator it=cols.begin(); it != cols.end(); it++) {
if (*it == col) { if (*it == col) {
return false; // duplicate column return false; // duplicate column
} }
} }
strs.push_back(topic.substr(lastpos, pos-lastpos)); strs.push_back(topic.substr(lastpos, pos-lastpos));
cols.push_back(col); cols.push_back(col);
lastpos = pos+1+len; lastpos = pos+1+len;
pos = topic.find('%', lastpos); pos = topic.find('%', lastpos);
} }
if (lastpos < end) { if (lastpos < end) {
strs.push_back(topic.substr(lastpos, end-lastpos)); strs.push_back(topic.substr(lastpos, end-lastpos));
} }
return true; return true;
} }
MqttHandler::MqttHandler(BusHandler* busHandler, MessageMap* messages) MqttHandler::MqttHandler(BusHandler* busHandler, MessageMap* messages)
: DataSink(), DataSource(busHandler), Thread(), m_messages(messages) { : DataSink(), DataSource(busHandler), Thread(), m_messages(messages) {
bool enabled = g_port != 0; bool enabled = g_port != 0;
m_publishByField = false; m_publishByField = false;
m_mosquitto = NULL; m_mosquitto = NULL;
if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) { if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) {
logOtherError("mqtt", "malformed topic %s", g_topic); logOtherError("mqtt", "malformed topic %s", g_topic);
return; return;
} }
if (!enabled) { if (!enabled) {
return; return;
} }
int major = -1; int major = -1;
mosquitto_lib_version(&major, NULL, NULL); mosquitto_lib_version(&major, NULL, NULL);
if (major != LIBMOSQUITTO_MAJOR) { if (major != LIBMOSQUITTO_MAJOR) {
logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR); logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR);
return; return;
} }
if (m_topicCols.empty()) { if (m_topicCols.empty()) {
if (m_topicStrs.empty()) { if (m_topicStrs.empty()) {
m_topicStrs.push_back(""); m_topicStrs.push_back("");
} else { } else {
string str = m_topicStrs[0]; string str = m_topicStrs[0];
if (str.empty() || str[str.length()-1] != '/') { if (str.empty() || str[str.length()-1] != '/') {
m_topicStrs[0] = str+"/"; m_topicStrs[0] = str+"/";
} }
} }
m_topicCols.push_back(COLUMN_CIRCUIT); // circuit m_topicCols.push_back(COLUMN_CIRCUIT); // circuit
m_topicStrs.push_back("/"); m_topicStrs.push_back("/");
m_topicCols.push_back(COLUMN_NAME); // name m_topicCols.push_back(COLUMN_NAME); // name
} else { } else {
for (size_t i = 0; i < m_topicCols.size(); i++) { for (size_t i = 0; i < m_topicCols.size(); i++) {
if (m_topicCols[i] == COLUMN_FIELDS) { // fields if (m_topicCols[i] == COLUMN_FIELDS) { // fields
m_publishByField = true; m_publishByField = true;
break; break;
} }
} }
} }
m_globalTopic = getTopic(NULL)+"global/"; m_globalTopic = getTopic(NULL)+"global/";
m_mosquitto = NULL; m_mosquitto = NULL;
if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) { if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) {
logOtherError("mqtt", "unable to initialize"); logOtherError("mqtt", "unable to initialize");
} else { } else {
string clientId = PACKAGE_STRING; string clientId = PACKAGE_STRING;
clientId += " "+static_cast<unsigned>(getpid()); clientId += " "+static_cast<unsigned>(getpid());
m_mosquitto = mosquitto_new(clientId.c_str(), m_mosquitto = mosquitto_new(clientId.c_str(),
#if (LIBMOSQUITTO_MAJOR >= 1) #if (LIBMOSQUITTO_MAJOR >= 1)
true, true,
#endif #endif
this); this);
if (!m_mosquitto) { if (!m_mosquitto) {
logOtherError("mqtt", "unable to instantiate"); logOtherError("mqtt", "unable to instantiate");
} }
} }
if (m_mosquitto) { if (m_mosquitto) {
/*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING /*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING
| MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/ | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/
string willTopic = m_globalTopic+"running"; string willTopic = m_globalTopic+"running";
string willData = "false"; string willData = "false";
size_t len = willData.length(); size_t len = willData.length();
mosquitto_will_set(m_mosquitto, mosquitto_will_set(m_mosquitto,
#if (LIBMOSQUITTO_MAJOR < 1) #if (LIBMOSQUITTO_MAJOR < 1)
true, true,
#endif #endif
willTopic.c_str(), (uint32_t)len, reinterpret_cast<const uint8_t*>(willData.c_str()), 0, true); willTopic.c_str(), (uint32_t)len, reinterpret_cast<const uint8_t*>(willData.c_str()), 0, true);
if (mosquitto_connect(m_mosquitto, g_host, g_port, 60 if (mosquitto_connect(m_mosquitto, g_host, g_port, 60
#if (LIBMOSQUITTO_MAJOR < 1) #if (LIBMOSQUITTO_MAJOR < 1)
, true , true
#endif #endif
) != MOSQ_ERR_SUCCESS) { ) != MOSQ_ERR_SUCCESS) {
logOtherError("mqtt", "unable to connect"); logOtherError("mqtt", "unable to connect");
mosquitto_destroy(m_mosquitto); mosquitto_destroy(m_mosquitto);
m_mosquitto = NULL; m_mosquitto = NULL;
} else { } else {
logOtherNotice("mqtt", "connection established"); logOtherNotice("mqtt", "connection established");
} }
} }
} }
MqttHandler::~MqttHandler() { MqttHandler::~MqttHandler() {
join(); join();
if (m_mosquitto) { if (m_mosquitto) {
mosquitto_destroy(m_mosquitto); mosquitto_destroy(m_mosquitto);
m_mosquitto = NULL; m_mosquitto = NULL;
} }
mosquitto_lib_cleanup(); mosquitto_lib_cleanup();
} }
void MqttHandler::start() { void MqttHandler::start() {
if (m_mosquitto) { if (m_mosquitto) {
Thread::start("MQTT"); Thread::start("MQTT");
} }
} }
void on_message( void on_message(
#if (LIBMOSQUITTO_MAJOR >= 1) #if (LIBMOSQUITTO_MAJOR >= 1)
struct mosquitto *mosq, struct mosquitto *mosq,
#endif #endif
void *obj, const struct mosquitto_message *message) { void *obj, const struct mosquitto_message *message) {
MqttHandler* handler = reinterpret_cast<MqttHandler*>(obj); MqttHandler* handler = reinterpret_cast<MqttHandler*>(obj);
if (!handler || !message || !handler->isRunning()) { if (!handler || !message || !handler->isRunning()) {
return; return;
} }
string topic(message->topic); string topic(message->topic);
string data(message->payloadlen > 0 ? reinterpret_cast<char*>(message->payload) : ""); string data(message->payloadlen > 0 ? reinterpret_cast<char*>(message->payload) : "");
handler->notifyTopic(topic, data); handler->notifyTopic(topic, data);
} }
void MqttHandler::notifyTopic(string topic, string data) { void MqttHandler::notifyTopic(string topic, string data) {
size_t pos = topic.rfind('/'); size_t pos = topic.rfind('/');
if (pos == string::npos) { if (pos == string::npos) {
return; return;
} }
string suffix = topic.substr(pos+1); string suffix = topic.substr(pos+1);
bool isWrite = false; bool isWrite = false;
if (suffix.empty()) { if (suffix.empty()) {
return; return;
} }
string direction = suffix.substr(0, 3); string direction = suffix.substr(0, 3);
isWrite = direction == "set"; isWrite = direction == "set";
if (!isWrite && direction != "get") { if (!isWrite && direction != "get") {
return; return;
} }
suffix = suffix.substr(3); // security level suffix = suffix.substr(3); // security level
logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str()); logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str());
string remain = topic.substr(0, pos); string remain = topic.substr(0, pos);
size_t last = 0; size_t last = 0;
string circuit, name; string circuit, name;
size_t idx; size_t idx;
for (idx = 0; idx < m_topicStrs.size()+1; idx++) { for (idx = 0; idx < m_topicStrs.size()+1; idx++) {
string field; string field;
string chk; string chk;
if (idx < m_topicStrs.size()) { if (idx < m_topicStrs.size()) {
chk = m_topicStrs[idx]; chk = m_topicStrs[idx];
pos = remain.find(chk, last); pos = remain.find(chk, last);
if (pos == string::npos) { if (pos == string::npos) {
return; return;
} }
} else if (idx-1 < m_topicCols.size()) { } else if (idx-1 < m_topicCols.size()) {
pos = remain.size(); pos = remain.size();
} else if (last < remain.size()) { } else if (last < remain.size()) {
return; return;
} else { } else {
break; break;
} }
field = remain.substr(last, pos-last); field = remain.substr(last, pos-last);
last = pos+chk.size(); last = pos+chk.size();
if (idx == 0) { if (idx == 0) {
if (pos > 0) { if (pos > 0) {
return; return;
} }
} else { } else {
if (field.empty()) { if (field.empty()) {
return; return;
} }
switch (m_topicCols[idx-1]) { switch (m_topicCols[idx-1]) {
case COLUMN_CIRCUIT: case COLUMN_CIRCUIT:
circuit = field; circuit = field;
break; break;
case COLUMN_NAME: case COLUMN_NAME:
name = field; name = field;
break; break;
case COLUMN_FIELDS: case COLUMN_FIELDS:
//field = field; // TODO add support for writing a single field //field = field; // TODO add support for writing a single field
break; break;
default: default:
return; return;
} }
} }
} }
if (circuit.empty() || name.empty()) { if (circuit.empty() || name.empty()) {
return; return;
} }
logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str()); logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str());
if (suffix.length() > 0) { if (suffix.length() > 0) {
circuit += "#"+suffix; circuit += "#"+suffix;
} }
Message* message = m_messages->find(circuit, name, isWrite); Message* message = m_messages->find(circuit, name, isWrite);
if (message == NULL) { if (message == NULL) {
message = m_messages->find(circuit, name, isWrite, true); message = m_messages->find(circuit, name, isWrite, true);
} }
if (message == NULL) { if (message == NULL) {
logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str()); logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str());
return; return;
} }
if (!message->isPassive()) { if (!message->isPassive()) {
result_t result = m_busHandler->readFromBus(message, data); result_t result = m_busHandler->readFromBus(message, data);
if (result != RESULT_OK) { if (result != RESULT_OK) {
logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result)); logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result));
return; return;
} }
logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str()); logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str());
} }
ostringstream ostream; ostringstream ostream;
publishMessage(message, ostream); publishMessage(message, ostream);
} }
void MqttHandler::run() { void MqttHandler::run() {
time_t lastTaskRun, now, start, lastSignal = 0; time_t lastTaskRun, now, start, lastSignal = 0;
bool signal = false; bool signal = false;
string signalTopic = m_globalTopic+"signal"; string signalTopic = m_globalTopic+"signal";
string uptimeTopic = m_globalTopic+"uptime"; string uptimeTopic = m_globalTopic+"uptime";
ostringstream updates; ostringstream updates;
time(&now); time(&now);
start = lastTaskRun = now; start = lastTaskRun = now;
publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION); publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION);
publishTopic(m_globalTopic+"running", "true"); publishTopic(m_globalTopic+"running", "true");
publishTopic(signalTopic, "false"); publishTopic(signalTopic, "false");
mosquitto_message_callback_set(m_mosquitto, on_message); mosquitto_message_callback_set(m_mosquitto, on_message);
string subTopic = getTopic(NULL)+"#"; string subTopic = getTopic(NULL)+"#";
mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0); mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0);
while (isRunning()) { while (isRunning()) {
handleTraffic(); handleTraffic();
time(&now); time(&now);
if (now < start) { if (now < start) {
// clock skew // clock skew
if (now < lastSignal) { if (now < lastSignal) {
lastSignal -= lastTaskRun-now; lastSignal -= lastTaskRun-now;
} }
lastTaskRun = now; lastTaskRun = now;
} else if (now > lastTaskRun+15) { } else if (now > lastTaskRun+15) {
if (m_busHandler->hasSignal()) { if (m_busHandler->hasSignal()) {
lastSignal = now; lastSignal = now;
if (!signal) { if (!signal) {
signal = true; signal = true;
publishTopic(signalTopic, "true"); publishTopic(signalTopic, "true");
} }
} else { } else {
if (signal) { if (signal) {
signal = false; signal = false;
publishTopic(signalTopic, "false"); publishTopic(signalTopic, "false");
} }
} }
time_t uptime = now-start; time_t uptime = now-start;
updates.str(""); updates.str("");
updates.clear(); updates.clear();
updates << dec << static_cast<unsigned>(uptime); updates << dec << static_cast<unsigned>(uptime);
publishTopic(uptimeTopic, updates.str()); publishTopic(uptimeTopic, updates.str());
time(&lastTaskRun); time(&lastTaskRun);
} }
if (!m_updatedMessages.empty()) { if (!m_updatedMessages.empty()) {
for (map<Message*, int>::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) { for (map<Message*, int>::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) {
Message* message = it->first; Message* message = it->first;
updates.str(""); updates.str("");
updates.clear(); updates.clear();
updates << dec; updates << dec;
publishMessage(message, updates); publishMessage(message, updates);
} }
m_updatedMessages.clear(); m_updatedMessages.clear();
} }
} }
} }
void MqttHandler::handleTraffic() { void MqttHandler::handleTraffic() {
if (m_mosquitto) { if (m_mosquitto) {
mosquitto_loop(m_mosquitto, -1 mosquitto_loop(m_mosquitto, -1
#if (LIBMOSQUITTO_MAJOR >= 1) #if (LIBMOSQUITTO_MAJOR >= 1)
, 1 , 1
#endif #endif
); );
} }
} }
string MqttHandler::getTopic(Message* message, signed char fieldIndex) { string MqttHandler::getTopic(Message* message, signed char fieldIndex) {
ostringstream ret; ostringstream ret;
for (size_t i = 0; i < m_topicStrs.size(); i++) { for (size_t i = 0; i < m_topicStrs.size(); i++) {
ret << m_topicStrs[i]; ret << m_topicStrs[i];
if (!message) { if (!message) {
break; break;
} }
if (i < m_topicCols.size()) { if (i < m_topicCols.size()) {
if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) { if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) {
ret << message->getFieldName(fieldIndex); ret << message->getFieldName(fieldIndex);
} else { } else {
message->dumpColumn(ret, m_topicCols[i]); message->dumpColumn(ret, m_topicCols[i]);
} }
} }
} }
return ret.str(); return ret.str();
} }
void MqttHandler::publishMessage(Message* message, ostringstream& updates) { void MqttHandler::publishMessage(Message* message, ostringstream& updates) {
result_t result = message->decodeLastData(updates); result_t result = message->decodeLastData(updates);
if (result != RESULT_OK) { if (result != RESULT_OK) {
logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result)); logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result));
return; return;
} }
if (m_publishByField) { if (m_publishByField) {
signed char index = 0; signed char index = 0;
istringstream input(updates.str()); istringstream input(updates.str());
string token; string token;
while (getline(input, token, UI_FIELD_SEPARATOR)) { while (getline(input, token, UI_FIELD_SEPARATOR)) {
string topic = getTopic(message, index); string topic = getTopic(message, index);
publishTopic(topic, token); publishTopic(topic, token);
index++; index++;
} }
} else { } else {
publishTopic(getTopic(message), updates.str()); publishTopic(getTopic(message), updates.str());
} }
} }
void MqttHandler::publishTopic(string topic, string data, bool retain) { void MqttHandler::publishTopic(string topic, string data, bool retain) {
logOtherDebug("mqtt", "publish %s %s", topic.c_str(), data.c_str()); logOtherDebug("mqtt", "publish %s %s", topic.c_str(), data.c_str());
mosquitto_publish(m_mosquitto, NULL, topic.c_str(), (uint32_t)data.size(), reinterpret_cast<const uint8_t*>(data.c_str()), 0, retain); mosquitto_publish(m_mosquitto, NULL, topic.c_str(), (uint32_t)data.size(), reinterpret_cast<const uint8_t*>(data.c_str()), 0, retain);
} }
} // namespace ebusd } // namespace ebusd
+59 -59
View File
@@ -55,81 +55,81 @@ DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages);
* The main class supporting MQTT data handling. * The main class supporting MQTT data handling.
*/ */
class MqttHandler : public DataSink, public DataSource, public Thread { class MqttHandler : public DataSink, public DataSource, public Thread {
public: public:
/** /**
* Constructor. * Constructor.
* @param busHandler the @a BusHandler instance. * @param busHandler the @a BusHandler instance.
* @param messages the @a MessageMap instance. * @param messages the @a MessageMap instance.
*/ */
MqttHandler(BusHandler* busHandler, MessageMap* messages); MqttHandler(BusHandler* busHandler, MessageMap* messages);
/** /**
* Destructor. * Destructor.
*/ */
virtual ~MqttHandler(); virtual ~MqttHandler();
// @copydoc // @copydoc
virtual void start(); virtual void start();
/** /**
* Notify the handler of a received MQTT message. * Notify the handler of a received MQTT message.
* @param topic the topic string. * @param topic the topic string.
* @param data the data string. * @param data the data string.
*/ */
void notifyTopic(string topic, string data); void notifyTopic(string topic, string data);
protected: protected:
// @copydoc // @copydoc
virtual void run(); virtual void run();
private: private:
/** /**
* Called regularly to handle MQTT traffic. * Called regularly to handle MQTT traffic.
*/ */
void handleTraffic(); void handleTraffic();
/** /**
* Build the MQTT topic string for the @a Message. * Build the MQTT topic string for the @a Message.
* @param message the @a Message to build the topic string for. * @param message the @a Message to build the topic string for.
* @param fieldIndex the optional field index for the field column, or -1. * @param fieldIndex the optional field index for the field column, or -1.
* @return the topic string. * @return the topic string.
*/ */
string getTopic(Message* message, signed char fieldIndex = -1); string getTopic(Message* message, signed char fieldIndex = -1);
/** /**
* Prepare a @a Message and publish as topic. * Prepare a @a Message and publish as topic.
* @param message the @a Message to publish. * @param message the @a Message to publish.
* @param updates the @a ostringstream for preparation. * @param updates the @a ostringstream for preparation.
*/ */
void publishMessage(Message* message, ostringstream& updates); void publishMessage(Message* message, ostringstream& updates);
/** /**
* Publish a topic update to MQTT. * Publish a topic update to MQTT.
* @param topic the topic string. * @param topic the topic string.
* @param data the data string. * @param data the data string.
* @param retain whether the topic shall be retained. * @param retain whether the topic shall be retained.
*/ */
void publishTopic(string topic, string data, bool retain = true); void publishTopic(string topic, string data, bool retain = true);
/** the @a MessageMap instance. */ /** the @a MessageMap instance. */
MessageMap* m_messages; MessageMap* m_messages;
/** the MQTT topic string parts. */ /** the MQTT topic string parts. */
vector<string> m_topicStrs; vector<string> m_topicStrs;
/** the MQTT topic column parts. */ /** the MQTT topic column parts. */
vector<size_t> m_topicCols; vector<size_t> m_topicCols;
/** the global topic prefix. */ /** the global topic prefix. */
string m_globalTopic; string m_globalTopic;
/** whether to publish a separate topic for each message field. */ /** whether to publish a separate topic for each message field. */
bool m_publishByField; bool m_publishByField;
/** the mosquitto structure if initialized, or NULL. */ /** the mosquitto structure if initialized, or NULL. */
struct mosquitto* m_mosquitto; struct mosquitto* m_mosquitto;
}; };
} // namespace ebusd } // namespace ebusd
+198 -198
View File
@@ -17,12 +17,12 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "network.h" #include "network.h"
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
# include <poll.h> # include <poll.h>
#endif #endif
#include <cstring> #include <cstring>
#include "log.h" #include "log.h"
@@ -32,271 +32,271 @@ namespace ebusd {
int Connection::m_ids = 0; int Connection::m_ids = 0;
void Connection::run() { void Connection::run() {
int ret; int ret;
struct timespec tdiff; struct timespec tdiff;
// set timeout // set timeout
tdiff.tv_sec = 2; tdiff.tv_sec = 2;
tdiff.tv_nsec = 0; tdiff.tv_nsec = 0;
int notifyFD = m_notify.notifyFD(); int notifyFD = m_notify.notifyFD();
int sockFD = m_socket->getFD(); int sockFD = m_socket->getFD();
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
int nfds = 2; int nfds = 2;
struct pollfd fds[nfds]; struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds)); memset(fds, 0, sizeof(fds));
fds[0].fd = notifyFD; fds[0].fd = notifyFD;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
fds[1].fd = sockFD; fds[1].fd = sockFD;
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD; int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD;
fd_set checkfds, exceptfds; fd_set checkfds, exceptfds;
FD_ZERO(&checkfds); FD_ZERO(&checkfds);
FD_SET(notifyFD, &checkfds); FD_SET(notifyFD, &checkfds);
FD_SET(sockFD, &checkfds); FD_SET(sockFD, &checkfds);
FD_ZERO(&exceptfds); FD_ZERO(&exceptfds);
FD_SET(notifyFD, &exceptfds); FD_SET(notifyFD, &exceptfds);
FD_SET(sockFD, &exceptfds); FD_SET(sockFD, &exceptfds);
#endif #endif
#endif #endif
bool closed = false; bool closed = false;
NetMessage message(m_isHttp); NetMessage message(m_isHttp);
while (!closed) { while (!closed) {
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// wait for new fd event // wait for new fd event
ret = ppoll(fds, nfds, &tdiff, NULL); ret = ppoll(fds, nfds, &tdiff, NULL);
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// set readfds to inital checkfds // set readfds to inital checkfds
fd_set readfds = checkfds; fd_set readfds = checkfds;
// wait for new fd event // wait for new fd event
ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL); ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL);
#endif #endif
#endif #endif
bool newData = false; bool newData = false;
if (ret != 0) { if (ret != 0) {
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// new data from notify // new data from notify
if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) { if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) {
break; break;
} }
// new data from socket // new data from socket
newData = fds[1].revents & POLLIN; newData = fds[1].revents & POLLIN;
closed = fds[1].revents & POLLRDHUP; closed = fds[1].revents & POLLRDHUP;
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// new data from notify // new data from notify
if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) { if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) {
break; break;
} }
// new data from socket // new data from socket
newData = FD_ISSET(sockFD, &readfds); newData = FD_ISSET(sockFD, &readfds);
closed = FD_ISSET(sockFD, &exceptfds); closed = FD_ISSET(sockFD, &exceptfds);
#endif #endif
#endif #endif
} }
if (newData || message.isListening()) { if (newData || message.isListening()) {
char data[256]; char data[256];
if (!m_socket->isValid()) { if (!m_socket->isValid()) {
break; break;
} }
if (newData) { if (newData) {
size_t datalen = m_socket->recv(data, sizeof(data)-1); size_t datalen = m_socket->recv(data, sizeof(data)-1);
// remove closed socket // remove closed socket
if (datalen <= 0) { if (datalen <= 0) {
break; break;
} }
data[datalen] = '\0'; data[datalen] = '\0';
} else { } else {
data[0] = '\0'; data[0] = '\0';
} }
// decode client data // decode client data
if (message.add(data)) { if (message.add(data)) {
m_netQueue->push(&message); m_netQueue->push(&message);
// wait for result // wait for result
logDebug(lf_network, "[%05d] wait for result", getID()); logDebug(lf_network, "[%05d] wait for result", getID());
string result = message.getResult(); string result = message.getResult();
if (!m_socket->isValid()) { if (!m_socket->isValid()) {
break; break;
} }
m_socket->send(result.c_str(), result.size()); m_socket->send(result.c_str(), result.size());
} }
if (message.isDisconnect() || !m_socket->isValid()) { if (message.isDisconnect() || !m_socket->isValid()) {
break; break;
} }
} }
} }
delete m_socket; delete m_socket;
m_socket = NULL; m_socket = NULL;
logInfo(lf_network, "[%05d] connection closed", getID()); logInfo(lf_network, "[%05d] connection closed", getID());
} }
Network::Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue<NetMessage*>* netQueue) Network::Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue<NetMessage*>* netQueue)
: Thread(), m_netQueue(netQueue), m_listening(false) { : Thread(), m_netQueue(netQueue), m_listening(false) {
m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0"); m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0");
if (m_tcpServer != NULL && m_tcpServer->start() == 0) { if (m_tcpServer != NULL && m_tcpServer->start() == 0) {
m_listening = true; m_listening = true;
} }
if (httpPort > 0) { if (httpPort > 0) {
m_httpServer = new TCPServer(httpPort, "0.0.0.0"); m_httpServer = new TCPServer(httpPort, "0.0.0.0");
m_httpServer->start(); m_httpServer->start();
} else { } else {
m_httpServer = NULL; m_httpServer = NULL;
} }
} }
Network::~Network() { Network::~Network() {
stop(); stop();
while (!m_connections.empty()) { while (!m_connections.empty()) {
Connection* connection = m_connections.back(); Connection* connection = m_connections.back();
m_connections.pop_back(); m_connections.pop_back();
connection->stop(); connection->stop();
connection->join(); connection->join();
delete connection; delete connection;
} }
if (m_tcpServer != NULL) { if (m_tcpServer != NULL) {
delete m_tcpServer; delete m_tcpServer;
} }
if (m_httpServer != NULL) { if (m_httpServer != NULL) {
delete m_httpServer; delete m_httpServer;
} }
join(); join();
} }
void Network::run() { void Network::run() {
if (!m_listening) { if (!m_listening) {
return; return;
} }
int ret; int ret;
struct timespec tdiff; struct timespec tdiff;
// set timeout // set timeout
tdiff.tv_sec = 1; tdiff.tv_sec = 1;
tdiff.tv_nsec = 0; tdiff.tv_nsec = 0;
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
int socketCount = m_httpServer ? 2 : 1; int socketCount = m_httpServer ? 2 : 1;
int nfds = 1+socketCount; int nfds = 1+socketCount;
struct pollfd fds[nfds]; struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds)); memset(fds, 0, sizeof(fds));
fds[0].fd = m_notify.notifyFD(); fds[0].fd = m_notify.notifyFD();
fds[0].events = POLLIN; fds[0].events = POLLIN;
fds[1].fd = m_tcpServer->getFD(); fds[1].fd = m_tcpServer->getFD();
fds[1].events = POLLIN; fds[1].events = POLLIN;
if (m_httpServer) { if (m_httpServer) {
fds[2].fd = m_httpServer->getFD(); fds[2].fd = m_httpServer->getFD();
fds[2].events = POLLIN; fds[2].events = POLLIN;
} }
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
int maxfd; int maxfd;
fd_set checkfds; fd_set checkfds;
FD_ZERO(&checkfds); FD_ZERO(&checkfds);
FD_SET(m_notify.notifyFD(), &checkfds); FD_SET(m_notify.notifyFD(), &checkfds);
FD_SET(m_tcpServer->getFD(), &checkfds); FD_SET(m_tcpServer->getFD(), &checkfds);
if (m_httpServer) { if (m_httpServer) {
FD_SET(m_httpServer->getFD(), &checkfds); FD_SET(m_httpServer->getFD(), &checkfds);
} }
maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ? maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ?
m_notify.notifyFD() : m_tcpServer->getFD(); m_notify.notifyFD() : m_tcpServer->getFD();
if (m_httpServer && m_httpServer->getFD() > maxfd) { if (m_httpServer && m_httpServer->getFD() > maxfd) {
maxfd = m_httpServer->getFD(); maxfd = m_httpServer->getFD();
} }
#endif #endif
#endif #endif
while (true) { while (true) {
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// wait for new fd event // wait for new fd event
ret = ppoll(fds, nfds, &tdiff, NULL); ret = ppoll(fds, nfds, &tdiff, NULL);
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// set readfds to inital checkfds // set readfds to inital checkfds
fd_set readfds = checkfds; fd_set readfds = checkfds;
// wait for new fd event // wait for new fd event
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
#endif #endif
#endif #endif
if (ret == 0) { if (ret == 0) {
cleanConnections(); cleanConnections();
continue; continue;
} }
bool newData = false, isHttp = false; bool newData = false, isHttp = false;
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// new data from notify // new data from notify
if (fds[0].revents & POLLIN) { if (fds[0].revents & POLLIN) {
return; return;
} }
// new data from socket // new data from socket
if (fds[1].revents & POLLIN) { if (fds[1].revents & POLLIN) {
newData = true; newData = true;
} else if (m_httpServer && fds[2].revents & POLLIN) { } else if (m_httpServer && fds[2].revents & POLLIN) {
newData = isHttp = true; newData = isHttp = true;
} }
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// new data from notify // new data from notify
if (FD_ISSET(m_notify.notifyFD(), &readfds)) { if (FD_ISSET(m_notify.notifyFD(), &readfds)) {
return; return;
} }
// new data from socket // new data from socket
if (FD_ISSET(m_tcpServer->getFD(), &readfds)) { if (FD_ISSET(m_tcpServer->getFD(), &readfds)) {
newData = true; newData = true;
} else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) { } else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) {
newData = isHttp = true; newData = isHttp = true;
} }
#endif #endif
#endif #endif
if (newData) { if (newData) {
TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket(); TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket();
if (socket == NULL) { if (socket == NULL) {
continue; continue;
} }
Connection* connection = new Connection(socket, isHttp, m_netQueue); Connection* connection = new Connection(socket, isHttp, m_netQueue);
if (connection == NULL) { if (connection == NULL) {
continue; continue;
} }
connection->start("connection"); connection->start("connection");
m_connections.push_back(connection); m_connections.push_back(connection);
logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str()); logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str());
} }
} }
} }
void Network::cleanConnections() { void Network::cleanConnections() {
list<Connection*>::iterator c_it; list<Connection*>::iterator c_it;
for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) { for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) {
if (!(*c_it)->isRunning()) { if (!(*c_it)->isRunning()) {
Connection* connection = *c_it; Connection* connection = *c_it;
c_it = m_connections.erase(c_it); c_it = m_connections.erase(c_it);
delete connection; delete connection;
logDebug(lf_network, "dead connection removed - %d", m_connections.size()); logDebug(lf_network, "dead connection removed - %d", m_connections.size());
} }
} }
} }
} // namespace ebusd } // namespace ebusd
+208 -208
View File
@@ -39,271 +39,271 @@ class Connection;
* Class for data/message transfer between @a Connection and @a MainLoop. * Class for data/message transfer between @a Connection and @a MainLoop.
*/ */
class NetMessage { class NetMessage {
public: public:
/** /**
* Constructor. * Constructor.
* @param isHttp whether this is a HTTP message. * @param isHttp whether this is a HTTP message.
*/ */
explicit NetMessage(const bool isHttp) explicit NetMessage(const bool isHttp)
: m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) { : m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) {
pthread_mutex_init(&m_mutex, NULL); pthread_mutex_init(&m_mutex, NULL);
pthread_cond_init(&m_cond, NULL); pthread_cond_init(&m_cond, NULL);
} }
/** /**
* Destructor. * Destructor.
*/ */
~NetMessage() { ~NetMessage() {
pthread_mutex_destroy(&m_mutex); pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_cond); pthread_cond_destroy(&m_cond);
} }
private: private:
/** /**
* Hidden copy constructor. * Hidden copy constructor.
* @param src the object to copy from. * @param src the object to copy from.
*/ */
NetMessage(const NetMessage& src); NetMessage(const NetMessage& src);
public: public:
/** /**
* Add request data received from the client. * Add request data received from the client.
* @param request the request data from the client. * @param request the request data from the client.
* @return true when the request is complete and the response shall be prepared. * @return true when the request is complete and the response shall be prepared.
*/ */
bool add(string request) { bool add(string request) {
if (request.length() > 0) { if (request.length() > 0) {
request.erase(remove(request.begin(), request.end(), '\r'), request.end()); request.erase(remove(request.begin(), request.end(), '\r'), request.end());
m_request.append(request); m_request.append(request);
} }
size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n"); size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n");
if (pos != string::npos) { if (pos != string::npos) {
if (m_isHttp) { if (m_isHttp) {
pos = m_request.find("\n"); pos = m_request.find("\n");
m_request.resize(pos); // reduce to first line m_request.resize(pos); // reduce to first line
// typical first line: GET /ehp/outsidetemp HTTP/1.1 // typical first line: GET /ehp/outsidetemp HTTP/1.1
pos = m_request.rfind(" HTTP/"); pos = m_request.rfind(" HTTP/");
if (pos != string::npos) { if (pos != string::npos) {
m_request.resize(pos); // remove "HTTP/x.x" suffix m_request.resize(pos); // remove "HTTP/x.x" suffix
} }
pos = 0; pos = 0;
while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) { while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) {
unsigned int value1, value2; unsigned int value1, value2;
if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) { if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) {
break; break;
} }
m_request[pos] = static_cast<char>(((value1&0x0f) << 4) | (value2&0x0f)); m_request[pos] = static_cast<char>(((value1&0x0f) << 4) | (value2&0x0f));
m_request.erase(pos+1, 2); m_request.erase(pos+1, 2);
} }
} else if (pos+1 == m_request.length()) { } else if (pos+1 == m_request.length()) {
m_request.resize(pos); // reduce to complete lines m_request.resize(pos); // reduce to complete lines
} }
return true; return true;
} }
return m_request.length() == 0 && m_listening; return m_request.length() == 0 && m_listening;
} }
/** /**
* Return whether this is a HTTP message. * Return whether this is a HTTP message.
* @return whether this is a HTTP message. * @return whether this is a HTTP message.
*/ */
bool isHttp() const { return m_isHttp; } bool isHttp() const { return m_isHttp; }
/** /**
* Return the request string. * Return the request string.
* @return the request string. * @return the request string.
*/ */
string getRequest() const { return m_request; } string getRequest() const { return m_request; }
/** /**
* Wait for the result being set and return the result string. * Wait for the result being set and return the result string.
* @return the result string. * @return the result string.
*/ */
string getResult() { string getResult() {
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
while (!m_resultSet) while (!m_resultSet)
pthread_cond_wait(&m_cond, &m_mutex); pthread_cond_wait(&m_cond, &m_mutex);
m_request.clear(); m_request.clear();
string result = m_result; string result = m_result;
m_result.clear(); m_result.clear();
m_resultSet = false; m_resultSet = false;
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return result; return result;
} }
/** /**
* Set the result string and notify the waiting thread. * Set the result string and notify the waiting thread.
* @param result the result string. * @param result the result string.
* @param listening whether the client is in listening mode. * @param listening whether the client is in listening mode.
* @param listenUntil the end time to which to updates were added (exclusive). * @param listenUntil the end time to which to updates were added (exclusive).
* @param disconnect true when the client shall be disconnected. * @param disconnect true when the client shall be disconnected.
*/ */
void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) { void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) {
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
m_result = result; m_result = result;
m_disconnect = disconnect; m_disconnect = disconnect;
m_listening = listening; m_listening = listening;
m_listenSince = listenUntil; m_listenSince = listenUntil;
m_resultSet = true; m_resultSet = true;
pthread_cond_signal(&m_cond); pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
} }
/** /**
* Return whether the client is in listening mode. * Return whether the client is in listening mode.
* @param listenSince set to the start time from which to add updates (inclusive). * @param listenSince set to the start time from which to add updates (inclusive).
* @return whether the client is in listening mode. * @return whether the client is in listening mode.
*/ */
bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; } bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; }
/** /**
* Return whether the client shall be disconnected. * Return whether the client shall be disconnected.
* @return true when the client shall be disconnected. * @return true when the client shall be disconnected.
*/ */
bool isDisconnect() { return m_disconnect; } bool isDisconnect() { return m_disconnect; }
private: private:
/** whether this is a HTTP message. */ /** whether this is a HTTP message. */
const bool m_isHttp; const bool m_isHttp;
/** the request string. */ /** the request string. */
string m_request; string m_request;
/** whether the result was already set. */ /** whether the result was already set. */
bool m_resultSet; bool m_resultSet;
/** the result string. */ /** the result string. */
string m_result; string m_result;
/** set to true when the client shall be disconnected. */ /** set to true when the client shall be disconnected. */
bool m_disconnect; bool m_disconnect;
/** mutex variable for exclusive lock. */ /** mutex variable for exclusive lock. */
pthread_mutex_t m_mutex; pthread_mutex_t m_mutex;
/** condition variable for exclusive lock. */ /** condition variable for exclusive lock. */
pthread_cond_t m_cond; pthread_cond_t m_cond;
/** whether the client is in listening mode. */ /** whether the client is in listening mode. */
bool m_listening; bool m_listening;
/** start timestamp of listening update. */ /** start timestamp of listening update. */
time_t m_listenSince; time_t m_listenSince;
}; };
/** /**
* class connection which handle client and baseloop communication. * class connection which handle client and baseloop communication.
*/ */
class Connection : public Thread { class Connection : public Thread {
public: public:
/** /**
* Constructor. * Constructor.
* @param socket the @a TCPSocket for communication. * @param socket the @a TCPSocket for communication.
* @param isHttp whether this is a HTTP message. * @param isHttp whether this is a HTTP message.
* @param netQueue the reference to the @a NetMessage @a Queue. * @param netQueue the reference to the @a NetMessage @a Queue.
*/ */
Connection(TCPSocket* socket, const bool isHttp, Queue<NetMessage*>* netQueue) Connection(TCPSocket* socket, const bool isHttp, Queue<NetMessage*>* netQueue)
: Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) { : Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) {
m_id = ++m_ids; m_id = ++m_ids;
} }
virtual ~Connection() { if (m_socket) delete m_socket; } virtual ~Connection() { if (m_socket) delete m_socket; }
/** /**
* endless loop for connection instance. * endless loop for connection instance.
*/ */
virtual void run(); virtual void run();
/** /**
* Stop this connection. * Stop this connection.
*/ */
virtual void stop() { m_notify.notify(); Thread::stop(); } virtual void stop() { m_notify.notify(); Thread::stop(); }
/** /**
* Return the ID of this connection. * Return the ID of this connection.
* @return the ID of this connection. * @return the ID of this connection.
*/ */
int getID() { return m_id; } int getID() { return m_id; }
private: private:
/** whether this is a HTTP connection. */ /** whether this is a HTTP connection. */
const bool m_isHttp; const bool m_isHttp;
/** the @a TCPSocket for communication. */ /** the @a TCPSocket for communication. */
TCPSocket* m_socket; TCPSocket* m_socket;
/** the reference to the @a NetMessage @a Queue. */ /** the reference to the @a NetMessage @a Queue. */
Queue<NetMessage*>* m_netQueue; Queue<NetMessage*>* m_netQueue;
/** notification object for shutdown procedure. */ /** notification object for shutdown procedure. */
Notify m_notify; Notify m_notify;
/** the ID of this connection. */ /** the ID of this connection. */
int m_id; int m_id;
/** the IF of the last opened connection. */ /** the IF of the last opened connection. */
static int m_ids; static int m_ids;
}; };
/** /**
* class network which listening on tcp socket for incoming connections. * class network which listening on tcp socket for incoming connections.
*/ */
class Network : public Thread { class Network : public Thread {
public: public:
/** /**
* create a network instance and listening for incoming connections. * create a network instance and listening for incoming connections.
* @param local true to accept connections only for local host. * @param local true to accept connections only for local host.
* @param port the port to listen for command line connections. * @param port the port to listen for command line connections.
* @param httpPort the port to listen for HTTP connections, or 0. * @param httpPort the port to listen for HTTP connections, or 0.
* @param netQueue the reference to the @a NetMessage @a Queue. * @param netQueue the reference to the @a NetMessage @a Queue.
*/ */
Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue<NetMessage*>* netQueue); Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue<NetMessage*>* netQueue);
/** /**
* destructor. * destructor.
*/ */
~Network(); ~Network();
/** /**
* endless loop for network instance. * endless loop for network instance.
*/ */
virtual void run(); virtual void run();
/** /**
* shutdown network subsystem. * shutdown network subsystem.
*/ */
void stop() const { m_notify.notify(); usleep(100000); } void stop() const { m_notify.notify(); usleep(100000); }
private: private:
/** the list of active @a Connection instances. */ /** the list of active @a Connection instances. */
list<Connection*> m_connections; list<Connection*> m_connections;
/** the reference to the @a NetMessage @a Queue. */ /** the reference to the @a NetMessage @a Queue. */
Queue<NetMessage*>* m_netQueue; Queue<NetMessage*>* m_netQueue;
/** the command line @a TCPServer instance. */ /** the command line @a TCPServer instance. */
TCPServer* m_tcpServer; TCPServer* m_tcpServer;
/** the HTTP @a TCPServer instance, or NULL. */ /** the HTTP @a TCPServer instance, or NULL. */
TCPServer* m_httpServer; TCPServer* m_httpServer;
/** @a Notify object for shutdown procedure. */ /** @a Notify object for shutdown procedure. */
Notify m_notify; Notify m_notify;
/** true if this instance is listening */ /** true if this instance is listening */
bool m_listening; bool m_listening;
/** /**
* clean inactive connections from container. * clean inactive connections from container.
*/ */
void cleanConnections(); void cleanConnections();
}; };
} // namespace ebusd } // namespace ebusd
+2 -2
View File
@@ -22,8 +22,8 @@
namespace ebusd { namespace ebusd {
bool libebus_contrib_register() { bool libebus_contrib_register() {
contrib_tem_register(); contrib_tem_register();
return true; return true;
} }
} // namespace ebusd } // namespace ebusd
+91 -91
View File
@@ -17,7 +17,7 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "tem.h" #include "tem.h"
@@ -36,108 +36,108 @@ using std::setw;
using std::dec; using std::dec;
void contrib_tem_register() { void contrib_tem_register() {
DataTypeList::getInstance()->add(new TemParamDataType("TEM_P")); DataTypeList::getInstance()->add(new TemParamDataType("TEM_P"));
} }
result_t TemParamDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived) { result_t TemParamDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived) {
if (divisor == 0) { if (divisor == 0) {
divisor = 1; divisor = 1;
} }
if (bitCount == 0) { if (bitCount == 0) {
bitCount = m_bitCount; bitCount = m_bitCount;
} }
if (divisor == 1 && bitCount == 16) { if (divisor == 1 && bitCount == 16) {
derived = this; derived = this;
return RESULT_OK; return RESULT_OK;
} }
return RESULT_ERR_INVALID_ARG; return RESULT_ERR_INVALID_ARG;
} }
result_t TemParamDataType::readSymbols(SymbolString& input, const bool isMaster, result_t TemParamDataType::readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat) { ostringstream& output, OutputFormat outputFormat) {
unsigned int value = 0; unsigned int value = 0;
result_t result = readRawValue(input, offset, length, value); result_t result = readRawValue(input, offset, length, value);
if (result != RESULT_OK) { if (result != RESULT_OK) {
return result; return result;
} }
if (value == m_replacement) { if (value == m_replacement) {
if (outputFormat & OF_JSON) { if (outputFormat & OF_JSON) {
output << "null"; output << "null";
} else { } else {
output << NULL_VALUE; output << NULL_VALUE;
} }
return RESULT_OK; return RESULT_OK;
} }
int grp = 0, num = 0; int grp = 0, num = 0;
if (isMaster) { if (isMaster) {
grp = (value & 0x1f); // grp in bits 0...5 grp = (value & 0x1f); // grp in bits 0...5
num = ((value >> 8) & 0x7f); // num in bits 8...13 num = ((value >> 8) & 0x7f); // num in bits 8...13
} else { } else {
grp = ((value >> 7) & 0x1f); // grp in bits 7...11 grp = ((value >> 7) & 0x1f); // grp in bits 7...11
num = (value & 0x7f); // num in bits 0...6 num = (value & 0x7f); // num in bits 0...6
} }
if (outputFormat & OF_JSON) { if (outputFormat & OF_JSON) {
output << '"'; output << '"';
} }
output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num); output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num);
if (outputFormat & OF_JSON) { if (outputFormat & OF_JSON) {
output << '"'; output << '"';
} }
output << setfill(' ') << setw(0); // reset output << setfill(' ') << setw(0); // reset
return RESULT_OK; return RESULT_OK;
} }
result_t TemParamDataType::writeSymbols(istringstream& input, result_t TemParamDataType::writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength) { SymbolString& output, const bool isMaster, unsigned char* usedLength) {
unsigned int value; unsigned int value;
int grp, num; int grp, num;
string token; string token;
const char* str = input.str().c_str(); const char* str = input.str().c_str();
if (strcasecmp(str, NULL_VALUE) == 0) { if (strcasecmp(str, NULL_VALUE) == 0) {
value = m_replacement; // replacement value value = m_replacement; // replacement value
} else { } else {
if (input.eof() || !getline(input, token, '-')) { if (input.eof() || !getline(input, token, '-')) {
return RESULT_ERR_EOF; // incomplete return RESULT_ERR_EOF; // incomplete
} }
str = token.c_str(); str = token.c_str();
if (str == NULL || *str == 0) { if (str == NULL || *str == 0) {
return RESULT_ERR_EOF; // input too short return RESULT_ERR_EOF; // input too short
} }
char* strEnd = NULL; char* strEnd = NULL;
grp = (unsigned int)strtoul(str, &strEnd, 10); grp = (unsigned int)strtoul(str, &strEnd, 10);
if (strEnd == NULL || strEnd == str || *strEnd != 0) { if (strEnd == NULL || strEnd == str || *strEnd != 0) {
return RESULT_ERR_INVALID_NUM; // invalid value return RESULT_ERR_INVALID_NUM; // invalid value
} }
if (input.eof() || !getline(input, token, '-')) { if (input.eof() || !getline(input, token, '-')) {
return RESULT_ERR_EOF; // incomplete return RESULT_ERR_EOF; // incomplete
} }
str = token.c_str(); str = token.c_str();
if (str == NULL || *str == 0) { if (str == NULL || *str == 0) {
return RESULT_ERR_EOF; // input too short return RESULT_ERR_EOF; // input too short
} }
strEnd = NULL; strEnd = NULL;
num = (unsigned int)strtoul(str, &strEnd, 10); num = (unsigned int)strtoul(str, &strEnd, 10);
if (strEnd == NULL || strEnd == str || *strEnd != 0) { if (strEnd == NULL || strEnd == str || *strEnd != 0) {
return RESULT_ERR_INVALID_NUM; // invalid value return RESULT_ERR_INVALID_NUM; // invalid value
} }
if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) { if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) {
return RESULT_ERR_OUT_OF_RANGE; // value out of range return RESULT_ERR_OUT_OF_RANGE; // value out of range
} }
if (isMaster) { if (isMaster) {
value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13 value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13
} else { } else {
value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6 value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6
} }
} }
if (value < getMinValue() || value > getMaxValue()) { if (value < getMinValue() || value > getMaxValue()) {
return RESULT_ERR_OUT_OF_RANGE; // value out of range return RESULT_ERR_OUT_OF_RANGE; // value out of range
} }
return writeRawValue(value, offset, length, output, usedLength); return writeRawValue(value, offset, length, output, usedLength);
} }
} // namespace ebusd } // namespace ebusd
+17 -17
View File
@@ -41,26 +41,26 @@ namespace ebusd {
* data. * data.
*/ */
class TemParamDataType : public NumberDataType { class TemParamDataType : public NumberDataType {
public: public:
/** /**
* Constructs a new instance. * Constructs a new instance.
* @param id the type identifier. * @param id the type identifier.
*/ */
explicit TemParamDataType(const string id) explicit TemParamDataType(const string id)
: NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {} : NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {}
// @copydoc // @copydoc
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived); virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
// @copydoc // @copydoc
virtual result_t readSymbols(SymbolString& input, const bool isMaster, virtual result_t readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat); ostringstream& output, OutputFormat outputFormat);
// @copydoc // @copydoc
virtual result_t writeSymbols(istringstream& input, virtual result_t writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength); SymbolString& output, const bool isMaster, unsigned char* usedLength);
}; };
/** /**
+153 -153
View File
@@ -28,167 +28,167 @@ using namespace ebusd;
static bool error = false; static bool error = false;
void verify(bool expectFailMatch, string type, string input, void verify(bool expectFailMatch, string type, string input,
bool match, string expectStr, string gotStr) { bool match, string expectStr, string gotStr) {
match = match && expectStr == gotStr; match = match && expectStr == gotStr;
if (expectFailMatch) { if (expectFailMatch) {
if (match) { if (match) {
cout << " failed " << type << " match >" << input cout << " failed " << type << " match >" << input
<< "< error: unexpectedly succeeded" << endl; << "< error: unexpectedly succeeded" << endl;
error = true; error = true;
} else { } else {
cout << " failed " << type << " match >" << input << "< OK" << endl; cout << " failed " << type << " match >" << input << "< OK" << endl;
} }
} else if (match) { } else if (match) {
cout << " " << type << " match >" << input << "< OK" << endl; cout << " " << type << " match >" << input << "< OK" << endl;
} else { } else {
cout << " " << type << " match >" << input << "< error: got >" cout << " " << type << " match >" << input << "< error: got >"
<< gotStr << "<, expected >" << expectStr << "<" << endl; << gotStr << "<, expected >" << expectStr << "<" << endl;
error = true; error = true;
} }
} }
int main() { int main() {
DataType* type = DataTypeList::getInstance()->get("TEM_P"); DataType* type = DataTypeList::getInstance()->get("TEM_P");
if (type == NULL) { if (type == NULL) {
cout << "datatype not registered" << endl; cout << "datatype not registered" << endl;
return 1; return 1;
} }
string checks[][5] = { string checks[][5] = {
// entry: definition, decoded value, master data, slave data, flags // entry: definition, decoded value, master data, slave data, flags
// definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] // definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
{"x,,TEM_P", "04-033", "10fe0700020421", "00", ""}, {"x,,TEM_P", "04-033", "10fe0700020421", "00", ""},
{"x,,TEM_P", "00-000", "10fe0700020000", "00", ""}, {"x,,TEM_P", "00-000", "10fe0700020000", "00", ""},
{"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""}, {"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""},
{"x,,TEM_P", "-", "10fe070002ffff", "00", ""}, {"x,,TEM_P", "-", "10fe070002ffff", "00", ""},
{"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"}, {"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"},
{"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"}, {"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"},
{"x,,TEM_P", "04-033", "1015070000", "022102", ""}, {"x,,TEM_P", "04-033", "1015070000", "022102", ""},
{"x,,TEM_P", "00-000", "1015070000", "020000", ""}, {"x,,TEM_P", "00-000", "1015070000", "020000", ""},
{"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""}, {"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""},
{"x,,TEM_P", "-", "1015070000", "02ffff", ""}, {"x,,TEM_P", "-", "1015070000", "02ffff", ""},
{"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"}, {"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"},
{"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"}, {"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"},
}; };
DataFieldTemplates* templates = new DataFieldTemplates(); DataFieldTemplates* templates = new DataFieldTemplates();
DataField* fields = NULL; DataField* fields = NULL;
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
string check[5] = checks[i]; string check[5] = checks[i];
istringstream isstr(check[0]); istringstream isstr(check[0]);
string expectStr = check[1]; string expectStr = check[1];
SymbolString mstr(false); SymbolString mstr(false);
result_t result = mstr.parseHex(check[2]); result_t result = mstr.parseHex(check[2]);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
error = true; error = true;
continue; continue;
} }
SymbolString sstr(false); SymbolString sstr(false);
result = sstr.parseHex(check[3]); result = sstr.parseHex(check[3]);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
error = true; error = true;
continue; continue;
} }
string flags = check[4]; string flags = check[4];
bool isSet = flags.find('s') != string::npos; bool isSet = flags.find('s') != string::npos;
bool failedRead = flags.find('r') != string::npos; bool failedRead = flags.find('r') != string::npos;
bool failedReadMatch = flags.find('R') != string::npos; bool failedReadMatch = flags.find('R') != string::npos;
bool failedWrite = flags.find('w') != string::npos; bool failedWrite = flags.find('w') != string::npos;
bool failedWriteMatch = flags.find('W') != string::npos; bool failedWriteMatch = flags.find('W') != string::npos;
string item; string item;
vector<string> entries; vector<string> entries;
while (getline(isstr, item, FIELD_SEPARATOR)) while (getline(isstr, item, FIELD_SEPARATOR))
entries.push_back(item); entries.push_back(item);
if (fields != NULL) { if (fields != NULL) {
delete fields; delete fields;
fields = NULL; fields = NULL;
} }
vector<string>::iterator it = entries.begin(); vector<string>::iterator it = entries.begin();
result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1]))); result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1])));
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl;
error = true; error = true;
continue; continue;
} }
if (fields == NULL) { if (fields == NULL) {
cout << "\"" << check[0] << "\": create error: NULL" << endl; cout << "\"" << check[0] << "\": create error: NULL" << endl;
error = true; error = true;
continue; continue;
} }
if (it != entries.end()) { if (it != entries.end()) {
cout << "\"" << check[0] << "\": create error: trailing input" << endl; cout << "\"" << check[0] << "\": create error: trailing input" << endl;
error = true; error = true;
continue; continue;
} }
cout << "\"" << check[0] << "\"=\""; cout << "\"" << check[0] << "\"=\"";
fields->dump(cout); fields->dump(cout);
cout << "\": create OK" << endl; cout << "\": create OK" << endl;
ostringstream output; ostringstream output;
SymbolString writeMstr(false); SymbolString writeMstr(false);
result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10)); result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10));
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl; cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl;
error = true; error = true;
} }
SymbolString writeSstr(false); SymbolString writeSstr(false);
result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2)); result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2));
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl; cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl;
error = true; error = true;
} }
result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false); result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false);
if (result >= RESULT_OK) { if (result >= RESULT_OK) {
result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty()); result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty());
} }
if (failedRead) { if (failedRead) {
if (result >= RESULT_OK) { if (result >= RESULT_OK) {
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
<< "< error: unexpectedly succeeded" << endl; << "< error: unexpectedly succeeded" << endl;
error = true; error = true;
} else { } else {
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3] cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
<< "< OK" << endl; << "< OK" << endl;
} }
} else if (result < RESULT_OK) { } else if (result < RESULT_OK) {
cout << " read " << fields->getName() << " >" << check[2] << " " << check[3] cout << " read " << fields->getName() << " >" << check[2] << " " << check[3]
<< "< error: " << getResultCode(result) << endl; << "< error: " << getResultCode(result) << endl;
error = true; error = true;
} else { } else {
bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0; bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0;
verify(failedReadMatch, "read", check[2], match, expectStr, output.str()); verify(failedReadMatch, "read", check[2], match, expectStr, output.str());
} }
istringstream input(expectStr); istringstream input(expectStr);
result = fields->write(input, pt_masterData, writeMstr, 0); result = fields->write(input, pt_masterData, writeMstr, 0);
if (result >= RESULT_OK) { if (result >= RESULT_OK) {
result = fields->write(input, pt_slaveData, writeSstr, 0); result = fields->write(input, pt_slaveData, writeSstr, 0);
} }
if (failedWrite) { if (failedWrite) {
if (result >= RESULT_OK) { if (result >= RESULT_OK) {
cout << " failed write " << fields->getName() << " >" cout << " failed write " << fields->getName() << " >"
<< expectStr << "< error: unexpectedly succeeded" << endl; << expectStr << "< error: unexpectedly succeeded" << endl;
error = true; error = true;
} else { } else {
cout << " failed write " << fields->getName() << " >" cout << " failed write " << fields->getName() << " >"
<< expectStr << "< OK" << endl; << expectStr << "< OK" << endl;
} }
} else if (result < RESULT_OK) { } else if (result < RESULT_OK) {
cout << " write " << fields->getName() << " >" << expectStr cout << " write " << fields->getName() << " >" << expectStr
<< "< error: " << getResultCode(result) << endl; << "< error: " << getResultCode(result) << endl;
error = true; error = true;
} else { } else {
bool match = mstr == writeMstr && sstr == writeSstr; bool match = mstr == writeMstr && sstr == writeSstr;
verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false)); verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false));
} }
delete fields; delete fields;
fields = NULL; fields = NULL;
} }
delete templates; delete templates;
return error ? 1 : 0; return error ? 1 : 0;
} }
+877 -877
View File
File diff suppressed because it is too large Load Diff
+483 -483
View File
File diff suppressed because it is too large Load Diff
+934 -934
View File
File diff suppressed because it is too large Load Diff
+341 -341
View File
@@ -89,9 +89,9 @@ static const unsigned int OF_JSON = 0x10; //!< JSON format.
/** the message part in which a data field is stored. */ /** the message part in which a data field is stored. */
enum PartType { enum PartType {
pt_any, //!< stored in any data (master or slave) pt_any, //!< stored in any data (master or slave)
pt_masterData, //!< stored in master data pt_masterData, //!< stored in master data
pt_slaveData, //!< stored in slave data pt_slaveData, //!< stored in slave data
}; };
/* flags for @a DataType. */ /* flags for @a DataType. */
@@ -151,121 +151,121 @@ void printErrorPos(ostream& out, vector<string>::iterator begin, const vector<st
* Base class for all kinds of data types. * Base class for all kinds of data types.
*/ */
class DataType { class DataType {
public: public:
/** /**
* Constructs a new instance. * Constructs a new instance.
* @param id the type identifier. * @param id the type identifier.
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
* @param flags the combination of flags (like #BCD). * @param flags the combination of flags (like #BCD).
* @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). * @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
*/ */
DataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement) DataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement)
: m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {} : m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DataType() { } virtual ~DataType() { }
/** /**
* @return the type identifier. * @return the type identifier.
*/ */
string getId() const { return m_id; } string getId() const { return m_id; }
/** /**
* @return the number of bits (maximum length if #ADJ flag is set). * @return the number of bits (maximum length if #ADJ flag is set).
*/ */
unsigned char getBitCount() const { return m_bitCount; } unsigned char getBitCount() const { return m_bitCount; }
/** /**
* Check whether a flag is set. * Check whether a flag is set.
* @param flag the flag to check (like #BCD). * @param flag the flag to check (like #BCD).
* @return whether the flag is set. * @return whether the flag is set.
*/ */
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; } bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
/** /**
* @return whether this type is ignored. * @return whether this type is ignored.
*/ */
bool isIgnored() const { return hasFlag(IGN); } bool isIgnored() const { return hasFlag(IGN); }
/** /**
* @return whether this type has an adjustable length. * @return whether this type has an adjustable length.
*/ */
bool isAdjustableLength() const { return hasFlag(ADJ); } bool isAdjustableLength() const { return hasFlag(ADJ); }
/** /**
* @return whether this field is derived from @a NumberDataType. * @return whether this field is derived from @a NumberDataType.
*/ */
bool isNumeric() const { return hasFlag(NUM); } bool isNumeric() const { return hasFlag(NUM); }
/** /**
* @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). * @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
*/ */
unsigned int getReplacement() const { return m_replacement; } unsigned int getReplacement() const { return m_replacement; }
/** /**
* Dump the type identifier with the specified length and optionally the * Dump the type identifier with the specified length and optionally the
* divisor to the output (@a FIELD_SEPARATOR is always appended!). * divisor to the output (@a FIELD_SEPARATOR is always appended!).
* @param output the @a ostream to dump to. * @param output the @a ostream to dump to.
* @param length the number of symbols to read/write. * @param length the number of symbols to read/write.
* @return true when a non-default divisor was written to the output. * @return true when a non-default divisor was written to the output.
*/ */
virtual bool dump(ostream& output, const unsigned char length) const; virtual bool dump(ostream& output, const unsigned char length) const;
/** /**
* Internal method for reading the numeric raw value from a @a SymbolString. * Internal method for reading the numeric raw value from a @a SymbolString.
* @param input the unescaped @a SymbolString to read the binary value from. * @param input the unescaped @a SymbolString to read the binary value from.
* @param offset the offset in the @a SymbolString. * @param offset the offset in the @a SymbolString.
* @param length the number of symbols to read. * @param length the number of symbols to read.
* @param value the variable in which to store the numeric raw value. * @param value the variable in which to store the numeric raw value.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t readRawValue(SymbolString& input, virtual result_t readRawValue(SymbolString& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
unsigned int& value) = 0; unsigned int& value) = 0;
/** /**
* Internal method for reading the field from a @a SymbolString. * Internal method for reading the field from a @a SymbolString.
* @param input the unescaped @a SymbolString to read the binary value from. * @param input the unescaped @a SymbolString to read the binary value from.
* @param isMaster whether the @a SymbolString is the master part. * @param isMaster whether the @a SymbolString is the master part.
* @param offset the offset in the @a SymbolString. * @param offset the offset in the @a SymbolString.
* @param length the number of symbols to read. * @param length the number of symbols to read.
* @param output the ostringstream to append the formatted value to. * @param output the ostringstream to append the formatted value to.
* @param outputFormat the @a OutputFormat options to use. * @param outputFormat the @a OutputFormat options to use.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t readSymbols(SymbolString& input, const bool isMaster, virtual result_t readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat) = 0; ostringstream& output, OutputFormat outputFormat) = 0;
/** /**
* Internal method for writing the field to a @a SymbolString. * Internal method for writing the field to a @a SymbolString.
* @param input the @a istringstream to parse the formatted value from. * @param input the @a istringstream to parse the formatted value from.
* @param offset the offset in the @a SymbolString. * @param offset the offset in the @a SymbolString.
* @param length the number of symbols to write, or @a REMAIN_LEN. * @param length the number of symbols to write, or @a REMAIN_LEN.
* @param output the unescaped @a SymbolString to write the binary value to. * @param output the unescaped @a SymbolString to write the binary value to.
* @param isMaster whether the @a SymbolString is the master part. * @param isMaster whether the @a SymbolString is the master part.
* @param usedLength the variable in which to store the used length in bytes, or NULL. * @param usedLength the variable in which to store the used length in bytes, or NULL.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t writeSymbols(istringstream& input, virtual result_t writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0; SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0;
protected: protected:
/** the type identifier. */ /** the type identifier. */
const string m_id; const string m_id;
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */ /** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
const unsigned char m_bitCount; const unsigned char m_bitCount;
/** the combination of flags (like #BCD). */ /** the combination of flags (like #BCD). */
const uint16_t m_flags; const uint16_t m_flags;
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */ /** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
const unsigned int m_replacement; const unsigned int m_replacement;
}; };
@@ -273,43 +273,43 @@ class DataType {
* A string based @a DataType. * A string based @a DataType.
*/ */
class StringDataType : public DataType { class StringDataType : public DataType {
public: public:
/** /**
* Constructs a new instance. * Constructs a new instance.
* @param id the type identifier. * @param id the type identifier.
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
* @param flags the combination of flags (like #BCD). * @param flags the combination of flags (like #BCD).
* @param replacement the replacement value (fill-up value). * @param replacement the replacement value (fill-up value).
* @param isHex true for hex digits instead of characters. * @param isHex true for hex digits instead of characters.
*/ */
StringDataType(const string id, const unsigned char bitCount, const uint16_t flags, StringDataType(const string id, const unsigned char bitCount, const uint16_t flags,
const unsigned int replacement, bool isHex = false) const unsigned int replacement, bool isHex = false)
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {} : DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~StringDataType() {} virtual ~StringDataType() {}
// @copydoc // @copydoc
virtual result_t readRawValue(SymbolString& input, virtual result_t readRawValue(SymbolString& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
unsigned int& value); unsigned int& value);
// @copydoc // @copydoc
virtual result_t readSymbols(SymbolString& input, const bool isMaster, virtual result_t readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat); ostringstream& output, OutputFormat outputFormat);
// @copydoc // @copydoc
virtual result_t writeSymbols(istringstream& input, virtual result_t writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength); SymbolString& output, const bool isMaster, unsigned char* usedLength);
private: private:
/** true for hex digits instead of characters. */ /** true for hex digits instead of characters. */
const bool m_isHex; const bool m_isHex;
}; };
@@ -317,66 +317,66 @@ class StringDataType : public DataType {
* A date/time based @a DataType. * A date/time based @a DataType.
*/ */
class DateTimeDataType : public DataType { class DateTimeDataType : public DataType {
public: public:
/** /**
* Constructs a new instance. * Constructs a new instance.
* @param id the type identifier. * @param id the type identifier.
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). * @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
* @param flags the combination of flags (like #BCD). * @param flags the combination of flags (like #BCD).
* @param replacement the replacement value. * @param replacement the replacement value.
* @param hasDate true if date part is present. * @param hasDate true if date part is present.
* @param hasTime true if time part is present. * @param hasTime true if time part is present.
* @param resolution the the resolution in minutes for time types, or 1. * @param resolution the the resolution in minutes for time types, or 1.
*/ */
DateTimeDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement, DateTimeDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
const bool hasDate, const bool hasTime, const int16_t resolution) const bool hasDate, const bool hasTime, const int16_t resolution)
: DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {} : DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DateTimeDataType() {} virtual ~DateTimeDataType() {}
/** /**
* @return true if date part is present. * @return true if date part is present.
*/ */
bool hasDate() const { return m_hasDate; } bool hasDate() const { return m_hasDate; }
/** /**
* @return true if time part is present. * @return true if time part is present.
*/ */
bool hasTime() const { return m_hasTime; } bool hasTime() const { return m_hasTime; }
/** /**
* @return the resolution in minutes for time types, or 1. * @return the resolution in minutes for time types, or 1.
*/ */
int16_t getResolution() const { return m_resolution; } int16_t getResolution() const { return m_resolution; }
// @copydoc // @copydoc
virtual result_t readRawValue(SymbolString& input, virtual result_t readRawValue(SymbolString& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
unsigned int& value); unsigned int& value);
// @copydoc // @copydoc
virtual result_t readSymbols(SymbolString& input, const bool isMaster, virtual result_t readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat); ostringstream& output, OutputFormat outputFormat);
// @copydoc // @copydoc
virtual result_t writeSymbols(istringstream& input, virtual result_t writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength); SymbolString& output, const bool isMaster, unsigned char* usedLength);
private: private:
/** true if date part is present. */ /** true if date part is present. */
const bool m_hasDate; const bool m_hasDate;
/** true if time part is present. */ /** true if time part is present. */
const bool m_hasTime; const bool m_hasTime;
/** the resolution in minutes for time types, or 1. */ /** the resolution in minutes for time types, or 1. */
const int16_t m_resolution; const int16_t m_resolution;
}; };
@@ -384,135 +384,135 @@ class DateTimeDataType : public DataType {
* A number based @a DataType. * A number based @a DataType.
*/ */
class NumberDataType : public DataType { class NumberDataType : public DataType {
public: public:
/** /**
* Constructs a new instance for multiple of 8 bits. * Constructs a new instance for multiple of 8 bits.
* @param id the type identifier. * @param id the type identifier.
* @param bitCount the number of bits (maximum length if #ADJ flag is set). * @param bitCount the number of bits (maximum length if #ADJ flag is set).
* @param flags the combination of flags (like #BCD). * @param flags the combination of flags (like #BCD).
* @param replacement the replacement value (no replacement if equal to minValue). * @param replacement the replacement value (no replacement if equal to minValue).
* @param minValue the minimum raw value. * @param minValue the minimum raw value.
* @param maxValue the maximum raw value. * @param maxValue the maximum raw value.
* @param divisor the divisor (negative for reciprocal). * @param divisor the divisor (negative for reciprocal).
*/ */
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement, NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
const unsigned int minValue, const unsigned int maxValue, const int divisor) const unsigned int minValue, const unsigned int maxValue, const int divisor)
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(NULL) {} : DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(NULL) {}
/** /**
* Constructs a new instance for less than 8 bits. * Constructs a new instance for less than 8 bits.
* @param id the type identifier. * @param id the type identifier.
* @param bitCount the number of bits (maximum length if #ADJ flag is set). * @param bitCount the number of bits (maximum length if #ADJ flag is set).
* @param flags the combination of flags (like #ADJ, may not include flag #BCD). * @param flags the combination of flags (like #ADJ, may not include flag #BCD).
* @param replacement the replacement value (no replacement if zero). * @param replacement the replacement value (no replacement if zero).
* @param firstBit the offset to the first bit. * @param firstBit the offset to the first bit.
* @param divisor the divisor (negative for reciprocal). * @param divisor the divisor (negative for reciprocal).
*/ */
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement, NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
const int16_t firstBit, const int divisor) const int16_t firstBit, const int divisor)
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor), m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {} : DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor), m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~NumberDataType() {} virtual ~NumberDataType() {}
/** /**
* Calculate the precision from the divisor. * Calculate the precision from the divisor.
* *
* @param divisor the divisor (negative for reciprocal). * @param divisor the divisor (negative for reciprocal).
* @return the precision for formatting the value. * @return the precision for formatting the value.
*/ */
static unsigned char calcPrecision(const int divisor); static unsigned char calcPrecision(const int divisor);
// @copydoc // @copydoc
virtual bool dump(ostream& output, const unsigned char length) const; virtual bool dump(ostream& output, const unsigned char length) const;
/** /**
* Derive a new @a NumberDataType from this. * Derive a new @a NumberDataType from this.
* @param divisor the extra divisor (negative for reciprocal) to apply, or * @param divisor the extra divisor (negative for reciprocal) to apply, or
* 1 for none (if applicable), or 0 to keep the current value. * 1 for none (if applicable), or 0 to keep the current value.
* @param bitCount the number of bits (maximum length if #ADJ flag is set, * @param bitCount the number of bits (maximum length if #ADJ flag is set,
* must be multiple of 8 with flag #BCD), or 0 to keep the current value. * must be multiple of 8 with flag #BCD), or 0 to keep the current value.
* @param derived the derived @a NumberDataType, or this if derivation is * @param derived the derived @a NumberDataType, or this if derivation is
* not necessary. * not necessary.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived); virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
/** /**
* @return the minimum raw value. * @return the minimum raw value.
*/ */
unsigned int getMinValue() const { return m_minValue; } unsigned int getMinValue() const { return m_minValue; }
/** /**
* @return the maximum raw value. * @return the maximum raw value.
*/ */
unsigned int getMaxValue() const { return m_maxValue; } unsigned int getMaxValue() const { return m_maxValue; }
/** /**
* @return the divisor (negative for reciprocal). * @return the divisor (negative for reciprocal).
*/ */
int getDivisor() const { return m_divisor; } int getDivisor() const { return m_divisor; }
/** /**
* @return the precision for formatting the value. * @return the precision for formatting the value.
*/ */
unsigned char getPrecision() const { return m_precision; } unsigned char getPrecision() const { return m_precision; }
/** /**
* @return the offset to the first bit. * @return the offset to the first bit.
*/ */
int16_t getFirstBit() const { return m_firstBit; } int16_t getFirstBit() const { return m_firstBit; }
// @copydoc // @copydoc
virtual result_t readRawValue(SymbolString& input, virtual result_t readRawValue(SymbolString& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
unsigned int& value); unsigned int& value);
// @copydoc // @copydoc
virtual result_t readSymbols(SymbolString& input, const bool isMaster, virtual result_t readSymbols(SymbolString& input, const bool isMaster,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
ostringstream& output, OutputFormat outputFormat); ostringstream& output, OutputFormat outputFormat);
/** /**
* Internal method for writing the numeric raw value to a @a SymbolString. * Internal method for writing the numeric raw value to a @a SymbolString.
* @param value the numeric raw value to write. * @param value the numeric raw value to write.
* @param offset the offset in the @a SymbolString. * @param offset the offset in the @a SymbolString.
* @param length the number of symbols to write, or @a REMAIN_LEN. * @param length the number of symbols to write, or @a REMAIN_LEN.
* @param output the unescaped @a SymbolString to write the binary value to. * @param output the unescaped @a SymbolString to write the binary value to.
* @param usedLength the variable in which to store the used length in bytes, * @param usedLength the variable in which to store the used length in bytes,
* or NULL. * or NULL.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t writeRawValue(unsigned int value, virtual result_t writeRawValue(unsigned int value,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, unsigned char* usedLength = NULL); SymbolString& output, unsigned char* usedLength = NULL);
// @copydoc // @copydoc
virtual result_t writeSymbols(istringstream& input, virtual result_t writeSymbols(istringstream& input,
const unsigned char offset, const unsigned char length, const unsigned char offset, const unsigned char length,
SymbolString& output, const bool isMaster, unsigned char* usedLength); SymbolString& output, const bool isMaster, unsigned char* usedLength);
private: private:
/** the minimum raw value. */ /** the minimum raw value. */
const unsigned int m_minValue; const unsigned int m_minValue;
/** the maximum raw value. */ /** the maximum raw value. */
const unsigned int m_maxValue; const unsigned int m_maxValue;
/** the divisor (negative for reciprocal). */ /** the divisor (negative for reciprocal). */
const int m_divisor; const int m_divisor;
/** the precision for formatting the value. */ /** the precision for formatting the value. */
const unsigned char m_precision; const unsigned char m_precision;
/** the offset to the first bit. */ /** the offset to the first bit. */
const int16_t m_firstBit; const int16_t m_firstBit;
/** the base @a NumberDataType for derived instances. */ /** the base @a NumberDataType for derived instances. */
NumberDataType* m_baseType; NumberDataType* m_baseType;
}; };
@@ -520,71 +520,71 @@ class NumberDataType : public DataType {
* A map of base @a DataType instances. * A map of base @a DataType instances.
*/ */
class DataTypeList { class DataTypeList {
public: public:
/** /**
* Constructs a new instance and registers the known base data types. * Constructs a new instance and registers the known base data types.
*/ */
DataTypeList(); DataTypeList();
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DataTypeList() { virtual ~DataTypeList() {
clear(); clear();
} }
/** /**
* Returns the singleton instance. * Returns the singleton instance.
* @return the singleton @a DataTypeList instance. * @return the singleton @a DataTypeList instance.
*/ */
static DataTypeList* getInstance(); static DataTypeList* getInstance();
/** /**
* Removes all @a DataType instances. * Removes all @a DataType instances.
*/ */
void clear(); void clear();
/** /**
* Adds a @a DataType instance to this map. * Adds a @a DataType instance to this map.
* @param dataType the @a DataType instance to add. * @param dataType the @a DataType instance to add.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
* Note: the caller may not free the added instance on success. * Note: the caller may not free the added instance on success.
*/ */
result_t add(DataType* dataType); result_t add(DataType* dataType);
/** /**
* Adds a @a DataType instance for later cleanup. * Adds a @a DataType instance for later cleanup.
* @param dataType the @a DataType instance to add. * @param dataType the @a DataType instance to add.
*/ */
void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); } void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); }
/** /**
* Gets the @a DataType instance with the specified ID. * Gets the @a DataType instance with the specified ID.
* @param id the ID string (excluding optional length suffix). * @param id the ID string (excluding optional length suffix).
* @param length the length in bytes, or 0 for default. * @param length the length in bytes, or 0 for default.
* @return the @a DataType instance, or NULL if not available. * @return the @a DataType instance, or NULL if not available.
* Note: the caller may not free the instance. * Note: the caller may not free the instance.
*/ */
DataType* get(const string id, const unsigned char length = 0); DataType* get(const string id, const unsigned char length = 0);
private: private:
/** the known @a DataType instances by ID only. */ /** the known @a DataType instances by ID only. */
map<string, DataType*> m_typesById; map<string, DataType*> m_typesById;
/** the known @a DataType instances by ID and length (i.e. "ID:BITS"). /** the known @a DataType instances by ID and length (i.e. "ID:BITS").
* Note: adjustable length types are stored by ID only. */ * Note: adjustable length types are stored by ID only. */
map<string, DataType*> m_typesByIdLength; map<string, DataType*> m_typesByIdLength;
/** the @a DataType instances to cleanup. */ /** the @a DataType instances to cleanup. */
list<DataType*> m_cleanupTypes; list<DataType*> m_cleanupTypes;
/** the singleton instance. */ /** the singleton instance. */
static DataTypeList s_instance; static DataTypeList s_instance;
#ifdef HAVE_CONTRIB #ifdef HAVE_CONTRIB
/** true when contributed datatypes were successfully initialized. */ /** true when contributed datatypes were successfully initialized. */
static bool s_contrib_initialized; static bool s_contrib_initialized;
#endif #endif
}; };
+218 -218
View File
@@ -17,7 +17,7 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "device.h" #include "device.h"
@@ -29,7 +29,7 @@
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <errno.h> #include <errno.h>
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
# include <poll.h> # include <poll.h>
#endif #endif
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -39,289 +39,289 @@
namespace ebusd { namespace ebusd {
Device::~Device() { Device::~Device() {
close(); close();
} }
Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) { Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) {
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) { if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
char* in = strdup(name); char* in = strdup(name);
bool udp = false; bool udp = false;
char* addrpos = in; char* addrpos = in;
char* portpos = strchr(addrpos, ':'); char* portpos = strchr(addrpos, ':');
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
addrpos += 4; addrpos += 4;
portpos = strchr(addrpos, ':'); portpos = strchr(addrpos, ':');
} }
if (portpos == NULL) { if (portpos == NULL) {
free(in); free(in);
return NULL; // invalid protocol or missing port return NULL; // invalid protocol or missing port
} }
result_t result = RESULT_OK; result_t result = RESULT_OK;
unsigned int port = parseInt(portpos+1, 10, 1, 65535, result); unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
if (result != RESULT_OK) { if (result != RESULT_OK) {
free(in); free(in);
return NULL; // invalid port return NULL; // invalid port
} }
struct sockaddr_in address; struct sockaddr_in address;
memset(reinterpret_cast<char*>(&address), 0, sizeof(address)); memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
*portpos = 0; *portpos = 0;
if (inet_aton(addrpos, &address.sin_addr) == 0) { if (inet_aton(addrpos, &address.sin_addr) == 0) {
struct hostent* h = gethostbyname(addrpos); struct hostent* h = gethostbyname(addrpos);
if (h == NULL) { if (h == NULL) {
free(in); free(in);
return NULL; // invalid host return NULL; // invalid host
} }
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length); memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
} }
free(in); free(in);
address.sin_family = AF_INET; address.sin_family = AF_INET;
address.sin_port = (in_port_t)htons((uint16_t)port); address.sin_port = (in_port_t)htons((uint16_t)port);
return new NetworkDevice(name, address, readOnly, initialSend, udp); return new NetworkDevice(name, address, readOnly, initialSend, udp);
} }
return new SerialDevice(name, checkDevice, readOnly, initialSend); return new SerialDevice(name, checkDevice, readOnly, initialSend);
} }
void Device::close() { void Device::close() {
if (m_fd != -1) { if (m_fd != -1) {
::close(m_fd); ::close(m_fd);
m_fd = -1; m_fd = -1;
} }
} }
bool Device::isValid() { bool Device::isValid() {
if (m_fd == -1) { if (m_fd == -1) {
return false; return false;
} }
if (m_checkDevice) { if (m_checkDevice) {
checkDevice(); checkDevice();
} }
return m_fd != -1; return m_fd != -1;
} }
result_t Device::send(const unsigned char value) { result_t Device::send(const unsigned char value) {
if (!isValid()) { if (!isValid()) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
if (m_readOnly || write(value) != 1) { if (m_readOnly || write(value) != 1) {
return RESULT_ERR_SEND; return RESULT_ERR_SEND;
} }
if (m_listener != NULL) { if (m_listener != NULL) {
m_listener->notifyDeviceData(value, false); m_listener->notifyDeviceData(value, false);
} }
return RESULT_OK; return RESULT_OK;
} }
result_t Device::recv(const unsigned int timeout, unsigned char& value) { result_t Device::recv(const unsigned int timeout, unsigned char& value) {
if (!isValid()) { if (!isValid()) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
if (!available() && timeout > 0) { if (!available() && timeout > 0) {
int ret; int ret;
struct timespec tdiff; struct timespec tdiff;
// set select timeout // set select timeout
tdiff.tv_sec = timeout/1000000; tdiff.tv_sec = timeout/1000000;
tdiff.tv_nsec = (timeout%1000000)*1000; tdiff.tv_nsec = (timeout%1000000)*1000;
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
int nfds = 1; int nfds = 1;
struct pollfd fds[nfds]; struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds)); memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd; fds[0].fd = m_fd;
fds[0].events = POLLIN; fds[0].events = POLLIN;
ret = ppoll(fds, nfds, &tdiff, NULL); ret = ppoll(fds, nfds, &tdiff, NULL);
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
fd_set readfds; fd_set readfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(m_fd, &readfds); FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL); ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL);
#else #else
ret = 1; // ignore timeout if neither ppoll nor pselect are available ret = 1; // ignore timeout if neither ppoll nor pselect are available
#endif #endif
#endif #endif
if (ret == -1) { if (ret == -1) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
if (ret == 0) { if (ret == 0) {
return RESULT_ERR_TIMEOUT; return RESULT_ERR_TIMEOUT;
} }
} }
// directly read byte from device // directly read byte from device
ssize_t nbytes = read(value); ssize_t nbytes = read(value);
if (nbytes == 0) { if (nbytes == 0) {
return RESULT_ERR_EOF; return RESULT_ERR_EOF;
} }
if (nbytes < 0) { if (nbytes < 0) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
if (m_listener != NULL) { if (m_listener != NULL) {
m_listener->notifyDeviceData(value, true); m_listener->notifyDeviceData(value, true);
} }
return RESULT_OK; return RESULT_OK;
} }
result_t SerialDevice::open() { result_t SerialDevice::open() {
if (m_fd != -1) { if (m_fd != -1) {
close(); close();
} }
struct termios newSettings; struct termios newSettings;
// open file descriptor // open file descriptor
m_fd = ::open(m_name, O_RDWR | O_NOCTTY); m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
if (m_fd < 0) { if (m_fd < 0) {
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
} }
if (isatty(m_fd) == 0) { if (isatty(m_fd) == 0) {
close(); close();
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
} }
if (flock(m_fd, LOCK_EX|LOCK_NB)) { if (flock(m_fd, LOCK_EX|LOCK_NB)) {
close(); close();
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
// save current settings // save current settings
tcgetattr(m_fd, &m_oldSettings); tcgetattr(m_fd, &m_oldSettings);
// create new settings // create new settings
memset(&newSettings, '\0', sizeof(newSettings)); memset(&newSettings, '\0', sizeof(newSettings));
newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD); newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors newSettings.c_iflag |= IGNPAR; // ignore parity errors
newSettings.c_oflag &= ~OPOST; newSettings.c_oflag &= ~OPOST;
// non-canonical mode: read() blocks until at least one byte is available // non-canonical mode: read() blocks until at least one byte is available
newSettings.c_cc[VMIN] = 1; newSettings.c_cc[VMIN] = 1;
newSettings.c_cc[VTIME] = 0; newSettings.c_cc[VTIME] = 0;
// empty device buffer // empty device buffer
tcflush(m_fd, TCIFLUSH); tcflush(m_fd, TCIFLUSH);
// activate new settings of serial device // activate new settings of serial device
tcsetattr(m_fd, TCSAFLUSH, &newSettings); tcsetattr(m_fd, TCSAFLUSH, &newSettings);
// set serial device into blocking mode // set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
if (m_initialSend && write(ESC) != 1) { if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND; return RESULT_ERR_SEND;
} }
return RESULT_OK; return RESULT_OK;
} }
void SerialDevice::close() { void SerialDevice::close() {
if (m_fd != -1) { if (m_fd != -1) {
// empty device buffer // empty device buffer
tcflush(m_fd, TCIOFLUSH); tcflush(m_fd, TCIOFLUSH);
// restore previous settings of the device // restore previous settings of the device
tcsetattr(m_fd, TCSANOW, &m_oldSettings); tcsetattr(m_fd, TCSANOW, &m_oldSettings);
} }
Device::close(); Device::close();
} }
void SerialDevice::checkDevice() { void SerialDevice::checkDevice() {
int port; int port;
if (ioctl(m_fd, TIOCMGET, &port) == -1) { if (ioctl(m_fd, TIOCMGET, &port) == -1) {
close(); close();
} }
} }
result_t NetworkDevice::open() { result_t NetworkDevice::open() {
if (m_fd != -1) { if (m_fd != -1) {
close(); close();
} }
m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0); m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
if (m_fd < 0) { if (m_fd < 0) {
return RESULT_ERR_GENERIC_IO; return RESULT_ERR_GENERIC_IO;
} }
int ret; int ret;
if (m_udp) { if (m_udp) {
struct sockaddr_in address = m_address; struct sockaddr_in address = m_address;
address.sin_addr.s_addr = INADDR_ANY; address.sin_addr.s_addr = INADDR_ANY;
ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address)); ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address));
} else { } else {
int value = 1; int value = 1;
ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value)); ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
value = 1; value = 1;
setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value)); setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value));
} }
if (ret == 0) { if (ret == 0) {
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address)); ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
} }
if (ret < 0) { if (ret < 0) {
close(); close();
return RESULT_ERR_GENERIC_IO; return RESULT_ERR_GENERIC_IO;
} }
int cnt; int cnt;
if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) { if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) {
// skip buffered input // skip buffered input
unsigned char buf[256]; unsigned char buf[256];
while (::read(m_fd, &buf, 256) > 0) { while (::read(m_fd, &buf, 256) > 0) {
} }
} }
if (m_bufSize == 0) { if (m_bufSize == 0) {
m_bufSize = MAX_LEN+1; m_bufSize = MAX_LEN+1;
m_buffer = (unsigned char*)malloc(m_bufSize); m_buffer = (unsigned char*)malloc(m_bufSize);
if (!m_buffer) { if (!m_buffer) {
m_bufSize = 0; m_bufSize = 0;
} }
} }
m_bufLen = 0; m_bufLen = 0;
if (m_initialSend && write(ESC) != 1) { if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND; return RESULT_ERR_SEND;
} }
return RESULT_OK; return RESULT_OK;
} }
void NetworkDevice::checkDevice() { void NetworkDevice::checkDevice() {
unsigned char value; unsigned char value;
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT); ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
if (c == 0 || (c < 0 && errno != EAGAIN)) { if (c == 0 || (c < 0 && errno != EAGAIN)) {
m_bufLen = 0; // flush read buffer m_bufLen = 0; // flush read buffer
close(); close();
} }
} }
bool NetworkDevice::available() { bool NetworkDevice::available() {
return m_buffer && m_bufLen > 0; return m_buffer && m_bufLen > 0;
} }
ssize_t NetworkDevice::write(const unsigned char value) { ssize_t NetworkDevice::write(const unsigned char value) {
m_bufLen = 0; // flush read buffer m_bufLen = 0; // flush read buffer
return Device::write(value); return Device::write(value);
} }
ssize_t NetworkDevice::read(unsigned char& value) { ssize_t NetworkDevice::read(unsigned char& value) {
if (available()) { if (available()) {
value = m_buffer[m_bufPos]; value = m_buffer[m_bufPos];
m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize); m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize);
m_bufLen--; m_bufLen--;
return 1; return 1;
} }
if (m_bufSize > 0) { if (m_bufSize > 0) {
ssize_t size = ::read(m_fd, m_buffer, m_bufSize); ssize_t size = ::read(m_fd, m_buffer, m_bufSize);
if (size <= 0) { if (size <= 0) {
return size; return size;
} }
value = m_buffer[0]; value = m_buffer[0];
m_bufPos = 1; m_bufPos = 1;
m_bufLen = (unsigned char)(size-1); m_bufLen = (unsigned char)(size-1);
return size; return size;
} }
return Device::read(value); return Device::read(value);
} }
} // namespace ebusd } // namespace ebusd
+177 -177
View File
@@ -42,18 +42,18 @@ namespace ebusd {
* Interface for listening to data received on/sent to a device. * Interface for listening to data received on/sent to a device.
*/ */
class DeviceListener { class DeviceListener {
public: public:
/** /**
* Destructor. * Destructor.
*/ */
virtual ~DeviceListener() {} virtual ~DeviceListener() {}
/** /**
* Listener method that is called when a data byte was received/sent. * Listener method that is called when a data byte was received/sent.
* @param byte the data byte received/sent. * @param byte the data byte received/sent.
* @param received @a true on reception, @a false on sending. * @param received @a true on reception, @a false on sending.
*/ */
virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract
}; };
@@ -61,227 +61,227 @@ class DeviceListener {
* The base class for accessing an eBUS. * The base class for accessing an eBUS.
*/ */
class Device { class Device {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
*/ */
Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1), : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
m_listener(NULL) {} m_listener(NULL) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~Device(); virtual ~Device();
/** /**
* Factory method for creating a new instance. * Factory method for creating a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @return the new @a Device, or NULL on error. * @return the new @a Device, or NULL on error.
* Note: the caller needs to free the created instance. * Note: the caller needs to free the created instance.
*/ */
static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false); static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false);
/** /**
* Get the transfer latency of this device. * Get the transfer latency of this device.
* @return the transfer latency in microseconds. * @return the transfer latency in microseconds.
*/ */
virtual unsigned int getLatency() const { return 0; } virtual unsigned int getLatency() const { return 0; }
/** /**
* Open the file descriptor. * Open the file descriptor.
* @return the @a result_t code. * @return the @a result_t code.
*/ */
virtual result_t open() = 0; // abstract virtual result_t open() = 0; // abstract
/** /**
* Close the file descriptor if opened. * Close the file descriptor if opened.
*/ */
virtual void close(); virtual void close();
/** /**
* Write a single byte to the device. * Write a single byte to the device.
* @param value the byte value to write. * @param value the byte value to write.
* @return the @a result_t code. * @return the @a result_t code.
*/ */
result_t send(const unsigned char value); result_t send(const unsigned char value);
/** /**
* Read a single byte from the device. * Read a single byte from the device.
* @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite. * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
* @param value the reference in which the received byte value is stored. * @param value the reference in which the received byte value is stored.
* @return the result_t code. * @return the result_t code.
*/ */
result_t recv(const unsigned int timeout, unsigned char& value); result_t recv(const unsigned int timeout, unsigned char& value);
/** /**
* Return the device name. * Return the device name.
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
*/ */
const char* getName() { return m_name; } const char* getName() { return m_name; }
/** /**
* Return whether the device is opened and available. * Return whether the device is opened and available.
* @return whether the device is opened and available. * @return whether the device is opened and available.
*/ */
bool isValid(); bool isValid();
/** /**
* Return whether to allow read access to the device only. * Return whether to allow read access to the device only.
* @return whether to allow read access to the device only. * @return whether to allow read access to the device only.
*/ */
bool isReadOnly() const { return m_readOnly; } bool isReadOnly() const { return m_readOnly; }
/** /**
* Set the @a DeviceListener. * Set the @a DeviceListener.
* @param listener the @a DeviceListener. * @param listener the @a DeviceListener.
*/ */
void setListener(DeviceListener* listener) { m_listener = listener; } void setListener(DeviceListener* listener) { m_listener = listener; }
protected: protected:
/** /**
* Check if the device is still available and close it if not. * Check if the device is still available and close it if not.
*/ */
virtual void checkDevice() = 0; // abstract virtual void checkDevice() = 0; // abstract
/** /**
* Check whether a byte is available immediately (without waiting). * Check whether a byte is available immediately (without waiting).
* @return true when a a byte is available immediately. * @return true when a a byte is available immediately.
*/ */
virtual bool available() { return false; } virtual bool available() { return false; }
/** /**
* Write a single byte. * Write a single byte.
* @param value the byte value to write. * @param value the byte value to write.
* @return the number of bytes written, or -1 on error. * @return the number of bytes written, or -1 on error.
*/ */
virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); } virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); }
/** /**
* Read a single byte. * Read a single byte.
* @param value the reference in which the read byte value is stored. * @param value the reference in which the read byte value is stored.
* @return the number of bytes read, or -1 on error. * @return the number of bytes read, or -1 on error.
*/ */
virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); } virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); }
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
const char* m_name; const char* m_name;
/** whether to regularly check the device availability (only for serial devices). */ /** whether to regularly check the device availability (only for serial devices). */
const bool m_checkDevice; const bool m_checkDevice;
/** whether to allow read access to the device only. */ /** whether to allow read access to the device only. */
const bool m_readOnly; const bool m_readOnly;
/** whether to send an initial @a ESC symbol in @a open(). */ /** whether to send an initial @a ESC symbol in @a open(). */
const bool m_initialSend; const bool m_initialSend;
/** the opened file descriptor, or -1. */ /** the opened file descriptor, or -1. */
int m_fd; int m_fd;
private: private:
/** the @a DeviceListener, or NULL. */ /** the @a DeviceListener, or NULL. */
DeviceListener* m_listener; DeviceListener* m_listener;
}; };
/** /**
* The @a Device for directly connected serial interfaces (tty). * The @a Device for directly connected serial interfaces (tty).
*/ */
class SerialDevice : public Device { class SerialDevice : public Device {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
*/ */
SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
: Device(name, checkDevice, readOnly, initialSend) {} : Device(name, checkDevice, readOnly, initialSend) {}
// @copydoc // @copydoc
virtual result_t open(); virtual result_t open();
// @copydoc // @copydoc
virtual void close(); virtual void close();
protected: protected:
// @copydoc // @copydoc
virtual void checkDevice(); virtual void checkDevice();
private: private:
/** the previous settings of the device for restoring. */ /** the previous settings of the device for restoring. */
termios m_oldSettings; termios m_oldSettings;
}; };
/** /**
* The @a Device for remote network interfaces. * The @a Device for remote network interfaces.
*/ */
class NetworkDevice : public Device { class NetworkDevice : public Device {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param address the socket address of the device. * @param address the socket address of the device.
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param udp true for UDP, false to TCP. * @param udp true for UDP, false to TCP.
*/ */
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend, NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
const bool udp) const bool udp)
: Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp), : Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp),
m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {} m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
// @copydoc // @copydoc
virtual unsigned int getLatency() const { return 10000; } virtual unsigned int getLatency() const { return 10000; }
// @copydoc // @copydoc
virtual result_t open(); virtual result_t open();
protected: protected:
// @copydoc // @copydoc
virtual void checkDevice(); virtual void checkDevice();
// @copydoc // @copydoc
virtual bool available(); virtual bool available();
// @copydoc // @copydoc
virtual ssize_t write(const unsigned char value); virtual ssize_t write(const unsigned char value);
// @copydoc // @copydoc
virtual ssize_t read(unsigned char& value); virtual ssize_t read(unsigned char& value);
private: private:
/** the socket address of the device. */ /** the socket address of the device. */
const struct sockaddr_in m_address; const struct sockaddr_in m_address;
/** true for UDP, false to TCP. */ /** true for UDP, false to TCP. */
const bool m_udp; const bool m_udp;
/** the buffer memory, or NULL. */ /** the buffer memory, or NULL. */
unsigned char* m_buffer; unsigned char* m_buffer;
/** the buffer size. */ /** the buffer size. */
unsigned char m_bufSize; unsigned char m_bufSize;
/** the buffer fill length. */ /** the buffer fill length. */
unsigned char m_bufLen; unsigned char m_bufLen;
/** the buffer read position. */ /** the buffer read position. */
unsigned char m_bufPos; unsigned char m_bufPos;
}; };
} // namespace ebusd } // namespace ebusd
+298 -298
View File
@@ -70,319 +70,319 @@ extern unsigned int parseInt(const char* str, int base, const unsigned int minVa
* An abstract class that support reading definitions from a file. * An abstract class that support reading definitions from a file.
*/ */
class FileReader { class FileReader {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
*/ */
explicit FileReader(bool supportsDefaults) explicit FileReader(bool supportsDefaults)
: m_supportsDefaults(supportsDefaults) {} : m_supportsDefaults(supportsDefaults) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~FileReader() {} virtual ~FileReader() {}
/** /**
* Read the definitions from a file. * Read the definitions from a file.
* @param filename the name of the file being read. * @param filename the name of the file being read.
* @param verbose whether to verbosely log problems. * @param verbose whether to verbosely log problems.
* @param defaultDest the default destination address (may be overwritten by file name), or empty. * @param defaultDest the default destination address (may be overwritten by file name), or empty.
* @param defaultCircuit the default circuit name (may be overwritten by file name), or empty. * @param defaultCircuit the default circuit name (may be overwritten by file name), or empty.
* @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty. * @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t readFromFile(const string filename, bool verbose = false, virtual result_t readFromFile(const string filename, bool verbose = false,
string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") { string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") {
ifstream ifs; ifstream ifs;
ifs.open(filename.c_str(), ifstream::in); ifs.open(filename.c_str(), ifstream::in);
if (!ifs.is_open()) { if (!ifs.is_open()) {
m_lastError = filename; m_lastError = filename;
return RESULT_ERR_NOTFOUND; return RESULT_ERR_NOTFOUND;
} }
size_t lastSep = filename.find_last_of('/'); size_t lastSep = filename.find_last_of('/');
if (lastSep != string::npos) { // potential destination address, matches "^ZZ." if (lastSep != string::npos) { // potential destination address, matches "^ZZ."
// extract defaultDest, defaultCircuit, defaultSuffix from filename: // extract defaultDest, defaultCircuit, defaultSuffix from filename:
// ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv // ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv
unsigned char checkDest; unsigned char checkDest;
string checkIdent, useCircuit, useSuffix; string checkIdent, useCircuit, useSuffix;
unsigned int checkSw, checkHw; unsigned int checkSw, checkHw;
if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) { if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) {
defaultDest = filename.substr(lastSep+1, 2); defaultDest = filename.substr(lastSep+1, 2);
if (!useCircuit.empty()) { if (!useCircuit.empty()) {
defaultCircuit = useCircuit; defaultCircuit = useCircuit;
} }
if (!useSuffix.empty()) { if (!useSuffix.empty()) {
defaultSuffix = useSuffix; defaultSuffix = useSuffix;
} }
} }
} }
unsigned int lineNo = 0; unsigned int lineNo = 0;
vector<string> row; vector<string> row;
vector< vector<string> > defaults; vector< vector<string> > defaults;
while (splitFields(ifs, row, lineNo)) { while (splitFields(ifs, row, lineNo)) {
if (row.empty()) { if (row.empty()) {
continue; continue;
} }
result_t result; result_t result;
vector<string>::iterator it = row.begin(); vector<string>::iterator it = row.begin();
const vector<string>::iterator end = row.end(); const vector<string>::iterator end = row.end();
if (m_supportsDefaults) { if (m_supportsDefaults) {
if (row[0][0] == '*') { if (row[0][0] == '*') {
row[0] = row[0].substr(1); row[0] = row[0].substr(1);
result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
if (result == RESULT_OK) { if (result == RESULT_OK) {
continue; continue;
} }
} else { } else {
result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
} }
} else { } else {
result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo); result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
if (!verbose) { if (!verbose) {
ifs.close(); ifs.close();
ostringstream error; ostringstream error;
error << filename << ":" << static_cast<unsigned>(lineNo); error << filename << ":" << static_cast<unsigned>(lineNo);
if (m_lastError.length() > 0) { if (m_lastError.length() > 0) {
error << ": " << m_lastError; error << ": " << m_lastError;
} }
m_lastError = error.str(); m_lastError = error.str();
return result; return result;
} }
if (m_lastError.length() > 0) { if (m_lastError.length() > 0) {
cout << m_lastError << endl; cout << m_lastError << endl;
} }
printErrorPos(cout, row.begin(), end, it, filename, lineNo, result); printErrorPos(cout, row.begin(), end, it, filename, lineNo, result);
} else if (!verbose) { } else if (!verbose) {
m_lastError = ""; m_lastError = "";
} }
} }
ifs.close(); ifs.close();
return RESULT_OK; return RESULT_OK;
} }
/** /**
* Return a @a string describing the last error position. * Return a @a string describing the last error position.
* @return a @a string describing the last error position. * @return a @a string describing the last error position.
*/ */
virtual string getLastError() { return m_lastError; } virtual string getLastError() { return m_lastError; }
/** /**
* Add a default row that was read from a file. * Add a default row that was read from a file.
* @param defaults the list to add the default row to. * @param defaults the list to add the default row to.
* @param row the default row (initial star char removed). * @param row the default row (initial star char removed).
* @param begin an iterator to the first column of the default row to read (for error reporting). * @param begin an iterator to the first column of the default row to read (for error reporting).
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
* @param filename the name of the file being read. * @param filename the name of the file being read.
* @param lineNo the current line number in the file being read. * @param lineNo the current line number in the file being read.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row, virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix, vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
const string& filename, unsigned int lineNo) { const string& filename, unsigned int lineNo) {
defaults.push_back(row); defaults.push_back(row);
begin = row.end(); begin = row.end();
return RESULT_OK; return RESULT_OK;
} }
/** /**
* Add a definition that was read from a file. * Add a definition that was read from a file.
* @param begin an iterator to the first column of the definition row to read. * @param begin an iterator to the first column of the definition row to read.
* @param end the end iterator of the definition row to read. * @param end the end iterator of the definition row to read.
* @param defaults all previously read default rows (initial star char removed), or NULL if not supported. * @param defaults all previously read default rows (initial star char removed), or NULL if not supported.
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty. * @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty. * @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty. * @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
* @param filename the name of the file being read. * @param filename the name of the file being read.
* @param lineNo the current line number in the file being read. * @param lineNo the current line number in the file being read.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end, virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end,
vector< vector<string> >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix, vector< vector<string> >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix,
const string& filename, unsigned int lineNo) = 0; const string& filename, unsigned int lineNo) = 0;
/** /**
* Left and right trim the string. * Left and right trim the string.
* @param str the @a string to trim. * @param str the @a string to trim.
*/ */
static void trim(string& str) { static void trim(string& str) {
size_t pos = str.find_first_not_of(" \t"); size_t pos = str.find_first_not_of(" \t");
if (pos != string::npos) { if (pos != string::npos) {
str.erase(0, pos); str.erase(0, pos);
} }
pos = str.find_last_not_of(" \t"); pos = str.find_last_not_of(" \t");
if (pos != string::npos) { if (pos != string::npos) {
str.erase(pos+1); str.erase(pos+1);
} }
} }
/** /**
* Convert all upper case characters in the string to lower case. * Convert all upper case characters in the string to lower case.
* @param str the @a string to convert. * @param str the @a string to convert.
*/ */
static void tolower(string& str) { static void tolower(string& str) {
transform(str.begin(), str.end(), str.begin(), ::tolower); transform(str.begin(), str.end(), str.begin(), ::tolower);
} }
/** /**
* Split the next line(s) from the @a istring into fields. * Split the next line(s) from the @a istring into fields.
* @param ifs the @a istream to read from. * @param ifs the @a istream to read from.
* @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines. * @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines.
* @param lineNo the current line number (incremented with each line read). * @param lineNo the current line number (incremented with each line read).
* @return true if there are more lines to read, false when there are no more lines left. * @return true if there are more lines to read, false when there are no more lines left.
*/ */
static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) { static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) {
row.clear(); row.clear();
string line; string line;
bool quotedText = false, wasQuoted = false; bool quotedText = false, wasQuoted = false;
ostringstream field; ostringstream field;
char prev = FIELD_SEPARATOR; char prev = FIELD_SEPARATOR;
bool empty = true, read = false; bool empty = true, read = false;
while (getline(ifs, line)) { while (getline(ifs, line)) {
read = true; read = true;
lineNo++; lineNo++;
trim(line); trim(line);
size_t length = line.length(); size_t length = line.length();
if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) { if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) {
continue; // skip empty lines and comments continue; // skip empty lines and comments
} }
for (size_t pos = 0; pos < length; pos++) { for (size_t pos = 0; pos < length; pos++) {
char ch = line[pos]; char ch = line[pos];
switch (ch) { switch (ch) {
case FIELD_SEPARATOR: case FIELD_SEPARATOR:
if (quotedText) { if (quotedText) {
field << ch; field << ch;
} else { } else {
string str = field.str(); string str = field.str();
trim(str); trim(str);
empty &= str.empty(); empty &= str.empty();
row.push_back(str); row.push_back(str);
field.str(""); field.str("");
wasQuoted = false; wasQuoted = false;
} }
break; break;
case TEXT_SEPARATOR: case TEXT_SEPARATOR:
if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote
field << ch; field << ch;
quotedText = true; quotedText = true;
} else if (quotedText) { } else if (quotedText) {
quotedText = false; quotedText = false;
} else if (prev == FIELD_SEPARATOR) { } else if (prev == FIELD_SEPARATOR) {
quotedText = wasQuoted = true; quotedText = wasQuoted = true;
} else { } else {
field << ch; field << ch;
} }
break; break;
case '\r': case '\r':
break; break;
default: default:
if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) { if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) {
field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text
quotedText = true; quotedText = true;
} else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) { } else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) {
field << VALUE_SEPARATOR; field << VALUE_SEPARATOR;
} }
field << ch; field << ch;
break; break;
} }
prev = ch; prev = ch;
} }
if (!quotedText) { if (!quotedText) {
break; break;
} }
} }
string str = field.str(); string str = field.str();
trim(str); trim(str);
if (empty && str.empty()) { if (empty && str.empty()) {
row.clear(); row.clear();
return read; return read;
} }
row.push_back(str); row.push_back(str);
return true; return true;
} }
/** /**
* Extract default values from the file name. * Extract default values from the file name.
* @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv". * @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv".
* @param dest the output destination address ZZ (hex digits). * @param dest the output destination address ZZ (hex digits).
* @param ident the identification part IDENT (up to 5 characters, set to empty if not present). * @param ident the identification part IDENT (up to 5 characters, set to empty if not present).
* @param circuit the circuit part CIRCUIT (set to IDENT if not present). * @param circuit the circuit part CIRCUIT (set to IDENT if not present).
* @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present). * @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present).
* @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present). * @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present).
* @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present). * @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present).
* @return true if at least the address and the identification part were extracted, false otherwise. * @return true if at least the address and the identification part were extracted, false otherwise.
*/ */
static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit, static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit,
string& suffix, unsigned int& software, unsigned int& hardware) { string& suffix, unsigned int& software, unsigned int& hardware) {
ident = circuit = suffix = ""; ident = circuit = suffix = "";
software = hardware = UINT_MAX; software = hardware = UINT_MAX;
if (name.length() > 4 && name.substr(name.length()-4) == ".csv") { if (name.length() > 4 && name.substr(name.length()-4) == ".csv") {
name = name.substr(0, name.length()-3); // including trailing "." name = name.substr(0, name.length()-3); // including trailing "."
} }
size_t pos = name.find('.'); size_t pos = name.find('.');
if (pos != 2) { if (pos != 2) {
return false; // missing "ZZ." return false; // missing "ZZ."
} }
result_t result = RESULT_OK; result_t result = RESULT_OK;
dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL); dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL);
if (result != RESULT_OK || !isValidAddress(dest)) { if (result != RESULT_OK || !isValidAddress(dest)) {
return false; // invalid "ZZ" return false; // invalid "ZZ"
} }
name.erase(0, pos); name.erase(0, pos);
if (name.length() > 1) { if (name.length() > 1) {
pos = name.rfind(".SW"); // check for ".SWxxxx." pos = name.rfind(".SW"); // check for ".SWxxxx."
if (pos != string::npos && name.find(".", pos+1) == pos+7) { if (pos != string::npos && name.find(".", pos+1) == pos+7) {
software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
if (result != RESULT_OK) { if (result != RESULT_OK) {
return false; // invalid "SWxxxx" return false; // invalid "SWxxxx"
} }
name.erase(pos, 7); name.erase(pos, 7);
} }
} }
if (name.length() > 1) { if (name.length() > 1) {
pos = name.rfind(".HW"); // check for ".HWxxxx." pos = name.rfind(".HW"); // check for ".HWxxxx."
if (pos != string::npos && name.find(".", pos+1) == pos+7) { if (pos != string::npos && name.find(".", pos+1) == pos+7) {
hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL); hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
if (result != RESULT_OK) { if (result != RESULT_OK) {
return false; // invalid "HWxxxx" return false; // invalid "HWxxxx"
} }
name.erase(pos, 7); name.erase(pos, 7);
} }
} }
if (name.length() > 1) { if (name.length() > 1) {
pos = name.find('.', 1); // check for ".IDENT." pos = name.find('.', 1); // check for ".IDENT."
if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".." if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".."
ident = circuit = name.substr(1, pos-1); ident = circuit = name.substr(1, pos-1);
name.erase(0, pos); name.erase(0, pos);
pos = name.find('.', 1); // check for ".CIRCUIT." pos = name.find('.', 1); // check for ".CIRCUIT."
if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) { if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) {
circuit = name.substr(1, pos-1); circuit = name.substr(1, pos-1);
name.erase(0, pos); name.erase(0, pos);
pos = name.find('.', 1); // check for ".SUFFIX." pos = name.find('.', 1); // check for ".SUFFIX."
} }
if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') { if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') {
suffix = name.substr(0, 2); suffix = name.substr(0, 2);
name.erase(0, pos); name.erase(0, pos);
} }
} }
} }
return true; return true;
} }
private: private:
/** whether this instance supports rows with defaults (starting with a star). */ /** whether this instance supports rows with defaults (starting with a star). */
bool m_supportsDefaults; bool m_supportsDefaults;
protected: protected:
/** a @a string describing the last error position. */ /** a @a string describing the last error position. */
string m_lastError; string m_lastError;
}; };
} // namespace ebusd } // namespace ebusd
+1871 -1871
View File
File diff suppressed because it is too large Load Diff
+1080 -1080
View File
File diff suppressed because it is too large Load Diff
+33 -33
View File
@@ -22,40 +22,40 @@
namespace ebusd { namespace ebusd {
const char* getResultCode(result_t resultCode) { const char* getResultCode(result_t resultCode) {
switch (resultCode) { switch (resultCode) {
case RESULT_OK: return "done"; case RESULT_OK: return "done";
case RESULT_CONTINUE: return "continue"; case RESULT_CONTINUE: return "continue";
case RESULT_EMPTY: return "empty"; case RESULT_EMPTY: return "empty";
case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error"; case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error";
case RESULT_ERR_DEVICE: return "ERR: generic device error"; case RESULT_ERR_DEVICE: return "ERR: generic device error";
case RESULT_ERR_SEND: return "ERR: send error"; case RESULT_ERR_SEND: return "ERR: send error";
case RESULT_ERR_ESC: return "ERR: invalid escape sequence"; case RESULT_ERR_ESC: return "ERR: invalid escape sequence";
case RESULT_ERR_TIMEOUT: return "ERR: read timeout"; case RESULT_ERR_TIMEOUT: return "ERR: read timeout";
case RESULT_ERR_NOTFOUND: return "ERR: element not found"; case RESULT_ERR_NOTFOUND: return "ERR: element not found";
case RESULT_ERR_EOF: return "ERR: end of input reached"; case RESULT_ERR_EOF: return "ERR: end of input reached";
case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument"; case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument";
case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument"; case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument";
case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address"; case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address";
case RESULT_ERR_INVALID_POS: return "ERR: invalid position"; 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_OUT_OF_RANGE: return "ERR: argument value out of valid range";
case RESULT_ERR_INVALID_PART: return "ERR: invalid part type"; case RESULT_ERR_INVALID_PART: return "ERR: invalid part type";
case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type"; case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type";
case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list"; case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list";
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry"; case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name"; case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost"; case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost";
case RESULT_ERR_CRC: return "ERR: CRC error"; case RESULT_ERR_CRC: return "ERR: CRC error";
case RESULT_ERR_ACK: return "ERR: ACK error"; case RESULT_ERR_ACK: return "ERR: ACK error";
case RESULT_ERR_NAK: return "ERR: NAK received"; case RESULT_ERR_NAK: return "ERR: NAK received";
case RESULT_ERR_NO_SIGNAL: return "ERR: no signal"; case RESULT_ERR_NO_SIGNAL: return "ERR: no signal";
case RESULT_ERR_SYN: return "ERR: SYN received"; case RESULT_ERR_SYN: return "ERR: SYN received";
default: default:
if (resultCode >= 0) { if (resultCode >= 0) {
return "done: unknown result code"; return "done: unknown result code";
} }
return "ERR: unknown result code"; return "ERR: unknown result code";
} }
} }
} // namespace ebusd } // namespace ebusd
+26 -26
View File
@@ -31,37 +31,37 @@ namespace ebusd {
/** type for result code. */ /** type for result code. */
enum result_t { enum result_t {
RESULT_OK = 0, //!< success RESULT_OK = 0, //!< success
RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received) RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received)
RESULT_EMPTY = 2, //!< empty result RESULT_EMPTY = 2, //!< empty result
RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal) RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal)
RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal) RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal)
RESULT_ERR_SEND = -3, //!< send error RESULT_ERR_SEND = -3, //!< send error
RESULT_ERR_ESC = -4, //!< invalid escape sequence RESULT_ERR_ESC = -4, //!< invalid escape sequence
RESULT_ERR_TIMEOUT = -5, //!< read timeout RESULT_ERR_TIMEOUT = -5, //!< read timeout
RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable
RESULT_ERR_EOF = -7, //!< end of input reached RESULT_ERR_EOF = -7, //!< end of input reached
RESULT_ERR_INVALID_ARG = -8, //!< invalid argument RESULT_ERR_INVALID_ARG = -8, //!< invalid argument
RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument
RESULT_ERR_INVALID_ADDR = -10, //!< invalid address RESULT_ERR_INVALID_ADDR = -10, //!< invalid address
RESULT_ERR_INVALID_POS = -11, //!< invalid position RESULT_ERR_INVALID_POS = -11, //!< invalid position
RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range
RESULT_ERR_INVALID_PART = -13, //!< invalid part type value RESULT_ERR_INVALID_PART = -13, //!< invalid part type value
RESULT_ERR_MISSING_TYPE = -14, //!< missing data type RESULT_ERR_MISSING_TYPE = -14, //!< missing data type
RESULT_ERR_INVALID_LIST = -15, //!< invalid value list RESULT_ERR_INVALID_LIST = -15, //!< invalid value list
RESULT_ERR_DUPLICATE = -16, //!< duplicate entry RESULT_ERR_DUPLICATE = -16, //!< duplicate entry
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name) RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
RESULT_ERR_CRC = -19, //!< CRC error RESULT_ERR_CRC = -19, //!< CRC error
RESULT_ERR_ACK = -20, //!< ACK error RESULT_ERR_ACK = -20, //!< ACK error
RESULT_ERR_NAK = -21, //!< NAK received RESULT_ERR_NAK = -21, //!< NAK received
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
RESULT_ERR_SYN = -23, //!< SYN received instead of answer RESULT_ERR_SYN = -23, //!< SYN received instead of answer
}; };
+178 -178
View File
@@ -35,160 +35,160 @@ using std::setfill;
* CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1. * CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1.
*/ */
static const unsigned char CRC_LOOKUP_TABLE[] = { static const unsigned char CRC_LOOKUP_TABLE[] = {
0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee, 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc, 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a, 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8, 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26, 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14, 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42, 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70, 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5, 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7, 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81, 0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3, 0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d, 0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f, 0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49, 0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b, 0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
}; };
void SymbolString::addAll(const SymbolString& str, bool skipLastSymbol) { void SymbolString::addAll(const SymbolString& str, bool skipLastSymbol) {
bool addCrc = m_unescapeState == 0; bool addCrc = m_unescapeState == 0;
bool isEscaped = str.m_unescapeState == 0; bool isEscaped = str.m_unescapeState == 0;
vector<unsigned char> data = str.m_data; vector<unsigned char> data = str.m_data;
size_t end = data.size(); size_t end = data.size();
if (end > 0 && skipLastSymbol) { if (end > 0 && skipLastSymbol) {
end--; end--;
} }
for (size_t i = 0; i < end; i++) { for (size_t i = 0; i < end; i++) {
push_back(data[i], isEscaped, addCrc); push_back(data[i], isEscaped, addCrc);
} }
if (addCrc) { if (addCrc) {
push_back(m_crc, false, false); // add CRC push_back(m_crc, false, false); // add CRC
} }
} }
result_t SymbolString::parseHex(const string& str, const bool isEscaped) { result_t SymbolString::parseHex(const string& str, const bool isEscaped) {
bool addCrc = m_unescapeState == 0; bool addCrc = m_unescapeState == 0;
for (size_t i = 0; i < str.size(); i += 2) { for (size_t i = 0; i < str.size(); i += 2) {
char* strEnd = NULL; char* strEnd = NULL;
const char* strBegin = str.substr(i, 2).c_str(); const char* strBegin = str.substr(i, 2).c_str();
unsigned long value = strtoul(strBegin, &strEnd, 16); unsigned long value = strtoul(strBegin, &strEnd, 16);
if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) { if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) {
return RESULT_ERR_INVALID_NUM; // invalid value return RESULT_ERR_INVALID_NUM; // invalid value
} }
push_back((unsigned char)value, isEscaped, addCrc); push_back((unsigned char)value, isEscaped, addCrc);
} }
if (addCrc) { if (addCrc) {
push_back(m_crc, false, false); // add CRC push_back(m_crc, false, false); // add CRC
} }
return RESULT_OK; return RESULT_OK;
} }
const string SymbolString::getDataStr(const bool unescape, const bool skipLastSymbol) { const string SymbolString::getDataStr(const bool unescape, const bool skipLastSymbol) {
ostringstream sstr; ostringstream sstr;
bool previousEscape = false; bool previousEscape = false;
for (size_t i = 0; i < m_data.size(); i++) { for (size_t i = 0; i < m_data.size(); i++) {
unsigned char value = m_data[i]; unsigned char value = m_data[i];
if (m_unescapeState == 0 && unescape && previousEscape) { if (m_unescapeState == 0 && unescape && previousEscape) {
if (!skipLastSymbol || i+1 < m_data.size()) { if (!skipLastSymbol || i+1 < m_data.size()) {
if (value == 0x00) { if (value == 0x00) {
sstr << "a9"; // ESC sstr << "a9"; // ESC
} else if (value == 0x01) { } else if (value == 0x01) {
sstr << "aa"; // SYN sstr << "aa"; // SYN
} else { } else {
sstr << "XX"; // invalid escape sequence sstr << "XX"; // invalid escape sequence
} }
} }
previousEscape = false; previousEscape = false;
} else if (m_unescapeState == 0 && unescape && value == ESC) { } else if (m_unescapeState == 0 && unescape && value == ESC) {
previousEscape = true; // escape sequence not yet finished previousEscape = true; // escape sequence not yet finished
} else if (!skipLastSymbol || i+1 < m_data.size()) { } else if (!skipLastSymbol || i+1 < m_data.size()) {
sstr << nouppercase << setw(2) << hex sstr << nouppercase << setw(2) << hex
<< setfill('0') << static_cast<unsigned>(value); << setfill('0') << static_cast<unsigned>(value);
} }
} }
return sstr.str(); return sstr.str();
} }
result_t 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 (m_unescapeState == 0) { // store escaped data
if (!isEscaped && value == ESC) { if (!isEscaped && value == ESC) {
m_data.push_back(ESC); m_data.push_back(ESC);
m_data.push_back(0x00); m_data.push_back(0x00);
if (updateCRC) { if (updateCRC) {
addCRC(ESC); addCRC(ESC);
addCRC(0x00); addCRC(0x00);
} }
} else if (!isEscaped && value == SYN) { } else if (!isEscaped && value == SYN) {
m_data.push_back(ESC); m_data.push_back(ESC);
m_data.push_back(0x01); m_data.push_back(0x01);
if (updateCRC) { if (updateCRC) {
addCRC(ESC); addCRC(ESC);
addCRC(0x01); addCRC(0x01);
} }
} else { } else {
m_data.push_back(value); m_data.push_back(value);
if (updateCRC) { if (updateCRC) {
addCRC(value); addCRC(value);
} }
} }
return RESULT_OK; return RESULT_OK;
} }
if (!isEscaped) { if (!isEscaped) {
if (m_unescapeState != 1) { if (m_unescapeState != 1) {
return RESULT_ERR_ESC; // invalid unescape state return RESULT_ERR_ESC; // invalid unescape state
} }
m_data.push_back(value); m_data.push_back(value);
if (updateCRC) { if (updateCRC) {
if (value == ESC) { if (value == ESC) {
addCRC(ESC); addCRC(ESC);
addCRC(0x00); addCRC(0x00);
} else if (value == SYN) { } else if (value == SYN) {
addCRC(ESC); addCRC(ESC);
addCRC(0x01); addCRC(0x01);
} else { } else {
addCRC(value); addCRC(value);
} }
} }
return RESULT_OK; return RESULT_OK;
} }
if (m_unescapeState != 1) { if (m_unescapeState != 1) {
if (updateCRC) { if (updateCRC) {
addCRC(value); addCRC(value);
} }
if (value == 0x00) { if (value == 0x00) {
m_data.push_back(ESC); m_data.push_back(ESC);
m_unescapeState = 1; m_unescapeState = 1;
return RESULT_OK; return RESULT_OK;
} }
if (value == 0x01) { if (value == 0x01) {
m_data.push_back(SYN); m_data.push_back(SYN);
m_unescapeState = 1; m_unescapeState = 1;
return RESULT_OK; return RESULT_OK;
} }
return RESULT_ERR_ESC; // invalid escape sequence return RESULT_ERR_ESC; // invalid escape sequence
} }
if (value == ESC) { if (value == ESC) {
if (updateCRC) { if (updateCRC) {
addCRC(value); addCRC(value);
} }
m_unescapeState = 2; m_unescapeState = 2;
return RESULT_CONTINUE; return RESULT_CONTINUE;
} }
if (updateCRC) { if (updateCRC) {
addCRC(value); addCRC(value);
} }
m_data.push_back(value); m_data.push_back(value);
return RESULT_OK; return RESULT_OK;
} }
void SymbolString::addCRC(const unsigned char value) { void SymbolString::addCRC(const unsigned char value) {
m_crc = CRC_LOOKUP_TABLE[m_crc]^value; m_crc = CRC_LOOKUP_TABLE[m_crc]^value;
} }
@@ -199,66 +199,66 @@ void SymbolString::addCRC(const unsigned char value) {
* @return the 1-based index of the upper or lower 4 bits of a master address (1 to 5), or 0. * @return the 1-based index of the upper or lower 4 bits of a master address (1 to 5), or 0.
*/ */
unsigned char getMasterPartIndex(unsigned char bits) { unsigned char getMasterPartIndex(unsigned char bits) {
switch (bits) { switch (bits) {
case 0x0: case 0x0:
return 1; return 1;
case 0x1: case 0x1:
return 2; return 2;
case 0x3: case 0x3:
return 3; return 3;
case 0x7: case 0x7:
return 4; return 4;
case 0xF: case 0xF:
return 5; return 5;
default: default:
return 0; return 0;
} }
} }
bool isMaster(unsigned char addr) { bool isMaster(unsigned char addr) {
return getMasterPartIndex(addr & 0x0F) > 0 return getMasterPartIndex(addr & 0x0F) > 0
&& getMasterPartIndex((addr & 0xF0)>>4) > 0; && getMasterPartIndex((addr & 0xF0)>>4) > 0;
} }
bool isSlaveMaster(unsigned char addr) { bool isSlaveMaster(unsigned char addr) {
return isMaster((unsigned char)(addr+256-5)); return isMaster((unsigned char)(addr+256-5));
} }
unsigned char getSlaveAddress(unsigned char addr) { unsigned char getSlaveAddress(unsigned char addr) {
if (isMaster(addr)) { if (isMaster(addr)) {
return (unsigned char)(addr+5); return (unsigned char)(addr+5);
} }
if (isValidAddress(addr, false)) { if (isValidAddress(addr, false)) {
return addr; return addr;
} }
return SYN; return SYN;
} }
unsigned char getMasterAddress(unsigned char addr) { unsigned char getMasterAddress(unsigned char addr) {
if (isMaster(addr)) { if (isMaster(addr)) {
return addr; return addr;
} }
addr = (unsigned char)(addr+256-5); addr = (unsigned char)(addr+256-5);
if (isMaster(addr)) { if (isMaster(addr)) {
return addr; return addr;
} }
return SYN; return SYN;
} }
unsigned char getMasterNumber(unsigned char addr) { unsigned char getMasterNumber(unsigned char addr) {
unsigned char priority = getMasterPartIndex(addr & 0x0F); unsigned char priority = getMasterPartIndex(addr & 0x0F);
if (priority == 0) { if (priority == 0) {
return 0; return 0;
} }
unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4); unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4);
if (index == 0) { if (index == 0) {
return 0; return 0;
} }
return (unsigned char)(5*(priority-1) + index); return (unsigned char)(5*(priority-1) + index);
} }
bool isValidAddress(unsigned char addr, bool allowBroadcast) { bool isValidAddress(unsigned char addr, bool allowBroadcast) {
return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST); return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST);
} }
} // namespace ebusd } // namespace ebusd
+117 -117
View File
@@ -79,141 +79,141 @@ static const unsigned char BROADCAST = 0xFE; //!< the broadcast destination addr
* A string of escaped or unescaped bus symbols. * A string of escaped or unescaped bus symbols.
*/ */
class SymbolString { class SymbolString {
public: public:
/** /**
* Creates a new empty escaped or unescaped instance. * Creates a new empty escaped or unescaped instance.
* @param escaped whether to create an escaped instance. * @param escaped whether to create an escaped instance.
*/ */
explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {} explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {}
/** /**
* Add all symbols from the other @a SymbolString and the calculated CRC if escaped. * Add all symbols from the other @a SymbolString and the calculated CRC if escaped.
* @param str the @a SymbolString to copy from. * @param str the @a SymbolString to copy from.
* @param skipLastSymbol whether to skip the last symbol (probably the CRC). * @param skipLastSymbol whether to skip the last symbol (probably the CRC).
*/ */
void addAll(const SymbolString& str, const bool skipLastSymbol = false); void addAll(const SymbolString& str, const bool skipLastSymbol = false);
/** /**
* Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped. * Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped.
* @param str the hex @a string. * @param str the hex @a string.
* @param isEscaped whether the hex string is escaped. * @param isEscaped whether the hex string is escaped.
* @return @a RESULT_OK on success, or an error code. * @return @a RESULT_OK on success, or an error code.
*/ */
result_t parseHex(const string& str, const bool isEscaped = false); result_t parseHex(const string& str, const bool isEscaped = false);
/** /**
* Return the symbols as hex string. * Return the symbols as hex string.
* @param unescape whether to unescape an escaped instance. * @param unescape whether to unescape an escaped instance.
* @param skipLastSymbol whether to skip the last symbol (probably the CRC). * @param skipLastSymbol whether to skip the last symbol (probably the CRC).
* @return the symbols as hex string. * @return the symbols as hex string.
*/ */
const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true); const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true);
/** /**
* Return a reference to the symbol at the specified index. * Return a reference to the symbol at the specified index.
* @param index the index of the symbol to return. * @param index the index of the symbol to return.
* @return the reference to the symbol at the specified index. * @return the reference to the symbol at the specified index.
*/ */
unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; } unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; }
/** /**
* Return whether this instance is equal to the other instance. * Return whether this instance is equal to the other instance.
* @param other the other instance. * @param other the other instance.
* @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols). * @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; }
/** /**
* Return whether this instance is different from the other instance. * Return whether this instance is different from the other instance.
* @param other the other instance. * @param other the other instance.
* @return true if this instance is different from the other instance. * @return true if this instance is different from the other instance.
*/ */
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; }
/** /**
* Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC). * Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC).
* @param other the other instance. * @param other the other instance.
* @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols), * @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols),
* 1 if this instance is completely different to the other instance, * 1 if this instance is completely different to the other instance,
* 2 if this instance only differs from the other instance in the first byte (the master address). * 2 if this instance only differs from the other instance in the first byte (the master address).
*/ */
int compareMaster(SymbolString& other) { int compareMaster(SymbolString& other) {
if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) { if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) {
return 1; return 1;
} }
if (m_data == other.m_data) { if (m_data == other.m_data) {
return 0; return 0;
} }
if (m_data.size() == 1) { if (m_data.size() == 1) {
return 2; return 2;
} }
if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) { if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) {
return 2; return 2;
} }
return 1; return 1;
} }
/** /**
* Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary. * Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary.
* @param value the symbol to append. * @param value the symbol to append.
* @param isEscaped whether the symbol is escaped. * @param isEscaped whether the symbol is escaped.
* @param updateCRC whether to update the calculated CRC in @a m_crc. * @param updateCRC whether to update the calculated CRC in @a m_crc.
* @return RESULT_OK if another symbol was appended, * @return RESULT_OK if another symbol was appended,
* RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received, * RESULT_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. * RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected.
*/ */
result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true); result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true);
/** /**
* Return the number of symbols in this symbol string. * Return the number of symbols in this symbol string.
* @return the number of available symbols. * @return the number of available symbols.
*/ */
unsigned char size() const { return (unsigned char)m_data.size(); } unsigned char size() const { return (unsigned char)m_data.size(); }
/** /**
* Return the calculated CRC. * Return the calculated CRC.
* @return the calculated CRC. * @return the calculated CRC.
*/ */
unsigned char getCRC() const { return m_crc; } unsigned char getCRC() const { return m_crc; }
/** /**
* Clear the symbols. * Clear the symbols.
*/ */
void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; } void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; }
/** /**
* Clear the symbols and adjust the escape mode. * Clear the symbols and adjust the escape mode.
* @param escape true to set to an escaped instance, false to set to an unescaped instance. * @param escape true to set to an escaped instance, false to set to an unescaped instance.
*/ */
void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; } void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; }
private: private:
/** /**
* Hidden copy constructor. * Hidden copy constructor.
* @param str the @a SymbolString to copy from. * @param str the @a SymbolString to copy from.
*/ */
SymbolString(const SymbolString& str) SymbolString(const SymbolString& str)
: m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {} : m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {}
/** /**
* Update the calculated CRC in @a m_crc by adding a value. * Update the calculated CRC in @a m_crc by adding a value.
* @param value the (escaped) value to add to the calculated CRC in @a m_crc. * @param value the (escaped) value to add to the calculated CRC in @a m_crc.
*/ */
void addCRC(const unsigned char value); void addCRC(const unsigned char value);
/** the string of bus symbols. */ /** the string of bus symbols. */
vector<unsigned char> m_data; vector<unsigned char> m_data;
/** /**
* 0 if the symbols in @a m_data are escaped, * 0 if the symbols in @a m_data are escaped,
* 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol, * 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol,
* 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol. * 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol.
*/ */
int m_unescapeState; int m_unescapeState;
/** the calculated CRC. */ /** the calculated CRC. */
unsigned char m_crc; unsigned char m_crc;
}; };
File diff suppressed because it is too large Load Diff
+29 -29
View File
@@ -24,39 +24,39 @@ using namespace std;
using namespace ebusd; using namespace ebusd;
int main() { int main() {
Device* device = Device::create("/dev/ttyUSB20", true, false, false); Device* device = Device::create("/dev/ttyUSB20", true, false, false);
if (device == NULL) { if (device == NULL) {
cout << "unable to create device" << endl; cout << "unable to create device" << endl;
return -1; return -1;
} }
result_t result = device->open(); result_t result = device->open();
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "open failed: " << getResultCode(result) << endl; cout << "open failed: " << getResultCode(result) << endl;
} else { } else {
if (!device->isValid()) { if (!device->isValid()) {
cout << "device not available." << endl; cout << "device not available." << endl;
} }
int count = 0; int count = 0;
while (1) { while (1) {
unsigned char byte = 0; unsigned char byte = 0;
result = device->recv(0, byte); result = device->recv(0, byte);
if (result == RESULT_OK) { if (result == RESULT_OK) {
cout << hex << setw(2) << setfill('0') cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(byte) << endl; << static_cast<unsigned>(byte) << endl;
} }
count++; count++;
} }
device->close(); device->close();
if (!device->isValid()) { if (!device->isValid()) {
cout << "close successful." << endl; cout << "close successful." << endl;
} }
} }
delete device; delete device;
return 0; return 0;
} }
+61 -61
View File
@@ -28,70 +28,70 @@ using namespace ebusd;
static bool error = false; static bool error = false;
void verify(bool expectFailMatch, string type, string input, void verify(bool expectFailMatch, string type, string input,
bool match, string expectStr, string gotStr) { bool match, string expectStr, string gotStr) {
match = match && expectStr == gotStr; match = match && expectStr == gotStr;
if (expectFailMatch) { if (expectFailMatch) {
if (match) { if (match) {
cout << " failed " << type << " match >" << input cout << " failed " << type << " match >" << input
<< "< error: unexpectedly succeeded" << endl; << "< error: unexpectedly succeeded" << endl;
error = true; error = true;
} else { } else {
cout << " failed " << type << " match >" << input << "< OK" << endl; cout << " failed " << type << " match >" << input << "< OK" << endl;
} }
} else if (match) { } else if (match) {
cout << " " << type << " match >" << input << "< OK" << endl; cout << " " << type << " match >" << input << "< OK" << endl;
} else { } else {
cout << " " << type << " match >" << input << "< error: got >" cout << " " << type << " match >" << input << "< error: got >"
<< gotStr << "<, expected >" << expectStr << "<" << endl; << gotStr << "<, expected >" << expectStr << "<" << endl;
error = true; error = true;
} }
} }
int main() { int main() {
istringstream ifs( istringstream ifs(
"line 1 col 1,line 1 col 2,line 1 col 3\n" "line 1 col 1,line 1 col 2,line 1 col 3\n"
"line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n" "line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n"
"line 4 col 1,\"line 4 col 2 part 1\n" "line 4 col 1,\"line 4 col 2 part 1\n"
"line 4 col 2 part 2\",line 4 col 3\n" "line 4 col 2 part 2\",line 4 col 3\n"
",,,\n" ",,,\n"
"line 6 col 1,,line 6 col 3\n" "line 6 col 1,,line 6 col 3\n"
"line 8 col 1,\"line 8 col 2 part 1;\n" "line 8 col 1,\"line 8 col 2 part 1;\n"
"line 8 col 2 part 2\",line 8 col 3\n" "line 8 col 2 part 2\",line 8 col 3\n"
); );
string resultlines[][3] = { string resultlines[][3] = {
{"line 1 col 1", "line 1 col 2", "line 1 col 3"}, {"line 1 col 1", "line 1 col 2", "line 1 col 3"},
{"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""}, {"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""},
{"", "", ""}, {"", "", ""},
{"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"}, {"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"},
{"", "", ""}, {"", "", ""},
{"line 6 col 1", "", "line 6 col 3"}, {"line 6 col 1", "", "line 6 col 3"},
{"", "", ""}, {"", "", ""},
{"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"}, {"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"},
}; };
unsigned int lineNo = 0; unsigned int lineNo = 0;
vector<string> row; vector<string> row;
while (FileReader::splitFields(ifs, row, lineNo)) { while (FileReader::splitFields(ifs, row, lineNo)) {
cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl; cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl;
string resultline[3] = resultlines[lineNo-1]; string resultline[3] = resultlines[lineNo-1];
if (row.empty()) { if (row.empty()) {
cout << " result empty"; cout << " result empty";
if (resultline[0] == "") { if (resultline[0] == "") {
cout << ": OK" << endl; cout << ": OK" << endl;
} else { } else {
cout << ": error" << endl; cout << ": error" << endl;
error = true; error = true;
} }
continue; continue;
} }
for (vector<string>::iterator it = row.begin(); it != row.end(); it++) { for (vector<string>::iterator it = row.begin(); it != row.end(); it++) {
string got = *it; string got = *it;
string expect = resultline[distance(row.begin(), it)]; string expect = resultline[distance(row.begin(), it)];
ostringstream type; ostringstream type;
type << "line " << static_cast<unsigned>(lineNo) << " col " << static_cast<size_t>(distance(row.begin(), it)+1); type << "line " << static_cast<unsigned>(lineNo) << " col " << static_cast<size_t>(distance(row.begin(), it)+1);
verify(false, type.str(), expect, got == expect, expect, got); verify(false, type.str(), expect, got == expect, expect, got);
} }
} }
return error ? 1 : 0; return error ? 1 : 0;
} }
+377 -377
View File
@@ -27,397 +27,397 @@
using namespace ebusd; using namespace ebusd;
void verify(bool expectFailMatch, string type, string input, void verify(bool expectFailMatch, string type, string input,
bool match, string expectStr, string gotStr) { bool match, string expectStr, string gotStr) {
if (expectFailMatch) { if (expectFailMatch) {
if (match) { if (match) {
cout << " failed " << type << " match >" << input cout << " failed " << type << " match >" << input
<< "< error: unexpectedly succeeded" << endl; << "< error: unexpectedly succeeded" << endl;
} else { } else {
cout << " failed " << type << " match >" << input << "< OK" << endl; cout << " failed " << type << " match >" << input << "< OK" << endl;
} }
} else if (match) { } else if (match) {
cout << " " << type << " match >" << input << "< OK" << endl; cout << " " << type << " match >" << input << "< OK" << endl;
} else { } else {
cout << " " << type << " match >" << input << "< error: got >" cout << " " << type << " match >" << input << "< error: got >"
<< gotStr << "<, expected >" << expectStr << "<" << endl; << gotStr << "<, expected >" << expectStr << "<" << endl;
} }
} }
DataFieldTemplates* templates = NULL; DataFieldTemplates* templates = NULL;
namespace ebusd { namespace ebusd {
DataFieldTemplates* getTemplates(const string filename) { DataFieldTemplates* getTemplates(const string filename) {
if (filename == "") { // avoid compiler warning if (filename == "") { // avoid compiler warning
return templates; return templates;
} }
return templates; return templates;
} }
} }
int main() { int main() {
// message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields... // message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
// field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]] // field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
// template: name,type[:len][,[divisor|values][,[unit][,[comment]]]] // template: name,type[:len][,[divisor|values][,[unit][,[comment]]]]
// condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values // condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values
string checks[][5] = { string checks[][5] = {
// "message", "decoded", "master", "slave", "flags" // "message", "decoded", "master", "slave", "flags"
{"date,HDA:3,,,Datum", "", "", "", "template"}, {"date,HDA:3,,,Datum", "", "", "", "template"},
{"time,VTI,,,", "", "", "", "template"}, {"time,VTI,,,", "", "", "", "template"},
{"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"}, {"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"},
{"temp,D2C,,°C,Temperatur", "", "", "", "template"}, {"temp,D2C,,°C,Temperatur", "", "", "", "template"},
{"temp1,D1C,,°C,Temperatur", "", "", "", "template"}, {"temp1,D1C,,°C,Temperatur", "", "", "", "template"},
{"temp2,D2B,,°C,Temperatur", "", "", "", "template"}, {"temp2,D2B,,°C,Temperatur", "", "", "", "template"},
{"power,UCH,,kW", "", "", "", "template"}, {"power,UCH,,kW", "", "", "", "template"},
{"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"}, {"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"},
{"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"}, {"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"},
{"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"}, {"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"},
{"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"}, {"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"},
{"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"}, {"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"},
{"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"}, {"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"}, {"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
{"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"}, {"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"},
{"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"}, {"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"},
{"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"}, {"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"},
{"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""}, {"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""},
{"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"}, {"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"},
{"w,,first,,,15,b509", "", "ff15b50900", "00", "m"}, {"w,,first,,,15,b509", "", "ff15b50900", "00", "m"},
{"w,,,,,,b505,2d", "", "", "", "defaults"}, {"w,,,,,,b505,2d", "", "", "", "defaults"},
{"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"}, {"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"},
{"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"}, {"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"},
{"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"}, {"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"},
{"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"}, {"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"},
{"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"}, {"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"},
{"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, {"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
{"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, {"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
{"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"}, {"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
{"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"}, {"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
{"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"}, {"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"},
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"}, {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"},
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"}, {"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"},
{"u,ehp,ActualEnvironmentPower,Energiebezug,,08,B509,29BA00,,s,IGN:2,,,,,s,power", "8", "1008b5090329ba00", "03ba0008", "pm"}, {"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"}, {"uw,ehp,test,Test,,08,B5de,ab,,,power,,,,,s,hex:1", "8;39", "1008b5de02ab08", "0139", "pm"},
{"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"}, {"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"},
{"", "55.50;ok", "1025b50903290000", "050000780300", "d"}, {"", "55.50;ok", "1025b50903290000", "050000780300", "d"},
{"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" }, {"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" },
{"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, {"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
{"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" }, {"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
{"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" }, {"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" },
{"r,ehp,,,,08,b509,0d", "", "", "", "defaults" }, {"r,ehp,,,,08,b509,0d", "", "", "", "defaults" },
{"w,ehp,,,,08,b509,0e", "", "", "", "defaults" }, {"w,ehp,,,,08,b509,0e", "", "", "", "defaults" },
{"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" }, {"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" },
{"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" }, {"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" },
{"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" }, {"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" },
{"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" }, {"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" },
{"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" }, {"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" },
{"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" }, {"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" },
{"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" }, {"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" },
{"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" }, {"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" },
{"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" }, {"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" },
{"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" }, {"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" },
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" }, {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" },
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" }, {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" },
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" }, {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" },
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" }, {"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" },
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" }, {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" },
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" }, {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" },
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" }, {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" },
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" }, {"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" },
{"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" }, {"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" },
{"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"}, {"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"},
}; };
templates = new DataFieldTemplates(); templates = new DataFieldTemplates();
MessageMap* messages = new MessageMap(); MessageMap* messages = new MessageMap();
vector< vector<string> > defaultsRows; vector< vector<string> > defaultsRows;
map<string, Condition*> &conditions = messages->getConditions(); map<string, Condition*> &conditions = messages->getConditions();
Message* message = NULL; Message* message = NULL;
vector<Message*> deleteMessages; vector<Message*> deleteMessages;
vector<SymbolString*> mstrs; vector<SymbolString*> mstrs;
vector<SymbolString*> sstrs; vector<SymbolString*> sstrs;
mstrs.resize(1); mstrs.resize(1);
sstrs.resize(1); sstrs.resize(1);
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) { for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
string check[5] = checks[i]; string check[5] = checks[i];
string inputStr = check[1]; string inputStr = check[1];
string flags = check[4]; string flags = check[4];
bool isTemplate = flags == "template"; bool isTemplate = flags == "template";
bool isCondition = flags == "condition"; bool isCondition = flags == "condition";
bool isDefaults = isCondition || flags == "defaults"; bool isDefaults = isCondition || flags == "defaults";
bool dontMap = flags.find('m') != string::npos; bool dontMap = flags.find('m') != string::npos;
bool onlyMap = flags.find('M') != string::npos; bool onlyMap = flags.find('M') != string::npos;
bool failedCreate = flags.find('c') != string::npos; bool failedCreate = flags.find('c') != string::npos;
bool isChain = flags.find('C') != string::npos; bool isChain = flags.find('C') != string::npos;
bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos; bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos;
bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos; bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos;
bool withMessageDump = flags.find('N') != string::npos; bool withMessageDump = flags.find('N') != string::npos;
bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos); bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos);
bool failedPrepare = flags.find('p') != string::npos; bool failedPrepare = flags.find('p') != string::npos;
bool failedPrepareMatch = flags.find('P') != string::npos; bool failedPrepareMatch = flags.find('P') != string::npos;
bool multi = flags.find('*') != string::npos; bool multi = flags.find('*') != string::npos;
bool withInput = flags.find('i') != string::npos; bool withInput = flags.find('i') != string::npos;
result_t result = RESULT_EMPTY; result_t result = RESULT_EMPTY;
vector<string> entries; vector<string> entries;
istringstream ifs(check[0]); istringstream ifs(check[0]);
unsigned int lineNo = 0; unsigned int lineNo = 0;
if (!FileReader::splitFields(ifs, entries, lineNo)) { if (!FileReader::splitFields(ifs, entries, lineNo)) {
entries.clear(); entries.clear();
} }
if (isTemplate) { if (isTemplate) {
// store new template // store new template
DataField* fields = NULL; DataField* fields = NULL;
vector<string>::iterator it = entries.begin(); vector<string>::iterator it = entries.begin();
result = DataField::create(it, entries.end(), templates, fields, false, true, false); result = DataField::create(it, entries.end(), templates, fields, false, true, false);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl;
} else if (it != entries.end()) { } else if (it != entries.end()) {
cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl; cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
} else { } else {
cout << "\"" << check[0] << "\": create template OK" << endl; cout << "\"" << check[0] << "\": create template OK" << endl;
result = templates->add(fields, "", true); result = templates->add(fields, "", true);
if (result == RESULT_OK) { if (result == RESULT_OK) {
cout << " store template OK" << endl; cout << " store template OK" << endl;
} else { } else {
cout << " store template error: " << getResultCode(result) << endl; cout << " store template error: " << getResultCode(result) << endl;
delete fields; delete fields;
} }
} }
continue; continue;
} }
if (isDefaults) { if (isDefaults) {
// store defaults or condition // store defaults or condition
vector<string>::iterator it = entries.begin(); vector<string>::iterator it = entries.begin();
size_t oldSize = conditions.size(); size_t oldSize = conditions.size();
result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1); result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl;
} else if (it != entries.end()) { } else if (it != entries.end()) {
cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl; cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
} else { } else {
cout << "\"" << check[0] << "\": read defaults OK" << endl; cout << "\"" << check[0] << "\": read defaults OK" << endl;
if (isCondition) { if (isCondition) {
if (conditions.size() == oldSize) { if (conditions.size() == oldSize) {
cout << " create condition error" << endl; cout << " create condition error" << endl;
} else { } else {
result = messages->resolveConditions(); result = messages->resolveConditions();
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl; cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl;
} else { } else {
cout << " resolve conditions OK" << endl; cout << " resolve conditions OK" << endl;
} }
} }
} }
} }
continue; continue;
} }
if (isChain) { if (isChain) {
size_t pos = 0; size_t pos = 0;
string token; string token;
istringstream stream(check[2]); istringstream stream(check[2]);
while (getline(stream, token, VALUE_SEPARATOR)) { while (getline(stream, token, VALUE_SEPARATOR)) {
if (pos >= mstrs.size()) { if (pos >= mstrs.size()) {
mstrs.resize(pos+1); mstrs.resize(pos+1);
} else if (mstrs[pos] != NULL) { } else if (mstrs[pos] != NULL) {
delete mstrs[pos]; delete mstrs[pos];
} }
mstrs[pos] = new SymbolString(false); mstrs[pos] = new SymbolString(false);
result = mstrs[pos]->parseHex(token); result = mstrs[pos]->parseHex(token);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
break; break;
} }
pos++; pos++;
} }
pos = 0; pos = 0;
stream.str(check[3]); stream.str(check[3]);
stream.clear(); stream.clear();
while (getline(stream, token, VALUE_SEPARATOR)) { while (getline(stream, token, VALUE_SEPARATOR)) {
if (pos >= sstrs.size()) { if (pos >= sstrs.size()) {
sstrs.resize(pos+1); sstrs.resize(pos+1);
} else if (sstrs[pos] != NULL) { } else if (sstrs[pos] != NULL) {
delete sstrs[pos]; delete sstrs[pos];
} }
sstrs[pos] = new SymbolString(false); sstrs[pos] = new SymbolString(false);
result = sstrs[pos]->parseHex(token); result = sstrs[pos]->parseHex(token);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
break; break;
} }
pos++; pos++;
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
continue; continue;
} }
} else { } else {
if (mstrs[0] != NULL) { if (mstrs[0] != NULL) {
delete mstrs[0]; delete mstrs[0];
} }
mstrs[0] = new SymbolString(false); mstrs[0] = new SymbolString(false);
result = mstrs[0]->parseHex(check[2]); result = mstrs[0]->parseHex(check[2]);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
continue; continue;
} }
if (sstrs[0] != NULL) { if (sstrs[0] != NULL) {
delete sstrs[0]; delete sstrs[0];
} }
sstrs[0] = new SymbolString(false); sstrs[0] = new SymbolString(false);
result = sstrs[0]->parseHex(check[3]); result = sstrs[0]->parseHex(check[3]);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl; cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
continue; continue;
} }
} }
if (deleteMessages.size() > 0) { if (deleteMessages.size() > 0) {
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
Message* deleteMessage = *it; Message* deleteMessage = *it;
delete deleteMessage; delete deleteMessage;
} }
deleteMessages.clear(); deleteMessages.clear();
} }
if (entries.size() == 0) { if (entries.size() == 0) {
message = messages->find(*mstrs[0]); message = messages->find(*mstrs[0]);
if (message == NULL) { if (message == NULL) {
cout << "\"" << check[2] << "\": find error: NULL" << endl; cout << "\"" << check[2] << "\": find error: NULL" << endl;
continue; continue;
} }
cout << "\"" << check[2] << "\": find OK" << endl; cout << "\"" << check[2] << "\": find OK" << endl;
} else { } else {
vector<string>::iterator it = entries.begin(); vector<string>::iterator it = entries.begin();
string types = *it; string types = *it;
Condition* condition = NULL; Condition* condition = NULL;
result = messages->readConditions(types, "no file", condition); result = messages->readConditions(types, "no file", condition);
if (result == RESULT_OK) { if (result == RESULT_OK) {
*it = types; *it = types;
result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages); result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages);
} }
if (failedCreate) { if (failedCreate) {
if (result == RESULT_OK) { if (result == RESULT_OK) {
cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl; cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl;
} else { } else {
cout << "\"" << check[0] << "\": failed create OK" << endl; cout << "\"" << check[0] << "\": failed create OK" << endl;
} }
continue; continue;
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": create error: " cout << "\"" << check[0] << "\": create error: "
<< getResultCode(result) << endl; << getResultCode(result) << endl;
printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result); printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result);
continue; continue;
} }
if (deleteMessages.size() == 0) { if (deleteMessages.size() == 0) {
cout << "\"" << check[0] << "\": create error: NULL" << endl; cout << "\"" << check[0] << "\": create error: NULL" << endl;
continue; continue;
} }
if (it != entries.end()) { if (it != entries.end()) {
cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl; cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
continue; continue;
} }
if (multi && deleteMessages.size() == 1) { if (multi && deleteMessages.size() == 1) {
cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl; cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl;
continue; continue;
} }
if (!multi && deleteMessages.size() > 1) { if (!multi && deleteMessages.size() > 1) {
cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl; cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl;
continue; continue;
} }
cout << "\"" << check[0] << "\": create OK" << endl; cout << "\"" << check[0] << "\": create OK" << endl;
if (!dontMap) { if (!dontMap) {
result_t result = RESULT_OK; result_t result = RESULT_OK;
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
Message* deleteMessage = *it; Message* deleteMessage = *it;
result_t result = messages->add(deleteMessage); result_t result = messages->add(deleteMessage);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "\"" << check[0] << "\": add error: " cout << "\"" << check[0] << "\": add error: "
<< getResultCode(result) << endl; << getResultCode(result) << endl;
break; break;
} }
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
continue; continue;
} }
cout << " map OK" << endl; cout << " map OK" << endl;
message = deleteMessages.front(); message = deleteMessages.front();
deleteMessages.clear(); deleteMessages.clear();
if (onlyMap) { if (onlyMap) {
continue; continue;
} }
Message* foundMessage = messages->find(*mstrs[0]); Message* foundMessage = messages->find(*mstrs[0]);
if (foundMessage == message) { if (foundMessage == message) {
cout << " find OK" << endl; cout << " find OK" << endl;
} else if (foundMessage == NULL) { } else if (foundMessage == NULL) {
cout << " find error: NULL" << endl; cout << " find error: NULL" << endl;
} else { } else {
cout << " find error: different" << endl; cout << " find error: different" << endl;
} }
} else { } else {
message = deleteMessages.front(); message = deleteMessages.front();
} }
} }
if (message->isPassive() || decode) { if (message->isPassive() || decode) {
ostringstream output; ostringstream output;
for (unsigned char index = 0; index < message->getCount(); index++) { for (unsigned char index = 0; index < message->getCount(); index++) {
message->storeLastData(*mstrs[index], *sstrs[index]); message->storeLastData(*mstrs[index], *sstrs[index]);
} }
if (withMessageDump && !decodeJson) { if (withMessageDump && !decodeJson) {
message->dump(output, NULL, true); message->dump(output, NULL, true);
output << ": "; output << ": ";
} }
result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false); result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: " cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
<< getResultCode(result) << endl; << getResultCode(result) << endl;
continue; continue;
} }
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl; cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl;
bool match = inputStr == output.str(); bool match = inputStr == output.str();
verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str()); verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str());
} }
if (!message->isPassive() && (withInput || !decode)) { if (!message->isPassive() && (withInput || !decode)) {
istringstream input(inputStr); istringstream input(inputStr);
SymbolString writeMstr(false); SymbolString writeMstr(false);
result = message->prepareMaster(0xff, writeMstr, input); result = message->prepareMaster(0xff, writeMstr, input);
if (failedPrepare) { if (failedPrepare) {
if (result == RESULT_OK) { if (result == RESULT_OK) {
cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl; cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl;
} else { } else {
cout << " \"" << inputStr << "\": failed prepare OK" << endl; cout << " \"" << inputStr << "\": failed prepare OK" << endl;
} }
continue; continue;
} }
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << " \"" << inputStr << "\": prepare error: " cout << " \"" << inputStr << "\": prepare error: "
<< getResultCode(result) << endl; << getResultCode(result) << endl;
continue; continue;
} }
cout << " \"" << inputStr << "\": prepare OK" << endl; cout << " \"" << inputStr << "\": prepare OK" << endl;
bool match = writeMstr == *mstrs[0]; bool match = writeMstr == *mstrs[0];
verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false)); verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false));
} }
} }
if (deleteMessages.size() > 0) { if (deleteMessages.size() > 0) {
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) { for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
Message* deleteMessage = *it; Message* deleteMessage = *it;
delete deleteMessage; delete deleteMessage;
} }
deleteMessages.clear(); deleteMessages.clear();
} }
delete templates; delete templates;
delete messages; delete messages;
for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) { for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) {
delete *it; delete *it;
} }
for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) { for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) {
delete *it; delete *it;
} }
return 0; return 0;
} }
+59 -59
View File
@@ -27,70 +27,70 @@ using namespace ebusd;
static bool error = false; static bool error = false;
void verify(bool expectFailMatch, string type, string input, void verify(bool expectFailMatch, string type, string input,
bool match, string expectStr, string gotStr) { bool match, string expectStr, string gotStr) {
match = match && expectStr == gotStr; match = match && expectStr == gotStr;
if (expectFailMatch) { if (expectFailMatch) {
if (match) { if (match) {
cout << " failed " << type << " match >" << input cout << " failed " << type << " match >" << input
<< "< error: unexpectedly succeeded" << endl; << "< error: unexpectedly succeeded" << endl;
error = true; error = true;
} else { } else {
cout << " failed " << type << " match >" << input << "< OK" << endl; cout << " failed " << type << " match >" << input << "< OK" << endl;
} }
} else if (match) { } else if (match) {
cout << " " << type << " match >" << input << "< OK" << endl; cout << " " << type << " match >" << input << "< OK" << endl;
} else { } else {
cout << " " << type << " match >" << input << "< error: got >" cout << " " << type << " match >" << input << "< error: got >"
<< gotStr << "<, expected >" << expectStr << "<" << endl; << gotStr << "<, expected >" << expectStr << "<" << endl;
error = true; error = true;
} }
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
SymbolString sstr(true); SymbolString sstr(true);
if (argc > 1) { if (argc > 1) {
result_t result = sstr.parseHex(argv[1], true); result_t result = sstr.parseHex(argv[1], true);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "parse escaped error: " << getResultCode(result) << endl; cout << "parse escaped error: " << getResultCode(result) << endl;
} else { } else {
unsigned char gotCrc = sstr.getCRC(); unsigned char gotCrc = sstr.getCRC();
cout << "calculated CRC: 0x" cout << "calculated CRC: 0x"
<< nouppercase << setw(2) << hex << setfill('0') << nouppercase << setw(2) << hex << setfill('0')
<< static_cast<unsigned>(gotCrc) << endl; << static_cast<unsigned>(gotCrc) << endl;
} }
return 0; return 0;
} }
string gotStr, expectStr; string gotStr, expectStr;
result_t result = sstr.parseHex("10feb5050427a915aa", false); result_t result = sstr.parseHex("10feb5050427a915aa", false);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "parse escaped error: " << getResultCode(result) << endl; cout << "parse escaped error: " << getResultCode(result) << endl;
error = true; error = true;
} else { } else {
gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177"; gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177";
verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr); verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr);
unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77; unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77;
ostringstream ostr; ostringstream ostr;
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc); ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc);
expectStr = ostr.str(); expectStr = ostr.str();
ostr.str(""); ostr.str("");
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc); ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc);
gotStr = ostr.str(); gotStr = ostr.str();
verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr); verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr);
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr); verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr);
} }
sstr = SymbolString(false); sstr = SymbolString(false);
result = sstr.parseHex("10feb5050427a90015a90177", true); result = sstr.parseHex("10feb5050427a90015a90177", true);
if (result != RESULT_OK) { if (result != RESULT_OK) {
cout << "parse unescaped error: " << getResultCode(result) << endl; cout << "parse unescaped error: " << getResultCode(result) << endl;
error = true; error = true;
} else { } else {
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77"; gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr); verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr);
} }
return error ? 1 : 0; return error ? 1 : 0;
} }
+11 -11
View File
@@ -18,8 +18,8 @@
#include "clock.h" #include "clock.h"
#ifdef __MACH__ #ifdef __MACH__
# include <mach/clock.h> # include <mach/clock.h>
# include <mach/mach.h> # include <mach/mach.h>
#endif #endif
#ifdef __MACH__ #ifdef __MACH__
@@ -29,15 +29,15 @@ static clock_serv_t clockServ;
void clockGettime(struct timespec* t) { void clockGettime(struct timespec* t) {
#ifdef __MACH__ #ifdef __MACH__
if (!clockInitialized) { if (!clockInitialized) {
clockInitialized = true; clockInitialized = true;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ); host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ);
} }
mach_timespec_t mts; mach_timespec_t mts;
clock_get_time(clockServ, &mts); clock_get_time(clockServ, &mts);
t->tv_sec = mts.tv_sec; t->tv_sec = mts.tv_sec;
t->tv_nsec = mts.tv_nsec; t->tv_nsec = mts.tv_nsec;
#else #else
clock_gettime(CLOCK_REALTIME, t); clock_gettime(CLOCK_REALTIME, t);
#endif #endif
} }
+101 -101
View File
@@ -27,23 +27,23 @@
/** the name of each @a LogFacility. */ /** the name of each @a LogFacility. */
static const char *facilityNames[] = { static const char *facilityNames[] = {
"main", "main",
"network", "network",
"bus", "bus",
"update", "update",
"other", "other",
"all", "all",
NULL NULL
}; };
/** the name of each @a LogLevel. */ /** the name of each @a LogLevel. */
static const char* levelNames[] = { static const char* levelNames[] = {
"none", "none",
"error", "error",
"notice", "notice",
"info", "info",
"debug", "debug",
NULL NULL
}; };
/** the bit combination of currently active log facilities (1 << @a LogFacility). */ /** the bit combination of currently active log facilities (1 << @a LogFacility). */
@@ -56,119 +56,119 @@ static LogLevel s_logLevel = ll_notice;
static FILE* s_logFile = stdout; static FILE* s_logFile = stdout;
bool setLogFacilities(const char* facilities) { bool setLogFacilities(const char* facilities) {
char *input = strdup(facilities); char *input = strdup(facilities);
char *opt = reinterpret_cast<char*>(input), *value = NULL; char *opt = reinterpret_cast<char*>(input), *value = NULL;
int newFacilites = 0; int newFacilites = 0;
while (*opt) { while (*opt) {
int val = getsubopt(&opt, (char *const *)facilityNames, &value); int val = getsubopt(&opt, (char *const *)facilityNames, &value);
if (val < 0 || val > lf_COUNT || value) { if (val < 0 || val > lf_COUNT || value) {
free(input); free(input);
return false; return false;
} }
if (val == lf_COUNT) { if (val == lf_COUNT) {
newFacilites = LF_ALL; newFacilites = LF_ALL;
} else { } else {
newFacilites |= 1 << val; newFacilites |= 1 << val;
} }
} }
//s_lastFacilities = newFacilites; //s_lastFacilities = newFacilites;
s_logFacilites = newFacilites; s_logFacilites = newFacilites;
free(input); free(input);
return true; return true;
} }
bool getLogFacilities(char* buffer) { bool getLogFacilities(char* buffer) {
if (s_logFacilites == LF_ALL) { if (s_logFacilites == LF_ALL) {
return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0; return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0;
} }
*buffer = 0; // for strcat to work *buffer = 0; // for strcat to work
bool found = false; bool found = false;
size_t len = 0; size_t len = 0;
for (int val = 0; val < lf_COUNT; val++) { for (int val = 0; val < lf_COUNT; val++) {
if (s_logFacilites&(1 << val)) { if (s_logFacilites&(1 << val)) {
if (found) { if (found) {
len += snprintf(buffer+len, 48-len, ","); len += snprintf(buffer+len, 48-len, ",");
} }
found = true; found = true;
len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]); len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]);
} }
} }
return true; return true;
} }
bool setLogLevel(const char* level) { bool setLogLevel(const char* level) {
char *input = strdup(level); char *input = strdup(level);
char *opt = reinterpret_cast<char*>(input), *value = NULL; char *opt = reinterpret_cast<char*>(input), *value = NULL;
int newLevel = 0; int newLevel = 0;
if (*opt) { if (*opt) {
int val = getsubopt(&opt, (char *const *)levelNames, &value); int val = getsubopt(&opt, (char *const *)levelNames, &value);
if (val < 0 || val >= ll_COUNT || value || *opt) { if (val < 0 || val >= ll_COUNT || value || *opt) {
free(input); free(input);
return false; return false;
} }
newLevel = val; newLevel = val;
} }
s_logLevel = (LogLevel)newLevel; s_logLevel = (LogLevel)newLevel;
free(input); free(input);
return true; return true;
} }
const char* getLogLevel() { const char* getLogLevel() {
return levelNames[s_logLevel]; return levelNames[s_logLevel];
} }
bool setLogFile(const char* filename) { bool setLogFile(const char* filename) {
FILE* newFile = fopen(filename, "a"); FILE* newFile = fopen(filename, "a");
if (newFile == NULL) { if (newFile == NULL) {
return false; return false;
} }
closeLogFile(); closeLogFile();
s_logFile = newFile; s_logFile = newFile;
return true; return true;
} }
void closeLogFile() { void closeLogFile() {
if (s_logFile != NULL) { if (s_logFile != NULL) {
if (s_logFile != stdout) { if (s_logFile != stdout) {
fclose(s_logFile); fclose(s_logFile);
} }
s_logFile = NULL; s_logFile = NULL;
} }
} }
bool needsLog(const LogFacility facility, const LogLevel level) { bool needsLog(const LogFacility facility, const LogLevel level) {
return ((s_logFacilites & (1 << facility)) != 0) return ((s_logFacilites & (1 << facility)) != 0)
&& (s_logLevel >= level); && (s_logLevel >= level);
} }
void logWrite(const char* facility, const char* level, const char* message, va_list ap) { void logWrite(const char* facility, const char* level, const char* message, va_list ap) {
struct timespec ts; struct timespec ts;
struct tm td; struct tm td;
clockGettime(&ts); clockGettime(&ts);
localtime_r(&ts.tv_sec, &td); localtime_r(&ts.tv_sec, &td);
char* buf; char* buf;
if (vasprintf(&buf, message, ap) >= 0 && buf) { if (vasprintf(&buf, message, ap) >= 0 && buf) {
fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n", fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n",
td.tm_year+1900, td.tm_mon+1, td.tm_mday, td.tm_year+1900, td.tm_mon+1, td.tm_mday,
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
facility, level, buf); facility, level, buf);
fflush(s_logFile); fflush(s_logFile);
} }
if (buf) { if (buf) {
free(buf); free(buf);
} }
} }
void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) { void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) {
va_list ap; va_list ap;
va_start(ap, message); va_start(ap, message);
logWrite(facilityNames[facility], levelNames[level], message, ap); logWrite(facilityNames[facility], levelNames[level], message, ap);
va_end(ap); va_end(ap);
} }
void logWrite(const char* facility, const LogLevel level, const char* message, ...) { void logWrite(const char* facility, const LogLevel level, const char* message, ...) {
va_list ap; va_list ap;
va_start(ap, message); va_start(ap, message);
logWrite(facility, levelNames[level], message, ap); logWrite(facility, levelNames[level], message, ap);
va_end(ap); va_end(ap);
} }
+12 -12
View File
@@ -23,12 +23,12 @@
/** the available log facilities. */ /** the available log facilities. */
enum LogFacility { enum LogFacility {
lf_main = 0, //!< main loop lf_main = 0, //!< main loop
lf_network, //!< network related lf_network, //!< network related
lf_bus, //!< eBUS related lf_bus, //!< eBUS related
lf_update, //!< updates found while listening to the bus lf_update, //!< updates found while listening to the bus
lf_other, //!< all other log facilities lf_other, //!< all other log facilities
lf_COUNT = 5 //!< number of available log facilities lf_COUNT = 5 //!< number of available log facilities
}; };
/** macro for enabling all log facilities. */ /** macro for enabling all log facilities. */
@@ -36,12 +36,12 @@ enum LogFacility {
/** the available log levels. */ /** the available log levels. */
enum LogLevel { enum LogLevel {
ll_none = 0, //!< no level at all ll_none = 0, //!< no level at all
ll_error, //!< error message ll_error, //!< error message
ll_notice, //!< important message ll_notice, //!< important message
ll_info, //!< informational message ll_info, //!< informational message
ll_debug, //!< debugging message (normally suppressed) ll_debug, //!< debugging message (normally suppressed)
ll_COUNT = 5 //!< number of available log levels ll_COUNT = 5 //!< number of available log levels
}; };
/** /**
+32 -32
View File
@@ -28,45 +28,45 @@
* class to notify other thread per pipe. * class to notify other thread per pipe.
*/ */
class Notify { class Notify {
public: public:
/** /**
* constructs a new instance and do notifying. * constructs a new instance and do notifying.
*/ */
Notify() { Notify() {
int pipefd[2]; int pipefd[2];
int ret = pipe(pipefd); int ret = pipe(pipefd);
if (ret == 0) { if (ret == 0) {
m_recvfd = pipefd[0]; m_recvfd = pipefd[0];
m_sendfd = pipefd[1]; m_sendfd = pipefd[1];
fcntl(m_sendfd, F_SETFL, O_NONBLOCK); fcntl(m_sendfd, F_SETFL, O_NONBLOCK);
} }
} }
/** /**
* destructor. * destructor.
*/ */
~Notify() { close(m_sendfd); close(m_recvfd); } ~Notify() { close(m_sendfd); close(m_recvfd); }
/** /**
* file descriptor to watch for notify event. * file descriptor to watch for notify event.
* @return the notification value. * @return the notification value.
*/ */
int notifyFD() { return m_recvfd; } int notifyFD() { return m_recvfd; }
/** /**
* write notify event to file descriptor. * write notify event to file descriptor.
* @return result of writing notification. * @return result of writing notification.
*/ */
int notify() const { return write(m_sendfd, "1", 1); } int notify() const { return write(m_sendfd, "1", 1); }
private: private:
/** file descriptor to watch */ /** file descriptor to watch */
int m_recvfd; int m_recvfd;
/** file descriptor to notify */ /** file descriptor to notify */
int m_sendfd; int m_sendfd;
}; };
#endif // LIB_UTILS_NOTIFY_H_ #endif // LIB_UTILS_NOTIFY_H_
+104 -104
View File
@@ -34,122 +34,122 @@ using std::list;
*/ */
template <typename T> template <typename T>
class Queue { class Queue {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
Queue() { Queue() {
pthread_mutex_init(&m_mutex, NULL); pthread_mutex_init(&m_mutex, NULL);
pthread_cond_init(&m_cond, NULL); pthread_cond_init(&m_cond, NULL);
} }
/** /**
* Destructor. * Destructor.
*/ */
~Queue() { ~Queue() {
pthread_mutex_destroy(&m_mutex); pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_cond); pthread_cond_destroy(&m_cond);
} }
private: private:
/** /**
* Hidden copy constructor. * Hidden copy constructor.
* @param src the object to copy from. * @param src the object to copy from.
*/ */
Queue(const Queue& src); Queue(const Queue& src);
public: public:
/** /**
* Add an item to the end of queue. * Add an item to the end of queue.
* @param item the item to add. * @param item the item to add.
*/ */
void push(T item) { void push(T item) {
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
m_queue.push_back(item); m_queue.push_back(item);
pthread_cond_broadcast(&m_cond); pthread_cond_broadcast(&m_cond);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
} }
/** /**
* Remove the first item from the queue optionally waiting for the queue being non-empty. * Remove the first item from the queue optionally waiting for the queue being non-empty.
* @param timeout the maximum time in seconds to wait for the queue being filled, or 0 for no wait. * @param timeout the maximum time in seconds to wait for the queue being filled, or 0 for no wait.
* @return the item, or NULL if no item is available within the specified time. * @return the item, or NULL if no item is available within the specified time.
*/ */
T pop(int timeout = 0) { T pop(int timeout = 0) {
T item; T item;
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
if (timeout > 0) { if (timeout > 0) {
struct timespec t; struct timespec t;
clockGettime(&t); clockGettime(&t);
t.tv_sec += timeout; t.tv_sec += timeout;
while (m_queue.empty()) { while (m_queue.empty()) {
if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) { if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) {
break; break;
} }
} }
} }
if (m_queue.empty()) { if (m_queue.empty()) {
item = NULL; item = NULL;
} else { } else {
item = m_queue.front(); item = m_queue.front();
m_queue.pop_front(); m_queue.pop_front();
} }
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return item; return item;
} }
/** /**
* Remove the specified item from the queue optionally waiting for it to appear in the queue. * Remove the specified item from the queue optionally waiting for it to appear in the queue.
* @param item the item to remove and optionally wait for. * @param item the item to remove and optionally wait for.
* @param wait true to wait for the item to appear in the queue. * @param wait true to wait for the item to appear in the queue.
* @return whether the item was removed. * @return whether the item was removed.
*/ */
bool remove(T item, bool wait = false) { bool remove(T item, bool wait = false) {
bool ret = false; bool ret = false;
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
do { do {
size_t oldSize = m_queue.size(); size_t oldSize = m_queue.size();
if (oldSize > 0) { if (oldSize > 0) {
m_queue.remove(item); m_queue.remove(item);
if (m_queue.size() != oldSize) { if (m_queue.size() != oldSize) {
ret = true; ret = true;
break; break;
} }
} }
pthread_cond_wait(&m_cond, &m_mutex); pthread_cond_wait(&m_cond, &m_mutex);
} while (wait); } while (wait);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return ret; return ret;
} }
/** /**
* Return the first item in the queue without removing it. * Return the first item in the queue without removing it.
* @return the item, or NULL if no item is available. * @return the item, or NULL if no item is available.
*/ */
T peek() { T peek() {
T item; T item;
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
if (m_queue.empty()) { if (m_queue.empty()) {
item = NULL; item = NULL;
} else { } else {
item = m_queue.front(); item = m_queue.front();
} }
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return item; return item;
} }
private: private:
/** the queue itself */ /** the queue itself */
list<T> m_queue; list<T> m_queue;
/** mutex variable for exclusive lock */ /** mutex variable for exclusive lock */
pthread_mutex_t m_mutex; pthread_mutex_t m_mutex;
/** condition variable for exclusive lock */ /** condition variable for exclusive lock */
pthread_cond_t m_cond; pthread_cond_t m_cond;
}; };
#endif // LIB_UTILS_QUEUE_H_ #endif // LIB_UTILS_QUEUE_H_
+49 -49
View File
@@ -30,59 +30,59 @@
using std::streamsize; using std::streamsize;
RotateFile::~RotateFile() { RotateFile::~RotateFile() {
if (m_stream) { if (m_stream) {
fclose(m_stream); fclose(m_stream);
m_stream = NULL; m_stream = NULL;
} }
} }
bool RotateFile::setEnabled(bool enabled) { bool RotateFile::setEnabled(bool enabled) {
if (enabled == m_enabled) { if (enabled == m_enabled) {
return false; return false;
} }
m_enabled = enabled; m_enabled = enabled;
if (m_stream) { if (m_stream) {
fclose(m_stream); fclose(m_stream);
m_stream = NULL; m_stream = NULL;
} }
if (enabled) { if (enabled) {
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
m_fileSize = 0; m_fileSize = 0;
} }
return true; return true;
} }
void RotateFile::write(unsigned char* value, unsigned int size, bool received) { void RotateFile::write(unsigned char* value, unsigned int size, bool received) {
if (!m_enabled || !m_stream) { if (!m_enabled || !m_stream) {
return; return;
} }
if (m_textMode) { if (m_textMode) {
struct timespec ts; struct timespec ts;
struct tm td; struct tm td;
clockGettime(&ts); clockGettime(&ts);
localtime_r(&ts.tv_sec, &td); localtime_r(&ts.tv_sec, &td);
fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c", fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c",
td.tm_year+1900, td.tm_mon+1, td.tm_mday, td.tm_year+1900, td.tm_mon+1, td.tm_mday,
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000, td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
received ? '<' : '>'); received ? '<' : '>');
for (unsigned int pos = 0; pos < size; pos++) { for (unsigned int pos = 0; pos < size; pos++) {
fprintf(m_stream, "%2.2x ", value[pos]); fprintf(m_stream, "%2.2x ", value[pos]);
} }
fprintf(m_stream, "\n"); fprintf(m_stream, "\n");
m_fileSize += 25+3*size+1; m_fileSize += 25+3*size+1;
} else { } else {
fwrite(value, (streamsize)size, 1, m_stream); fwrite(value, (streamsize)size, 1, m_stream);
m_fileSize += size; m_fileSize += size;
} }
if ((m_fileSize%1024) == 0) { if ((m_fileSize%1024) == 0) {
fflush(m_stream); fflush(m_stream);
} }
if (m_fileSize >= m_maxSize * 1024LL) { if (m_fileSize >= m_maxSize * 1024LL) {
string oldfile = string(m_fileName)+".old"; string oldfile = string(m_fileName)+".old";
if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) { if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) {
fclose(m_stream); fclose(m_stream);
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
m_fileSize = 0; m_fileSize = 0;
} }
} }
} }
+44 -44
View File
@@ -35,61 +35,61 @@ using std::string;
* Helper class for writing to a rotating file with maximum size. * Helper class for writing to a rotating file with maximum size.
*/ */
class RotateFile { class RotateFile {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
* @param fileName the name of the file write to. * @param fileName the name of the file write to.
* @param maxSize the maximum size of the file to write to. * @param maxSize the maximum size of the file to write to.
* @param textMode whether to write each byte with prefixed timestamp and direction as text. * @param textMode whether to write each byte with prefixed timestamp and direction as text.
*/ */
RotateFile(const string fileName, const unsigned int maxSize, const bool textMode = false) RotateFile(const string fileName, const unsigned int maxSize, const bool textMode = false)
: m_enabled(false), m_fileName(fileName), m_maxSize(maxSize), m_textMode(textMode), m_stream(), m_fileSize(0) {} : m_enabled(false), m_fileName(fileName), m_maxSize(maxSize), m_textMode(textMode), m_stream(), m_fileSize(0) {}
/** /**
* Destructor. * Destructor.
*/ */
virtual ~RotateFile(); virtual ~RotateFile();
/** /**
* Enable or disable writing to the file. * Enable or disable writing to the file.
* @param enabled @p true to enable writing to the file, @p false to disable it. * @param enabled @p true to enable writing to the file, @p false to disable it.
* @return @p true when the state was changed, @p false otherwise. * @return @p true when the state was changed, @p false otherwise.
*/ */
bool setEnabled(bool enabled = true); bool setEnabled(bool enabled = true);
/** /**
* Return whether writing to the file is enabled. * Return whether writing to the file is enabled.
* @return whether writing to the file is enabled. * @return whether writing to the file is enabled.
*/ */
bool isEnabled() { return m_enabled; } bool isEnabled() { return m_enabled; }
/** /**
* Write a number of bytes to the stream. * Write a number of bytes to the stream.
* @param value the pointer to the bytes to write. * @param value the pointer to the bytes to write.
* @param size the number of bytes to write. * @param size the number of bytes to write.
* @param received @a true on reception, @a false on sending (only relevant in text mode). * @param received @a true on reception, @a false on sending (only relevant in text mode).
*/ */
void write(unsigned char* value, unsigned int size, bool received = true); void write(unsigned char* value, unsigned int size, bool received = true);
private: private:
/** whether writing to the file is enabled. */ /** whether writing to the file is enabled. */
bool m_enabled; bool m_enabled;
/** the name of the file write to. */ /** the name of the file write to. */
const string m_fileName; const string m_fileName;
/** the maximum size of @a m_file, or 0 for infinite. */ /** the maximum size of @a m_file, or 0 for infinite. */
const unsigned int m_maxSize; const unsigned int m_maxSize;
/** whether to write each byte with prefixed timestamp and direction as text. */ /** whether to write each byte with prefixed timestamp and direction as text. */
const bool m_textMode; const bool m_textMode;
/** the @a FILE to writing to. */ /** the @a FILE to writing to. */
FILE* m_stream; FILE* m_stream;
/** the number of bytes already written to the @a m_file. */ /** the number of bytes already written to the @a m_file. */
uint64_t m_fileSize; uint64_t m_fileSize;
}; };
#endif // LIB_UTILS_ROTATEFILE_H_ #endif // LIB_UTILS_ROTATEFILE_H_
+68 -68
View File
@@ -24,98 +24,98 @@
#include <cstdlib> #include <cstdlib>
TCPSocket::TCPSocket(int sfd, struct sockaddr_in* address) : m_sfd(sfd) { TCPSocket::TCPSocket(int sfd, struct sockaddr_in* address) : m_sfd(sfd) {
char ip[17]; char ip[17];
inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1); inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1);
m_ip = ip; m_ip = ip;
m_port = (uint16_t)ntohs(address->sin_port); m_port = (uint16_t)ntohs(address->sin_port);
} }
bool TCPSocket::isValid() { bool TCPSocket::isValid() {
return fcntl(m_sfd, F_GETFL) != -1; return fcntl(m_sfd, F_GETFL) != -1;
} }
TCPSocket* TCPClient::connect(const string& server, const uint16_t& port) { TCPSocket* TCPClient::connect(const string& server, const uint16_t& port) {
struct sockaddr_in address; struct sockaddr_in address;
int ret; int ret;
memset(reinterpret_cast<char*>(&address), 0, sizeof(address)); memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
if (inet_addr(server.c_str()) == INADDR_NONE) { if (inet_addr(server.c_str()) == INADDR_NONE) {
struct hostent* he; struct hostent* he;
he = gethostbyname(server.c_str()); he = gethostbyname(server.c_str());
if (he == NULL) { if (he == NULL) {
return NULL; return NULL;
} }
memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length); memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length);
} else { } else {
ret = inet_aton(server.c_str(), &address.sin_addr); ret = inet_aton(server.c_str(), &address.sin_addr);
if (ret == 0) { if (ret == 0) {
return NULL; return NULL;
} }
} }
address.sin_family = AF_INET; address.sin_family = AF_INET;
address.sin_port = (in_port_t)htons(port); address.sin_port = (in_port_t)htons(port);
int sfd = socket(AF_INET, SOCK_STREAM, 0); int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0) { if (sfd < 0) {
return NULL; return NULL;
} }
ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address)); ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address));
if (ret < 0) { if (ret < 0) {
return NULL; return NULL;
} }
return new TCPSocket(sfd, &address); return new TCPSocket(sfd, &address);
} }
int TCPServer::start() { int TCPServer::start() {
if (m_listening) { if (m_listening) {
return 0; return 0;
} }
m_lfd = socket(AF_INET, SOCK_STREAM, 0); m_lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in address; struct sockaddr_in address;
memset(&address, 0, sizeof(address)); memset(&address, 0, sizeof(address));
address.sin_family = AF_INET; address.sin_family = AF_INET;
address.sin_port = (in_port_t)htons(m_port); address.sin_port = (in_port_t)htons(m_port);
if (m_address.size() > 0) { if (m_address.size() > 0) {
inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr)); inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr));
} else { } else {
address.sin_addr.s_addr = INADDR_ANY; address.sin_addr.s_addr = INADDR_ANY;
} }
int optval = 1; int optval = 1;
setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address)); int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address));
if (result != 0) { if (result != 0) {
return result; return result;
} }
result = listen(m_lfd, 5); result = listen(m_lfd, 5);
if (result != 0) { if (result != 0) {
return result; return result;
} }
m_listening = true; m_listening = true;
return result; return result;
} }
TCPSocket* TCPServer::newSocket() { TCPSocket* TCPServer::newSocket() {
if (!m_listening) { if (!m_listening) {
return NULL; return NULL;
} }
struct sockaddr_in address; struct sockaddr_in address;
socklen_t len = sizeof(address); socklen_t len = sizeof(address);
memset(&address, 0, sizeof(address)); memset(&address, 0, sizeof(address));
int sfd = accept(m_lfd, (struct sockaddr*) &address, &len); int sfd = accept(m_lfd, (struct sockaddr*) &address, &len);
if (sfd < 0) { if (sfd < 0) {
return NULL; return NULL;
} }
return new TCPSocket(sfd, &address); return new TCPSocket(sfd, &address);
} }
+100 -100
View File
@@ -38,140 +38,140 @@ using std::string;
* class for low level tcp socket operations. (open, close, send, receive). * class for low level tcp socket operations. (open, close, send, receive).
*/ */
class TCPSocket { class TCPSocket {
public: public:
/** grant access for friend class TCPClient */ /** grant access for friend class TCPClient */
friend class TCPClient; friend class TCPClient;
/** grant access for friend class TCPServer */ /** grant access for friend class TCPServer */
friend class TCPServer; friend class TCPServer;
/** /**
* destructor. * destructor.
*/ */
~TCPSocket() { close(m_sfd); } ~TCPSocket() { close(m_sfd); }
/** /**
* write bytes to opened file descriptor. * write bytes to opened file descriptor.
* @param buffer data to send. * @param buffer data to send.
* @param len number of bytes to send. * @param len number of bytes to send.
* @return number of written bytes or -1 if an error has occured. * @return number of written bytes or -1 if an error has occured.
*/ */
ssize_t send(const char* buffer, size_t len) { return ::send(m_sfd, buffer, len, MSG_NOSIGNAL); } ssize_t send(const char* buffer, size_t len) { return ::send(m_sfd, buffer, len, MSG_NOSIGNAL); }
/** /**
* read bytes from opened file descriptor. * read bytes from opened file descriptor.
* @param buffer for received bytes. * @param buffer for received bytes.
* @param len size of the receive buffer. * @param len size of the receive buffer.
* @return number of read bytes or -1 if an error has occured. * @return number of read bytes or -1 if an error has occured.
*/ */
ssize_t recv(char* buffer, size_t len) { return ::recv(m_sfd, buffer, len, 0); } ssize_t recv(char* buffer, size_t len) { return ::recv(m_sfd, buffer, len, 0); }
/** /**
* returns the tcp port. * returns the tcp port.
* @return the tcp port. * @return the tcp port.
*/ */
uint16_t getPort() const { return m_port; } uint16_t getPort() const { return m_port; }
/** /**
* returns the ip address. * returns the ip address.
* @return the ip address. * @return the ip address.
*/ */
string getIP() const { return m_ip; } string getIP() const { return m_ip; }
/** /**
* returns the file descriptor. * returns the file descriptor.
* @return the file descriptor. * @return the file descriptor.
*/ */
int getFD() const { return m_sfd; } int getFD() const { return m_sfd; }
/** /**
* returns status of file descriptor. * returns status of file descriptor.
* @return true if file descriptor is valid. * @return true if file descriptor is valid.
*/ */
bool isValid(); bool isValid();
private: private:
/** file descriptor from tcp socket */ /** file descriptor from tcp socket */
int m_sfd; int m_sfd;
/** port of tcp socket */ /** port of tcp socket */
uint16_t m_port; uint16_t m_port;
/** ip address of tcp socket */ /** ip address of tcp socket */
string m_ip; string m_ip;
/** /**
* private constructor, limited access only for friend classes. * private constructor, limited access only for friend classes.
* @param sfd the file desctriptor of tcp socket. * @param sfd the file desctriptor of tcp socket.
* @param address struct which holds the ip address. * @param address struct which holds the ip address.
*/ */
TCPSocket(int sfd, struct sockaddr_in* address); TCPSocket(int sfd, struct sockaddr_in* address);
}; };
/** /**
* class to initiate a tcp socket connection to a listening server. * class to initiate a tcp socket connection to a listening server.
*/ */
class TCPClient { class TCPClient {
public: public:
/** /**
* initiate a tcp socket connection to a listening server. * initiate a tcp socket connection to a listening server.
* @param server the server name or ip address to connect. * @param server the server name or ip address to connect.
* @param port the tcp port. * @param port the tcp port.
* @return pointer to an opened tcp socket. * @return pointer to an opened tcp socket.
*/ */
TCPSocket* connect(const string& server, const uint16_t& port); TCPSocket* connect(const string& server, const uint16_t& port);
}; };
/** /**
* class for a tcp based network server. * class for a tcp based network server.
*/ */
class TCPServer { class TCPServer {
public: public:
/** /**
* creates a new instance of a listening tcp server. * creates a new instance of a listening tcp server.
* @param port the tcp port. * @param port the tcp port.
* @param address the ip address. * @param address the ip address.
*/ */
TCPServer(const uint16_t port, const string address) TCPServer(const uint16_t port, const string address)
: m_lfd(0), m_port(port), m_address(address), m_listening(false) {} : m_lfd(0), m_port(port), m_address(address), m_listening(false) {}
/** /**
* destructor. * destructor.
*/ */
~TCPServer() { if (m_lfd > 0) {close(m_lfd);} } ~TCPServer() { if (m_lfd > 0) {close(m_lfd);} }
/** /**
* start listening of tcp socket. * start listening of tcp socket.
* @return result of low level functions. * @return result of low level functions.
*/ */
int start(); int start();
/** /**
* accept an incoming tcp connection and create a local tcp socket for communication. * accept an incoming tcp connection and create a local tcp socket for communication.
* @return pointer to an opened tcp socket. * @return pointer to an opened tcp socket.
*/ */
TCPSocket* newSocket(); TCPSocket* newSocket();
/** /**
* returns the file descriptor. * returns the file descriptor.
* @return the file descriptor. * @return the file descriptor.
*/ */
int getFD() const { return m_lfd; } int getFD() const { return m_lfd; }
private: private:
/** file descriptor from listening tcp socket */ /** file descriptor from listening tcp socket */
int m_lfd; int m_lfd;
/** listening tcp port */ /** listening tcp port */
uint16_t m_port; uint16_t m_port;
/** listening tcp socket ip address */ /** listening tcp socket ip address */
string m_address; string m_address;
/** true if object is already listening */ /** true if object is already listening */
bool m_listening; bool m_listening;
}; };
#endif // LIB_UTILS_TCPSOCKET_H_ #endif // LIB_UTILS_TCPSOCKET_H_
+46 -46
View File
@@ -17,88 +17,88 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include "thread.h" #include "thread.h"
#include "clock.h" #include "clock.h"
void* Thread::runThread(void* arg) { void* Thread::runThread(void* arg) {
reinterpret_cast<Thread*>(arg)->enter(); reinterpret_cast<Thread*>(arg)->enter();
return NULL; return NULL;
} }
Thread::~Thread() { Thread::~Thread() {
if (m_started) { if (m_started) {
pthread_cancel(m_threadid); pthread_cancel(m_threadid);
pthread_detach(m_threadid); pthread_detach(m_threadid);
} }
} }
bool Thread::start(const char* name) { bool Thread::start(const char* name) {
int result = pthread_create(&m_threadid, NULL, runThread, this); int result = pthread_create(&m_threadid, NULL, runThread, this);
if (result == 0) { if (result == 0) {
#ifdef HAVE_PTHREAD_SETNAME_NP #ifdef HAVE_PTHREAD_SETNAME_NP
#ifndef __MACH__ #ifndef __MACH__
pthread_setname_np(m_threadid, name); pthread_setname_np(m_threadid, name);
#endif #endif
#endif #endif
m_started = true; m_started = true;
return true; return true;
} }
return false; return false;
} }
bool Thread::join() { bool Thread::join() {
int result = -1; int result = -1;
if (m_started) { if (m_started) {
m_stopped = true; m_stopped = true;
result = pthread_join(m_threadid, NULL); result = pthread_join(m_threadid, NULL);
if (result == 0) { if (result == 0) {
m_started = false; m_started = false;
} }
} }
return result == 0; return result == 0;
} }
void Thread::enter() { void Thread::enter() {
m_running = true; m_running = true;
run(); run();
m_running = false; m_running = false;
} }
WaitThread::WaitThread() WaitThread::WaitThread()
: Thread() { : Thread() {
pthread_mutex_init(&m_mutex, NULL); pthread_mutex_init(&m_mutex, NULL);
pthread_cond_init(&m_cond, NULL); pthread_cond_init(&m_cond, NULL);
} }
WaitThread::~WaitThread() { WaitThread::~WaitThread() {
pthread_mutex_destroy(&m_mutex); pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_cond); pthread_cond_destroy(&m_cond);
} }
void WaitThread::stop() { void WaitThread::stop() {
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond); pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
Thread::stop(); Thread::stop();
} }
bool WaitThread::join() { bool WaitThread::join() {
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond); pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return Thread::join(); return Thread::join();
} }
bool WaitThread::Wait(int seconds) { bool WaitThread::Wait(int seconds) {
struct timespec t; struct timespec t;
clockGettime(&t); clockGettime(&t);
t.tv_sec += seconds; t.tv_sec += seconds;
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
pthread_cond_timedwait(&m_cond, &m_mutex, &t); pthread_cond_timedwait(&m_cond, &m_mutex, &t);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
return isRunning(); return isRunning();
} }
+82 -82
View File
@@ -27,79 +27,79 @@
* wrapper class for pthread. * wrapper class for pthread.
*/ */
class Thread { class Thread {
public: public:
/** /**
* constructor. * constructor.
*/ */
Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {} Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {}
/** /**
* virtual destructor. * virtual destructor.
*/ */
virtual ~Thread(); virtual ~Thread();
/** /**
* Thread entry helper for pthread_create. * Thread entry helper for pthread_create.
* @param arg pointer to the @a Thread. * @param arg pointer to the @a Thread.
* @return NULL. * @return NULL.
*/ */
static void* runThread(void* arg); static void* runThread(void* arg);
/** /**
* Return whether this @a Thread is still running and not yet stopped. * 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. * @return true if this @a Thread is till running and not yet stopped.
*/ */
virtual bool isRunning() { return m_running && !m_stopped; } virtual bool isRunning() { return m_running && !m_stopped; }
/** /**
* Create the native thread and set its name. * Create the native thread and set its name.
* @param name the thread name to show in the process list. * @param name the thread name to show in the process list.
* @return whether the thread was started. * @return whether the thread was started.
*/ */
virtual bool start(const char* name); virtual bool start(const char* name);
/** /**
* Notify the thread that it shall stop. * Notify the thread that it shall stop.
*/ */
virtual void stop() { m_stopped = true; } virtual void stop() { m_stopped = true; }
/** /**
* Join the thread. * Join the thread.
* @return whether the thread was joined. * @return whether the thread was joined.
*/ */
virtual bool join(); virtual bool join();
/** /**
* Get the thread id. * Get the thread id.
* @return the thread id. * @return the thread id.
*/ */
pthread_t self() { return m_threadid; } pthread_t self() { return m_threadid; }
protected: protected:
/** /**
* Thread entry method to be overridden by derived class. * Thread entry method to be overridden by derived class.
*/ */
virtual void run() = 0; virtual void run() = 0;
private: private:
/** /**
* Enter the Thread loop by calling run(). * Enter the Thread loop by calling run().
*/ */
void enter(); void enter();
/** own thread id */ /** own thread id */
pthread_t m_threadid; pthread_t m_threadid;
/** Whether the thread was started. */ /** Whether the thread was started. */
bool m_started; bool m_started;
/** Whether the thread is still running (i.e. in @a run() ). */ /** Whether the thread is still running (i.e. in @a run() ). */
bool m_running; bool m_running;
/** Whether the thread was stopped by @a stop() or @a join(). */ /** Whether the thread was stopped by @a stop() or @a join(). */
bool m_stopped; bool m_stopped;
}; };
@@ -107,37 +107,37 @@ class Thread {
* A @a Thread that can be waited on. * A @a Thread that can be waited on.
*/ */
class WaitThread : public Thread { class WaitThread : public Thread {
public: public:
/** /**
* Constructor. * Constructor.
*/ */
WaitThread(); WaitThread();
/** /**
* Destructor. * Destructor.
*/ */
virtual ~WaitThread(); virtual ~WaitThread();
// @copydoc // @copydoc
virtual void stop(); virtual void stop();
// @copydoc // @copydoc
virtual bool join(); virtual bool join();
/** /**
* Wait for the specified amount of time. * Wait for the specified amount of time.
* @param seconds the number of seconds to wait. * @param seconds the number of seconds to wait.
* @return true if this @a WaitThread is still running and not yet stopped. * @return true if this @a WaitThread is still running and not yet stopped.
*/ */
bool Wait(int seconds); bool Wait(int seconds);
private: private:
/** the mutex for waiting. */ /** the mutex for waiting. */
pthread_mutex_t m_mutex; pthread_mutex_t m_mutex;
/** the condition for waiting. */ /** the condition for waiting. */
pthread_cond_t m_cond; pthread_cond_t m_cond;
}; };
#endif // LIB_UTILS_THREAD_H_ #endif // LIB_UTILS_THREAD_H_
+183 -183
View File
@@ -17,13 +17,13 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include <argp.h> #include <argp.h>
#include <string.h> #include <string.h>
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
# include <poll.h> # include <poll.h>
#endif #endif
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
@@ -39,20 +39,20 @@ using std::endl;
/** A structure holding all program options. */ /** A structure holding all program options. */
struct options { struct options {
const char* server; //!< ebusd server host (name or ip) [localhost] const char* server; //!< ebusd server host (name or ip) [localhost]
uint16_t port; //!< ebusd server port [8888] uint16_t port; //!< ebusd server port [8888]
char* const *args; //!< arguments to pass to ebusd char* const *args; //!< arguments to pass to ebusd
unsigned int argCount; //!< number of arguments to pass to ebusd unsigned int argCount; //!< number of arguments to pass to ebusd
}; };
/** the program options. */ /** the program options. */
static struct options opt = { static struct options opt = {
"localhost", // server "localhost", // server
8888, // port 8888, // port
NULL, // args NULL, // args
0 // argCount 0 // argCount
}; };
/** the version string of the program. */ /** the version string of the program. */
@@ -63,21 +63,21 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT"";
/** the documentation of the program. */ /** the documentation of the program. */
static const char argpdoc[] = static const char argpdoc[] =
"Client for acessing "PACKAGE" via TCP.\n" "Client for acessing "PACKAGE" via TCP.\n"
"\v" "\v"
"If given, send COMMAND together with CMDOPT options to "PACKAGE".\n" "If given, send COMMAND together with CMDOPT options to "PACKAGE".\n"
"Use 'help' as COMMAND for help on available "PACKAGE" commands."; "Use 'help' as COMMAND for help on available "PACKAGE" commands.";
/** the description of the accepted arguments. */ /** the description of the accepted arguments. */
static char argpargsdoc[] = "\nCOMMAND [CMDOPT...]"; static char argpargsdoc[] = "\nCOMMAND [CMDOPT...]";
/** the definition of the known program arguments. */ /** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = { static const struct argp_option argpoptions[] = {
{NULL, 0, NULL, 0, "Options:", 1 }, {NULL, 0, NULL, 0, "Options:", 1 },
{"server", 's', "HOST", 0, "Connect to HOST running "PACKAGE" (name or IP) [localhost]", 0 }, {"server", 's', "HOST", 0, "Connect to HOST running "PACKAGE" (name or IP) [localhost]", 0 },
{"port", 'p', "PORT", 0, "Connect to PORT on HOST [8888]", 0 }, {"port", 'p', "PORT", 0, "Connect to PORT on HOST [8888]", 0 },
{NULL, 0, NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0, NULL, 0 },
}; };
/** /**
@@ -87,216 +87,216 @@ static const struct argp_option argpoptions[] = {
* @param state the parsing state. * @param state the parsing state.
*/ */
error_t parse_opt(int key, char *arg, struct argp_state *state) { error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input; struct options *opt = (struct options*)state->input;
char* strEnd = NULL; char* strEnd = NULL;
unsigned int port; unsigned int port;
switch (key) { switch (key) {
// Device settings: // Device settings:
case 's': // --server=localhost case 's': // --server=localhost
if (arg == NULL || arg[0] == 0) { if (arg == NULL || arg[0] == 0) {
argp_error(state, "invalid server"); argp_error(state, "invalid server");
return EINVAL; return EINVAL;
} }
opt->server = arg; opt->server = arg;
break; break;
case 'p': // --port=8888 case 'p': // --port=8888
port = strtoul(arg, &strEnd, 10); port = strtoul(arg, &strEnd, 10);
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) { if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) {
argp_error(state, "invalid port"); argp_error(state, "invalid port");
return EINVAL; return EINVAL;
} }
opt->port = (uint16_t)port; opt->port = (uint16_t)port;
break; break;
case ARGP_KEY_ARGS: case ARGP_KEY_ARGS:
opt->args = state->argv + state->next; opt->args = state->argv + state->next;
opt->argCount = state->argc - state->next; opt->argCount = state->argc - state->next;
break; break;
default: default:
return ARGP_ERR_UNKNOWN; return ARGP_ERR_UNKNOWN;
} }
return 0; return 0;
} }
string fetchData(TCPSocket* socket, bool& listening) { string fetchData(TCPSocket* socket, bool& listening) {
char data[1024]; char data[1024];
ssize_t datalen; ssize_t datalen;
ostringstream ostream; ostringstream ostream;
string message, sendmessage; string message, sendmessage;
int ret; int ret;
struct timespec tdiff; struct timespec tdiff;
// set timeout // set timeout
tdiff.tv_sec = 0; tdiff.tv_sec = 0;
tdiff.tv_nsec = 1E8; tdiff.tv_nsec = 1E8;
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
int nfds = 2; int nfds = 2;
struct pollfd fds[nfds]; struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds)); memset(fds, 0, sizeof(fds));
fds[0].fd = STDIN_FILENO; fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN; fds[0].events = POLLIN;
fds[1].fd = socket->getFD(); fds[1].fd = socket->getFD();
fds[1].events = POLLIN; fds[1].events = POLLIN;
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
int maxfd; int maxfd;
fd_set checkfds; fd_set checkfds;
FD_ZERO(&checkfds); FD_ZERO(&checkfds);
FD_SET(STDIN_FILENO, &checkfds); FD_SET(STDIN_FILENO, &checkfds);
FD_SET(socket->getFD(), &checkfds); FD_SET(socket->getFD(), &checkfds);
maxfd = STDIN_FILENO; maxfd = STDIN_FILENO;
if (socket->getFD() > maxfd) { if (socket->getFD() > maxfd) {
maxfd = socket->getFD(); maxfd = socket->getFD();
} }
#endif #endif
#endif #endif
while(true) { while(true) {
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// wait for new fd event // wait for new fd event
ret = ppoll(fds, nfds, &tdiff, NULL); ret = ppoll(fds, nfds, &tdiff, NULL);
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// set readfds to inital checkfds // set readfds to inital checkfds
fd_set readfds = checkfds; fd_set readfds = checkfds;
// wait for new fd event // wait for new fd event
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL); ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
#endif #endif
#endif #endif
bool newData = false; bool newData = false;
bool newInput = false; bool newInput = false;
if (ret != 0) { if (ret != 0) {
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
// new data from notify // new data from notify
newInput = fds[0].revents & POLLIN; newInput = fds[0].revents & POLLIN;
// new data from socket // new data from socket
newData = fds[1].revents & POLLIN; newData = fds[1].revents & POLLIN;
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
// new data from notify // new data from notify
newInput = FD_ISSET(STDIN_FILENO, &readfds); newInput = FD_ISSET(STDIN_FILENO, &readfds);
// new data from socket // new data from socket
newData = FD_ISSET(socket->getFD(), &readfds); newData = FD_ISSET(socket->getFD(), &readfds);
#endif #endif
#endif #endif
} }
if (newData) { if (newData) {
if (socket->isValid()) { if (socket->isValid()) {
datalen = socket->recv(data, sizeof(data)); datalen = socket->recv(data, sizeof(data));
if (datalen < 0) { if (datalen < 0) {
perror("recv"); perror("recv");
break; break;
} }
for (int i = 0; i < datalen; i++) { for (int i = 0; i < datalen; i++) {
ostream << data[i]; ostream << data[i];
} }
string str = ostream.str(); string str = ostream.str();
if (listening) { if (listening) {
return str; return str;
} }
if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') { if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') {
return str; return str;
} }
} else { } else {
break; break;
} }
} else if (newInput) { } else if (newInput) {
getline(cin, message); getline(cin, message);
sendmessage = message+'\n'; sendmessage = message+'\n';
socket->send(sendmessage.c_str(), sendmessage.size()); socket->send(sendmessage.c_str(), sendmessage.size());
if (strcasecmp(message.c_str(), "Q") == 0 if (strcasecmp(message.c_str(), "Q") == 0
|| strcasecmp(message.c_str(), "QUIT") == 0 || strcasecmp(message.c_str(), "QUIT") == 0
|| strcasecmp(message.c_str(), "STOP") == 0) { || strcasecmp(message.c_str(), "STOP") == 0) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
return ""; return "";
} }
message.clear(); message.clear();
} }
} }
return ostream.str(); return ostream.str();
} }
void connect(const char* host, uint16_t port, char* const *args, int argCount) { void connect(const char* host, uint16_t port, char* const *args, int argCount) {
TCPClient* client = new TCPClient(); TCPClient* client = new TCPClient();
TCPSocket* socket = client->connect(host, port); TCPSocket* socket = client->connect(host, port);
bool once = args != NULL && argCount > 0; bool once = args != NULL && argCount > 0;
if (socket != NULL) { if (socket != NULL) {
string message, sendmessage; string message, sendmessage;
do { do {
bool listening = false; bool listening = false;
if (!once) { if (!once) {
cout << host << ": "; cout << host << ": ";
getline(cin, message); getline(cin, message);
} else { } else {
for (int i = 0; i < argCount; i++) { for (int i = 0; i < argCount; i++) {
if (i > 0) { if (i > 0) {
message += " "; message += " ";
} }
bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL; bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL;
if (quote) { if (quote) {
message += "\""; message += "\"";
} }
message += args[i]; message += args[i];
if (quote) { if (quote) {
message += "\""; message += "\"";
} }
} }
} }
sendmessage = message+'\n'; sendmessage = message+'\n';
socket->send(sendmessage.c_str(), sendmessage.size()); socket->send(sendmessage.c_str(), sendmessage.size());
if (strcasecmp(message.c_str(), "Q") == 0 if (strcasecmp(message.c_str(), "Q") == 0
|| strcasecmp(message.c_str(), "QUIT") == 0 || strcasecmp(message.c_str(), "QUIT") == 0
|| strcasecmp(message.c_str(), "STOP") == 0) || strcasecmp(message.c_str(), "STOP") == 0)
break; break;
if (message.length() > 0) { if (message.length() > 0) {
if (strcasecmp(message.c_str(), "L") == 0 if (strcasecmp(message.c_str(), "L") == 0
|| strcasecmp(message.c_str(), "LISTEN") == 0) { || strcasecmp(message.c_str(), "LISTEN") == 0) {
listening = true; listening = true;
while (listening && !cin.eof()) { while (listening && !cin.eof()) {
string result(fetchData(socket, listening)); string result(fetchData(socket, listening));
cout << result; cout << result;
if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) { if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) {
break; break;
} }
} }
} else { } else {
cout << fetchData(socket, listening); cout << fetchData(socket, listening);
} }
} }
} while (!once && !cin.eof()); } while (!once && !cin.eof());
delete socket; delete socket;
} else { } else {
cout << "error connecting to " << host << ":" << port << endl; cout << "error connecting to " << host << ":" << port << endl;
} }
delete client; delete client;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
return EINVAL; return EINVAL;
} }
connect(opt.server, opt.port, opt.args, opt.argCount); connect(opt.server, opt.port, opt.args, opt.argCount);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
+91 -91
View File
@@ -17,7 +17,7 @@
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include <config.h> # include <config.h>
#endif #endif
#include <argp.h> #include <argp.h>
@@ -42,18 +42,18 @@ using ebusd::Device;
/** A structure holding all program options. */ /** A structure holding all program options. */
struct options { struct options {
const char* device; //!< device to write to [/dev/ttyUSB60] const char* device; //!< device to write to [/dev/ttyUSB60]
unsigned int time; //!< delay between bytes in us [10000] unsigned int time; //!< delay between bytes in us [10000]
const char* dumpFile; //!< dump file to read const char* dumpFile; //!< dump file to read
}; };
/** the program options. */ /** the program options. */
static struct options opt = { static struct options opt = {
"/dev/ttyUSB60", // device "/dev/ttyUSB60", // device
10000, // time 10000, // time
"/tmp/ebus_dump.bin", // dumpFile "/tmp/ebus_dump.bin", // dumpFile
}; };
/** the version string of the program. */ /** the version string of the program. */
@@ -64,27 +64,27 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT"";
/** the documentation of the program. */ /** the documentation of the program. */
static const char argpdoc[] = static const char argpdoc[] =
"Feed data from an "PACKAGE" DUMPFILE to a serial device.\n" "Feed data from an "PACKAGE" DUMPFILE to a serial device.\n"
"\v" "\v"
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n" "With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
"\n" "\n"
"Example for setting up two pseudo terminals with 'socat':\n" "Example for setting up two pseudo terminals with 'socat':\n"
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n" " 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
" 2. create symbol links to appropriate devices, e.g.\n" " 2. create symbol links to appropriate devices, e.g.\n"
" 'ln -s /dev/pts/2 /dev/ttyUSB60'\n" " 'ln -s /dev/pts/2 /dev/ttyUSB60'\n"
" 'ln -s /dev/pts/3 /dev/ttyUSB20'\n" " 'ln -s /dev/pts/3 /dev/ttyUSB20'\n"
" 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n" " 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n"
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n"; " 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n";
/** the description of the accepted arguments. */ /** the description of the accepted arguments. */
static char argpargsdoc[] = "[DUMPFILE]"; static char argpargsdoc[] = "[DUMPFILE]";
/** the definition of the known program arguments. */ /** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = { static const struct argp_option argpoptions[] = {
{"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 }, {"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 },
{"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 0 }, {"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 0 },
{NULL, 0, NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0, NULL, 0 },
}; };
/** /**
@@ -94,80 +94,80 @@ static const struct argp_option argpoptions[] = {
* @param state the parsing state. * @param state the parsing state.
*/ */
error_t parse_opt(int key, char *arg, struct argp_state *state) { error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input; struct options *opt = (struct options*)state->input;
char* strEnd = NULL; char* strEnd = NULL;
switch (key) { switch (key) {
// Device settings: // Device settings:
case 'd': // --device=/dev/ttyUSB60 case 'd': // --device=/dev/ttyUSB60
if (arg == NULL || arg[0] == 0) { if (arg == NULL || arg[0] == 0) {
argp_error(state, "invalid device"); argp_error(state, "invalid device");
return EINVAL; return EINVAL;
} }
opt->device = arg; opt->device = arg;
break; break;
case 't': // --time=10000 case 't': // --time=10000
opt->time = (unsigned int)strtoul(arg, &strEnd, 10); opt->time = (unsigned int)strtoul(arg, &strEnd, 10);
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) { if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) {
argp_error(state, "invalid time"); argp_error(state, "invalid time");
return EINVAL; return EINVAL;
} }
break; break;
case ARGP_KEY_ARG: case ARGP_KEY_ARG:
if (state->arg_num == 0) { if (state->arg_num == 0) {
if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) { if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid dumpfile"); argp_error(state, "invalid dumpfile");
return EINVAL; return EINVAL;
} }
opt->dumpFile = arg; opt->dumpFile = arg;
} else { } else {
return ARGP_ERR_UNKNOWN; return ARGP_ERR_UNKNOWN;
} }
break; break;
default: default:
return ARGP_ERR_UNKNOWN; return ARGP_ERR_UNKNOWN;
} }
return 0; return 0;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL }; struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0); setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) { if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
return EINVAL; return EINVAL;
} }
Device* device = Device::create(opt.device, false, false, NULL); Device* device = Device::create(opt.device, false, false, NULL);
if (device == NULL) { if (device == NULL) {
cout << "unable to create device " << opt.device << endl; cout << "unable to create device " << opt.device << endl;
return EINVAL; return EINVAL;
} }
result_t result = device->open(); result_t result = device->open();
if (result != ebusd::RESULT_OK) { if (result != ebusd::RESULT_OK) {
cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl; cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl;
} }
if (!device->isValid()) { if (!device->isValid()) {
cout << "device " << opt.device << " not available" << endl; cout << "device " << opt.device << " not available" << endl;
} else { } else {
cout << "device opened" << endl; cout << "device opened" << endl;
fstream file(opt.dumpFile, ios::in | ios::binary); fstream file(opt.dumpFile, ios::in | ios::binary);
if (file.is_open()) { if (file.is_open()) {
while (true) { while (true) {
unsigned char byte = (unsigned char)file.get(); unsigned char byte = (unsigned char)file.get();
if (file.eof()) { if (file.eof()) {
break; break;
} }
cout << hex << setw(2) << setfill('0') cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(byte) << endl; << static_cast<unsigned>(byte) << endl;
device->send(byte); device->send(byte);
usleep(opt.time); usleep(opt.time);
} }
file.close(); file.close();
} else { } else {
cout << "error opening file " << opt.dumpFile << endl; cout << "error opening file " << opt.dumpFile << endl;
} }
} }
delete device; delete device;
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }