use spaces instead of tab
This commit is contained in:
+980
-980
File diff suppressed because it is too large
Load Diff
+427
-427
File diff suppressed because it is too large
Load Diff
+20
-20
@@ -17,14 +17,14 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "datahandler.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
#ifdef HAVE_MQTT
|
||||
# include "mqtthandler.h"
|
||||
# include "mqtthandler.h"
|
||||
#endif
|
||||
|
||||
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. */
|
||||
static struct argp_child g_argp_children[
|
||||
#ifdef HAVE_MQTT
|
||||
1
|
||||
1
|
||||
#endif
|
||||
+1
|
||||
+1
|
||||
];
|
||||
|
||||
const struct argp_child* datahandler_getargs() {
|
||||
size_t count = 0;
|
||||
size_t count = 0;
|
||||
#ifdef HAVE_MQTT
|
||||
g_argp_children[count++] = *mqtthandler_getargs();
|
||||
g_argp_children[count++] = *mqtthandler_getargs();
|
||||
#endif
|
||||
if (count > 0) {
|
||||
g_argp_children[count] = g_last_argp_child;
|
||||
return g_argp_children;
|
||||
}
|
||||
return NULL;
|
||||
if (count > 0) {
|
||||
g_argp_children[count] = g_last_argp_child;
|
||||
return g_argp_children;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<DataHandler*>& handlers) {
|
||||
bool success = true;
|
||||
bool success = true;
|
||||
#ifdef HAVE_MQTT
|
||||
DataHandler* handler = mqtthandler_register(busHandler, messages);
|
||||
if (handler) {
|
||||
handlers.push_back(handler);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
DataHandler* handler = mqtthandler_register(busHandler, messages);
|
||||
if (handler) {
|
||||
handlers.push_back(handler);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
return success;
|
||||
}
|
||||
|
||||
void DataSink::notifyUpdate(Message* message) {
|
||||
m_updatedMessages[message]++;
|
||||
m_updatedMessages[message]++;
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+58
-58
@@ -58,33 +58,33 @@ bool datahandler_register(BusHandler* busHandler, MessageMap* messages, list<Dat
|
||||
* Base class for all kinds of data handlers.
|
||||
*/
|
||||
class DataHandler {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
DataHandler() {}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
DataHandler() {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataHandler() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataHandler() {}
|
||||
|
||||
/**
|
||||
* Called to start the @a DataHandler.
|
||||
*/
|
||||
virtual void start() = 0;
|
||||
/**
|
||||
* Called to start the @a DataHandler.
|
||||
*/
|
||||
virtual void start() = 0;
|
||||
|
||||
/**
|
||||
* Return whether this is a @a DataSink instance.
|
||||
* @return whether this is a @a DataSink instance.
|
||||
*/
|
||||
virtual bool isDataSink() { return false; }
|
||||
/**
|
||||
* Return whether this is a @a DataSink instance.
|
||||
* @return whether this is a @a DataSink instance.
|
||||
*/
|
||||
virtual bool isDataSink() { return false; }
|
||||
|
||||
/**
|
||||
* Return whether this is a @a DataSource instance.
|
||||
* @return whether this is a @a DataSource instance.
|
||||
*/
|
||||
virtual bool isDataSource() { return false; }
|
||||
/**
|
||||
* Return whether this is a @a DataSource instance.
|
||||
* @return whether this is a @a DataSource instance.
|
||||
*/
|
||||
virtual bool isDataSource() { return false; }
|
||||
};
|
||||
|
||||
|
||||
@@ -92,30 +92,30 @@ class DataHandler {
|
||||
* Base class for listening to data updates.
|
||||
*/
|
||||
class DataSink : virtual public DataHandler {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
DataSink() {}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
DataSink() {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataSink() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataSink() {}
|
||||
|
||||
/**
|
||||
* Notify the sink of an updated @a Message.
|
||||
* @param message the updated @a Message.
|
||||
*/
|
||||
virtual void notifyUpdate(Message* message);
|
||||
/**
|
||||
* Notify the sink of an updated @a Message.
|
||||
* @param message the updated @a Message.
|
||||
*/
|
||||
virtual void notifyUpdate(Message* message);
|
||||
|
||||
// @copydoc
|
||||
virtual bool isDataSink() { return true; }
|
||||
// @copydoc
|
||||
virtual bool isDataSink() { return true; }
|
||||
|
||||
|
||||
protected:
|
||||
/** a map of updated @p Message instances. */
|
||||
map<Message*, int> m_updatedMessages;
|
||||
protected:
|
||||
/** a map of updated @p Message instances. */
|
||||
map<Message*, int> m_updatedMessages;
|
||||
};
|
||||
|
||||
|
||||
@@ -123,26 +123,26 @@ class DataSink : virtual public DataHandler {
|
||||
* Base class providing data to be sent on the bus.
|
||||
*/
|
||||
class DataSource : virtual public DataHandler {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
*/
|
||||
explicit DataSource(BusHandler* busHandler)
|
||||
: m_busHandler(busHandler) {}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
*/
|
||||
explicit DataSource(BusHandler* busHandler)
|
||||
: m_busHandler(busHandler) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataSource() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataSource() {}
|
||||
|
||||
// @copydoc
|
||||
virtual bool isDataSource() { return true; }
|
||||
// @copydoc
|
||||
virtual bool isDataSource() { return true; }
|
||||
|
||||
|
||||
protected:
|
||||
/** the @a BusHandler instance. */
|
||||
BusHandler* m_busHandler;
|
||||
protected:
|
||||
/** the @a BusHandler instance. */
|
||||
BusHandler* m_busHandler;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+768
-768
File diff suppressed because it is too large
Load Diff
+32
-32
@@ -31,44 +31,44 @@ namespace ebusd {
|
||||
|
||||
/** A structure holding all program options. */
|
||||
struct options {
|
||||
const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0]
|
||||
bool noDeviceCheck; //!< skip serial eBUS device test
|
||||
bool readOnly; //!< read-only access to the device
|
||||
bool initialSend; //!< send an initial escape symbol after connecting device
|
||||
int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
|
||||
const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0]
|
||||
bool noDeviceCheck; //!< skip serial eBUS device test
|
||||
bool readOnly; //!< read-only access to the device
|
||||
bool initialSend; //!< send an initial escape symbol after connecting device
|
||||
int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
|
||||
|
||||
const char* configPath; //!< path to CSV configuration files [/etc/ebusd]
|
||||
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)
|
||||
int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop
|
||||
int pollInterval; //!< poll interval in seconds, 0 to disable [5]
|
||||
const char* configPath; //!< path to CSV configuration files [/etc/ebusd]
|
||||
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)
|
||||
int checkConfig; //!< check CSV config files ( != 0) and optionally dump (2), then stop
|
||||
int pollInterval; //!< poll interval in seconds, 0 to disable [5]
|
||||
|
||||
unsigned char address; //!< own bus address [31]
|
||||
bool answer; //!< answer to requests from other masters
|
||||
int acquireTimeout; //!< bus acquisition timeout in us [9400]
|
||||
int acquireRetries; //!< number of retries for bus acquisition [3]
|
||||
int sendRetries; //!< number of retries for failed sends [2]
|
||||
int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
|
||||
int masterCount; //!< expected number of masters for arbitration [0]
|
||||
bool generateSyn; //!< enable AUTO-SYN symbol generation
|
||||
unsigned char address; //!< own bus address [31]
|
||||
bool answer; //!< answer to requests from other masters
|
||||
int acquireTimeout; //!< bus acquisition timeout in us [9400]
|
||||
int acquireRetries; //!< number of retries for bus acquisition [3]
|
||||
int sendRetries; //!< number of retries for failed sends [2]
|
||||
int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
|
||||
int masterCount; //!< expected number of masters for arbitration [0]
|
||||
bool generateSyn; //!< enable AUTO-SYN symbol generation
|
||||
|
||||
bool foreground; //!< run in foreground
|
||||
bool enableHex; //!< enable hex command
|
||||
const char* pidFile; //!< PID file name [/var/run/ebusd.pid]
|
||||
uint16_t port; //!< port to listen for command line connections [8888]
|
||||
bool localOnly; //!< listen on 127.0.0.1 interface only
|
||||
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]
|
||||
bool foreground; //!< run in foreground
|
||||
bool enableHex; //!< enable hex command
|
||||
const char* pidFile; //!< PID file name [/var/run/ebusd.pid]
|
||||
uint16_t port; //!< port to listen for command line connections [8888]
|
||||
bool localOnly; //!< listen on 127.0.0.1 interface only
|
||||
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* 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
|
||||
const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log]
|
||||
unsigned int logRawSize; //!< maximum size of raw log file in kB [100]
|
||||
bool logRaw; //!< raw log each received/sent byte on the bus
|
||||
const char* logRawFile; //!< name of raw log file [/var/log/ebusd.log]
|
||||
unsigned int logRawSize; //!< maximum size of raw log file in kB [100]
|
||||
|
||||
bool dump; //!< binary dump received bytes
|
||||
const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin]
|
||||
unsigned int dumpSize; //!< maximum size of dump file in kB [100]
|
||||
bool dump; //!< binary dump received bytes
|
||||
const char* dumpFile; //!< name of dump file [/tmp/ebusd_dump.bin]
|
||||
unsigned int dumpSize; //!< maximum size of dump file in kB [100]
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+1388
-1388
File diff suppressed because it is too large
Load Diff
+174
-174
@@ -36,220 +36,220 @@ namespace ebusd {
|
||||
* The main loop handling requests from connected clients.
|
||||
*/
|
||||
class MainLoop : public Thread, DeviceListener {
|
||||
public:
|
||||
/**
|
||||
* Construct the main loop and create network and bus handling components.
|
||||
* @param opt the program options.
|
||||
* @param device the @a Device instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
*/
|
||||
MainLoop(const struct options opt, Device *device, MessageMap* messages);
|
||||
public:
|
||||
/**
|
||||
* Construct the main loop and create network and bus handling components.
|
||||
* @param opt the program options.
|
||||
* @param device the @a Device instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
*/
|
||||
MainLoop(const struct options opt, Device *device, MessageMap* messages);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~MainLoop();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~MainLoop();
|
||||
|
||||
/**
|
||||
* Get the @a BusHandler instance.
|
||||
* @return the created @a BusHandler instance.
|
||||
*/
|
||||
BusHandler* getBusHandler() { return m_busHandler; }
|
||||
/**
|
||||
* Get the @a BusHandler instance.
|
||||
* @return the created @a BusHandler instance.
|
||||
*/
|
||||
BusHandler* getBusHandler() { return m_busHandler; }
|
||||
|
||||
/**
|
||||
* Add a client @a NetMessage to the queue.
|
||||
* @param message the client @a NetMessage to handle.
|
||||
*/
|
||||
void addMessage(NetMessage* message) { m_netQueue.push(message); }
|
||||
/**
|
||||
* Add a client @a NetMessage to the queue.
|
||||
* @param message the client @a NetMessage to handle.
|
||||
*/
|
||||
void addMessage(NetMessage* message) { m_netQueue.push(message); }
|
||||
|
||||
// @copydoc
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received);
|
||||
// @copydoc
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received);
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void run();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void run();
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Decode and execute client message.
|
||||
* @param data the data string to decode (may be empty).
|
||||
* @param connected set to false when the client connection shall be closed.
|
||||
* @param isHttp true for HTTP message.
|
||||
* @param listening set to true when the client is in listening mode.
|
||||
* @param reload set to true when the configuration files were reloaded.
|
||||
* @return result string to send back to the client.
|
||||
*/
|
||||
string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload);
|
||||
private:
|
||||
/**
|
||||
* Decode and execute client message.
|
||||
* @param data the data string to decode (may be empty).
|
||||
* @param connected set to false when the client connection shall be closed.
|
||||
* @param isHttp true for HTTP message.
|
||||
* @param listening set to true when the client is in listening mode.
|
||||
* @param reload set to true when the configuration files were reloaded.
|
||||
* @return result string to send back to the client.
|
||||
*/
|
||||
string decodeMessage(const string& data, const bool isHttp, bool& connected, bool& listening, bool& reload);
|
||||
|
||||
/**
|
||||
* Parse the hex master message from the remaining arguments.
|
||||
* @param args the arguments passed to the command.
|
||||
* @param argPos the index of the first argument to parse.
|
||||
* @param master the master @a SymbolString to write the data to.
|
||||
* @return the result from parsing the arguments.
|
||||
*/
|
||||
result_t parseHexMaster(vector<string> &args, size_t argPos, SymbolString& master);
|
||||
/**
|
||||
* Parse the hex master message from the remaining arguments.
|
||||
* @param args the arguments passed to the command.
|
||||
* @param argPos the index of the first argument to parse.
|
||||
* @param master the master @a SymbolString to write the data to.
|
||||
* @return the result from parsing the arguments.
|
||||
*/
|
||||
result_t parseHexMaster(vector<string> &args, size_t argPos, SymbolString& master);
|
||||
|
||||
/**
|
||||
* Execute the read command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeRead(vector<string> &args);
|
||||
/**
|
||||
* Execute the read command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeRead(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the write command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeWrite(vector<string> &args);
|
||||
/**
|
||||
* Execute the write command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeWrite(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the hex command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeHex(vector<string> &args);
|
||||
/**
|
||||
* Execute the hex command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeHex(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the find command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeFind(vector<string> &args);
|
||||
/**
|
||||
* Execute the find command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeFind(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the listen command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeListen(vector<string> &args, bool& listening);
|
||||
/**
|
||||
* Execute the listen command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeListen(vector<string> &args, bool& listening);
|
||||
|
||||
/**
|
||||
* Execute the state command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeState(vector<string> &args);
|
||||
/**
|
||||
* Execute the state command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeState(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the grab command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeGrab(vector<string> &args);
|
||||
/**
|
||||
* Execute the grab command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeGrab(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the scan command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeScan(vector<string> &args);
|
||||
/**
|
||||
* Execute the scan command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeScan(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the log command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeLog(vector<string> &args);
|
||||
/**
|
||||
* Execute the log command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeLog(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the raw command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeRaw(vector<string> &args);
|
||||
/**
|
||||
* Execute the raw command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeRaw(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the dump command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeDump(vector<string> &args);
|
||||
/**
|
||||
* Execute the dump command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeDump(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the reload command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeReload(vector<string> &args);
|
||||
/**
|
||||
* Execute the reload command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeReload(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the info command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeInfo(vector<string> &args);
|
||||
/**
|
||||
* Execute the info command.
|
||||
* @param args the arguments passed to the command (starting with the command itself), or empty for help.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeInfo(vector<string> &args);
|
||||
|
||||
/**
|
||||
* Execute the quit command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeQuit(vector<string> &args, bool& connected);
|
||||
/**
|
||||
* Execute the quit command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeQuit(vector<string> &args, bool& connected);
|
||||
|
||||
/**
|
||||
* Execute the help command.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeHelp();
|
||||
/**
|
||||
* Execute the help command.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeHelp();
|
||||
|
||||
/**
|
||||
* Execute the HTTP GET command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeGet(vector<string> &args, bool& connected);
|
||||
/**
|
||||
* Execute the HTTP GET command.
|
||||
* @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.
|
||||
* @return the result string.
|
||||
*/
|
||||
string executeGet(vector<string> &args, bool& connected);
|
||||
|
||||
/** the @a Device instance. */
|
||||
Device* m_device;
|
||||
/** the @a Device instance. */
|
||||
Device* m_device;
|
||||
|
||||
/** the number of reconnects requested from the @a Device. */
|
||||
unsigned int m_reconnectCount;
|
||||
/** the number of reconnects requested from the @a Device. */
|
||||
unsigned int m_reconnectCount;
|
||||
|
||||
/** the @a RotateFile for writing sent/received bytes in log format, or NULL. */
|
||||
RotateFile* m_logRawFile;
|
||||
/** the @a RotateFile for writing sent/received bytes in log format, or NULL. */
|
||||
RotateFile* m_logRawFile;
|
||||
|
||||
/** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */
|
||||
bool m_logRawEnabled;
|
||||
/** whether raw logging to @p logNotice is enabled (only relevant if m_logRawFile is NULL). */
|
||||
bool m_logRawEnabled;
|
||||
|
||||
/** the @a RotateFile for dumping received data, or NULL. */
|
||||
RotateFile* m_dumpFile;
|
||||
/** the @a RotateFile for dumping received data, or NULL. */
|
||||
RotateFile* m_dumpFile;
|
||||
|
||||
/** the @a MessageMap instance. */
|
||||
MessageMap* m_messages;
|
||||
/** the @a MessageMap instance. */
|
||||
MessageMap* m_messages;
|
||||
|
||||
/** the own master address for sending on the bus. */
|
||||
const unsigned char m_address;
|
||||
/** the own master address for sending on the bus. */
|
||||
const unsigned char m_address;
|
||||
|
||||
/** whether to pick configuration files matching initial scan. */
|
||||
const bool m_scanConfig;
|
||||
/** whether to pick configuration files matching initial scan. */
|
||||
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). */
|
||||
const unsigned char m_initialScan;
|
||||
/** 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;
|
||||
|
||||
/** whether to enable the hex command. */
|
||||
const bool m_enableHex;
|
||||
/** whether to enable the hex command. */
|
||||
const bool m_enableHex;
|
||||
|
||||
/** the created @a BusHandler instance. */
|
||||
BusHandler* m_busHandler;
|
||||
/** the created @a BusHandler instance. */
|
||||
BusHandler* m_busHandler;
|
||||
|
||||
/** the created @a Network instance. */
|
||||
Network* m_network;
|
||||
/** the created @a Network instance. */
|
||||
Network* m_network;
|
||||
|
||||
/** the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*> m_netQueue;
|
||||
/** the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*> m_netQueue;
|
||||
|
||||
/** the path for HTML files served by the HTTP port. */
|
||||
string m_htmlPath;
|
||||
/** the path for HTML files served by the HTTP port. */
|
||||
string m_htmlPath;
|
||||
|
||||
/** the registered @a DataHandler instances. */
|
||||
list<DataHandler*> m_dataHandlers;
|
||||
/** the registered @a DataHandler instances. */
|
||||
list<DataHandler*> m_dataHandlers;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+337
-337
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "mqtthandler.h"
|
||||
@@ -29,12 +29,12 @@ using std::dec;
|
||||
|
||||
/** the definition of the MQTT arguments. */
|
||||
static const struct argp_option g_mqtt_argp_options[] = {
|
||||
{NULL, 0, NULL, 0, "MQTT options:", 1 },
|
||||
{"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 },
|
||||
{"mqtttopic", 3, "TOPIC", 0, "Use MQTT TOPIC (prefix before /%circuit/%name or complete format) [ebusd]", 0 },
|
||||
{NULL, 0, NULL, 0, "MQTT options:", 1 },
|
||||
{"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 },
|
||||
{"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]
|
||||
@@ -49,61 +49,61 @@ static const char* g_topic = PACKAGE; //!< MQTT topic to use (prefix if without
|
||||
* @param state the parsing 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) {
|
||||
case 1: // --mqtthost=localhost
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid mqtthost");
|
||||
return EINVAL;
|
||||
}
|
||||
g_host = arg;
|
||||
break;
|
||||
switch (key) {
|
||||
case 1: // --mqtthost=localhost
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid mqtthost");
|
||||
return EINVAL;
|
||||
}
|
||||
g_host = arg;
|
||||
break;
|
||||
|
||||
case 2: // --mqttport=1883
|
||||
g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
argp_error(state, "invalid mqttport");
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case 2: // --mqttport=1883
|
||||
g_port = (uint16_t)parseInt(arg, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
argp_error(state, "invalid mqttport");
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // --mqtttopic=ebusd
|
||||
if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') {
|
||||
argp_error(state, "invalid mqtttopic");
|
||||
return EINVAL;
|
||||
}
|
||||
g_topic = arg;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
case 3: // --mqtttopic=ebusd
|
||||
if (arg == NULL || arg[0] == 0 || arg[0] == '/' || arg[strlen(arg)-1] == '/') {
|
||||
argp_error(state, "invalid mqtttopic");
|
||||
return EINVAL;
|
||||
}
|
||||
g_topic = arg;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
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_child g_mqtt_argp_child = {&g_mqtt_argp, 0, "", 1};
|
||||
|
||||
const struct argp_child* mqtthandler_getargs() {
|
||||
return &g_mqtt_argp_child;
|
||||
return &g_mqtt_argp_child;
|
||||
}
|
||||
|
||||
DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages) {
|
||||
return new MqttHandler(busHandler, messages);
|
||||
return new MqttHandler(busHandler, messages);
|
||||
}
|
||||
|
||||
/** the known topic column names. */
|
||||
static const char* columnNames[] = {
|
||||
"circuit",
|
||||
"name",
|
||||
"field",
|
||||
"circuit",
|
||||
"name",
|
||||
"field",
|
||||
};
|
||||
|
||||
/** the known topic column IDs. */
|
||||
static const size_t columnIds[] = {
|
||||
COLUMN_CIRCUIT,
|
||||
COLUMN_NAME,
|
||||
COLUMN_FIELDS,
|
||||
COLUMN_CIRCUIT,
|
||||
COLUMN_NAME,
|
||||
COLUMN_FIELDS,
|
||||
};
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
bool parseTopic(const string topic, vector<string> &strs, vector<size_t> &cols) {
|
||||
size_t lastpos = 0;
|
||||
size_t end = topic.length();
|
||||
vector<string> columns;
|
||||
for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) {
|
||||
size_t col = columnCount;
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < columnCount; i++) {
|
||||
len = strlen(columnNames[i]);
|
||||
if (topic.substr(pos+1, len) == columnNames[i]) {
|
||||
col = columnIds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (col == columnCount) {
|
||||
return false;
|
||||
}
|
||||
for (vector<size_t>::iterator it=cols.begin(); it != cols.end(); it++) {
|
||||
if (*it == col) {
|
||||
return false; // duplicate column
|
||||
}
|
||||
}
|
||||
strs.push_back(topic.substr(lastpos, pos-lastpos));
|
||||
cols.push_back(col);
|
||||
lastpos = pos+1+len;
|
||||
pos = topic.find('%', lastpos);
|
||||
}
|
||||
if (lastpos < end) {
|
||||
strs.push_back(topic.substr(lastpos, end-lastpos));
|
||||
}
|
||||
return true;
|
||||
size_t lastpos = 0;
|
||||
size_t end = topic.length();
|
||||
vector<string> columns;
|
||||
for (size_t pos=topic.find('%', lastpos); pos != string::npos; ) {
|
||||
size_t col = columnCount;
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < columnCount; i++) {
|
||||
len = strlen(columnNames[i]);
|
||||
if (topic.substr(pos+1, len) == columnNames[i]) {
|
||||
col = columnIds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (col == columnCount) {
|
||||
return false;
|
||||
}
|
||||
for (vector<size_t>::iterator it=cols.begin(); it != cols.end(); it++) {
|
||||
if (*it == col) {
|
||||
return false; // duplicate column
|
||||
}
|
||||
}
|
||||
strs.push_back(topic.substr(lastpos, pos-lastpos));
|
||||
cols.push_back(col);
|
||||
lastpos = pos+1+len;
|
||||
pos = topic.find('%', lastpos);
|
||||
}
|
||||
if (lastpos < end) {
|
||||
strs.push_back(topic.substr(lastpos, end-lastpos));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MqttHandler::MqttHandler(BusHandler* busHandler, MessageMap* messages)
|
||||
: DataSink(), DataSource(busHandler), Thread(), m_messages(messages) {
|
||||
bool enabled = g_port != 0;
|
||||
m_publishByField = false;
|
||||
m_mosquitto = NULL;
|
||||
if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) {
|
||||
logOtherError("mqtt", "malformed topic %s", g_topic);
|
||||
return;
|
||||
}
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
int major = -1;
|
||||
mosquitto_lib_version(&major, NULL, NULL);
|
||||
if (major != LIBMOSQUITTO_MAJOR) {
|
||||
logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR);
|
||||
return;
|
||||
}
|
||||
if (m_topicCols.empty()) {
|
||||
if (m_topicStrs.empty()) {
|
||||
m_topicStrs.push_back("");
|
||||
} else {
|
||||
string str = m_topicStrs[0];
|
||||
if (str.empty() || str[str.length()-1] != '/') {
|
||||
m_topicStrs[0] = str+"/";
|
||||
}
|
||||
}
|
||||
m_topicCols.push_back(COLUMN_CIRCUIT); // circuit
|
||||
m_topicStrs.push_back("/");
|
||||
m_topicCols.push_back(COLUMN_NAME); // name
|
||||
} else {
|
||||
for (size_t i = 0; i < m_topicCols.size(); i++) {
|
||||
if (m_topicCols[i] == COLUMN_FIELDS) { // fields
|
||||
m_publishByField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_globalTopic = getTopic(NULL)+"global/";
|
||||
m_mosquitto = NULL;
|
||||
if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) {
|
||||
logOtherError("mqtt", "unable to initialize");
|
||||
} else {
|
||||
string clientId = PACKAGE_STRING;
|
||||
clientId += " "+static_cast<unsigned>(getpid());
|
||||
m_mosquitto = mosquitto_new(clientId.c_str(),
|
||||
: DataSink(), DataSource(busHandler), Thread(), m_messages(messages) {
|
||||
bool enabled = g_port != 0;
|
||||
m_publishByField = false;
|
||||
m_mosquitto = NULL;
|
||||
if (enabled && !parseTopic(g_topic, m_topicStrs, m_topicCols)) {
|
||||
logOtherError("mqtt", "malformed topic %s", g_topic);
|
||||
return;
|
||||
}
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
int major = -1;
|
||||
mosquitto_lib_version(&major, NULL, NULL);
|
||||
if (major != LIBMOSQUITTO_MAJOR) {
|
||||
logOtherError("mqtt", "invalid mosquitto version %d instead of %d", major, LIBMOSQUITTO_MAJOR);
|
||||
return;
|
||||
}
|
||||
if (m_topicCols.empty()) {
|
||||
if (m_topicStrs.empty()) {
|
||||
m_topicStrs.push_back("");
|
||||
} else {
|
||||
string str = m_topicStrs[0];
|
||||
if (str.empty() || str[str.length()-1] != '/') {
|
||||
m_topicStrs[0] = str+"/";
|
||||
}
|
||||
}
|
||||
m_topicCols.push_back(COLUMN_CIRCUIT); // circuit
|
||||
m_topicStrs.push_back("/");
|
||||
m_topicCols.push_back(COLUMN_NAME); // name
|
||||
} else {
|
||||
for (size_t i = 0; i < m_topicCols.size(); i++) {
|
||||
if (m_topicCols[i] == COLUMN_FIELDS) { // fields
|
||||
m_publishByField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_globalTopic = getTopic(NULL)+"global/";
|
||||
m_mosquitto = NULL;
|
||||
if (mosquitto_lib_init() != MOSQ_ERR_SUCCESS) {
|
||||
logOtherError("mqtt", "unable to initialize");
|
||||
} else {
|
||||
string clientId = PACKAGE_STRING;
|
||||
clientId += " "+static_cast<unsigned>(getpid());
|
||||
m_mosquitto = mosquitto_new(clientId.c_str(),
|
||||
#if (LIBMOSQUITTO_MAJOR >= 1)
|
||||
true,
|
||||
true,
|
||||
#endif
|
||||
this);
|
||||
if (!m_mosquitto) {
|
||||
logOtherError("mqtt", "unable to instantiate");
|
||||
}
|
||||
}
|
||||
if (m_mosquitto) {
|
||||
/*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING
|
||||
| MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/
|
||||
string willTopic = m_globalTopic+"running";
|
||||
string willData = "false";
|
||||
size_t len = willData.length();
|
||||
mosquitto_will_set(m_mosquitto,
|
||||
this);
|
||||
if (!m_mosquitto) {
|
||||
logOtherError("mqtt", "unable to instantiate");
|
||||
}
|
||||
}
|
||||
if (m_mosquitto) {
|
||||
/*mosquitto_log_init(m_mosquitto, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING
|
||||
| MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);*/
|
||||
string willTopic = m_globalTopic+"running";
|
||||
string willData = "false";
|
||||
size_t len = willData.length();
|
||||
mosquitto_will_set(m_mosquitto,
|
||||
#if (LIBMOSQUITTO_MAJOR < 1)
|
||||
true,
|
||||
true,
|
||||
#endif
|
||||
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
|
||||
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 (LIBMOSQUITTO_MAJOR < 1)
|
||||
, true
|
||||
, true
|
||||
#endif
|
||||
) != MOSQ_ERR_SUCCESS) {
|
||||
logOtherError("mqtt", "unable to connect");
|
||||
mosquitto_destroy(m_mosquitto);
|
||||
m_mosquitto = NULL;
|
||||
} else {
|
||||
logOtherNotice("mqtt", "connection established");
|
||||
}
|
||||
}
|
||||
) != MOSQ_ERR_SUCCESS) {
|
||||
logOtherError("mqtt", "unable to connect");
|
||||
mosquitto_destroy(m_mosquitto);
|
||||
m_mosquitto = NULL;
|
||||
} else {
|
||||
logOtherNotice("mqtt", "connection established");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MqttHandler::~MqttHandler() {
|
||||
join();
|
||||
if (m_mosquitto) {
|
||||
mosquitto_destroy(m_mosquitto);
|
||||
m_mosquitto = NULL;
|
||||
}
|
||||
mosquitto_lib_cleanup();
|
||||
join();
|
||||
if (m_mosquitto) {
|
||||
mosquitto_destroy(m_mosquitto);
|
||||
m_mosquitto = NULL;
|
||||
}
|
||||
mosquitto_lib_cleanup();
|
||||
}
|
||||
|
||||
void MqttHandler::start() {
|
||||
if (m_mosquitto) {
|
||||
Thread::start("MQTT");
|
||||
}
|
||||
if (m_mosquitto) {
|
||||
Thread::start("MQTT");
|
||||
}
|
||||
}
|
||||
|
||||
void on_message(
|
||||
#if (LIBMOSQUITTO_MAJOR >= 1)
|
||||
struct mosquitto *mosq,
|
||||
struct mosquitto *mosq,
|
||||
#endif
|
||||
void *obj, const struct mosquitto_message *message) {
|
||||
MqttHandler* handler = reinterpret_cast<MqttHandler*>(obj);
|
||||
if (!handler || !message || !handler->isRunning()) {
|
||||
return;
|
||||
}
|
||||
string topic(message->topic);
|
||||
string data(message->payloadlen > 0 ? reinterpret_cast<char*>(message->payload) : "");
|
||||
handler->notifyTopic(topic, data);
|
||||
void *obj, const struct mosquitto_message *message) {
|
||||
MqttHandler* handler = reinterpret_cast<MqttHandler*>(obj);
|
||||
if (!handler || !message || !handler->isRunning()) {
|
||||
return;
|
||||
}
|
||||
string topic(message->topic);
|
||||
string data(message->payloadlen > 0 ? reinterpret_cast<char*>(message->payload) : "");
|
||||
handler->notifyTopic(topic, data);
|
||||
}
|
||||
|
||||
void MqttHandler::notifyTopic(string topic, string data) {
|
||||
size_t pos = topic.rfind('/');
|
||||
if (pos == string::npos) {
|
||||
return;
|
||||
}
|
||||
string suffix = topic.substr(pos+1);
|
||||
bool isWrite = false;
|
||||
if (suffix.empty()) {
|
||||
return;
|
||||
}
|
||||
string direction = suffix.substr(0, 3);
|
||||
isWrite = direction == "set";
|
||||
if (!isWrite && direction != "get") {
|
||||
return;
|
||||
}
|
||||
suffix = suffix.substr(3); // security level
|
||||
logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str());
|
||||
string remain = topic.substr(0, pos);
|
||||
size_t last = 0;
|
||||
string circuit, name;
|
||||
size_t idx;
|
||||
for (idx = 0; idx < m_topicStrs.size()+1; idx++) {
|
||||
string field;
|
||||
string chk;
|
||||
if (idx < m_topicStrs.size()) {
|
||||
chk = m_topicStrs[idx];
|
||||
pos = remain.find(chk, last);
|
||||
if (pos == string::npos) {
|
||||
return;
|
||||
}
|
||||
} else if (idx-1 < m_topicCols.size()) {
|
||||
pos = remain.size();
|
||||
} else if (last < remain.size()) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
field = remain.substr(last, pos-last);
|
||||
last = pos+chk.size();
|
||||
if (idx == 0) {
|
||||
if (pos > 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (field.empty()) {
|
||||
return;
|
||||
}
|
||||
switch (m_topicCols[idx-1]) {
|
||||
case COLUMN_CIRCUIT:
|
||||
circuit = field;
|
||||
break;
|
||||
case COLUMN_NAME:
|
||||
name = field;
|
||||
break;
|
||||
case COLUMN_FIELDS:
|
||||
//field = field; // TODO add support for writing a single field
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (circuit.empty() || name.empty()) {
|
||||
return;
|
||||
}
|
||||
logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str());
|
||||
if (suffix.length() > 0) {
|
||||
circuit += "#"+suffix;
|
||||
}
|
||||
Message* message = m_messages->find(circuit, name, isWrite);
|
||||
if (message == NULL) {
|
||||
message = m_messages->find(circuit, name, isWrite, true);
|
||||
}
|
||||
if (message == NULL) {
|
||||
logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str());
|
||||
return;
|
||||
}
|
||||
if (!message->isPassive()) {
|
||||
result_t result = m_busHandler->readFromBus(message, data);
|
||||
if (result != RESULT_OK) {
|
||||
logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result));
|
||||
return;
|
||||
}
|
||||
logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str());
|
||||
}
|
||||
ostringstream ostream;
|
||||
publishMessage(message, ostream);
|
||||
size_t pos = topic.rfind('/');
|
||||
if (pos == string::npos) {
|
||||
return;
|
||||
}
|
||||
string suffix = topic.substr(pos+1);
|
||||
bool isWrite = false;
|
||||
if (suffix.empty()) {
|
||||
return;
|
||||
}
|
||||
string direction = suffix.substr(0, 3);
|
||||
isWrite = direction == "set";
|
||||
if (!isWrite && direction != "get") {
|
||||
return;
|
||||
}
|
||||
suffix = suffix.substr(3); // security level
|
||||
logOtherDebug("mqtt", "received topic %s", topic.c_str(), data.c_str());
|
||||
string remain = topic.substr(0, pos);
|
||||
size_t last = 0;
|
||||
string circuit, name;
|
||||
size_t idx;
|
||||
for (idx = 0; idx < m_topicStrs.size()+1; idx++) {
|
||||
string field;
|
||||
string chk;
|
||||
if (idx < m_topicStrs.size()) {
|
||||
chk = m_topicStrs[idx];
|
||||
pos = remain.find(chk, last);
|
||||
if (pos == string::npos) {
|
||||
return;
|
||||
}
|
||||
} else if (idx-1 < m_topicCols.size()) {
|
||||
pos = remain.size();
|
||||
} else if (last < remain.size()) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
field = remain.substr(last, pos-last);
|
||||
last = pos+chk.size();
|
||||
if (idx == 0) {
|
||||
if (pos > 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (field.empty()) {
|
||||
return;
|
||||
}
|
||||
switch (m_topicCols[idx-1]) {
|
||||
case COLUMN_CIRCUIT:
|
||||
circuit = field;
|
||||
break;
|
||||
case COLUMN_NAME:
|
||||
name = field;
|
||||
break;
|
||||
case COLUMN_FIELDS:
|
||||
//field = field; // TODO add support for writing a single field
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (circuit.empty() || name.empty()) {
|
||||
return;
|
||||
}
|
||||
logOtherInfo("mqtt", "received topic for %s %s", circuit.c_str(), name.c_str());
|
||||
if (suffix.length() > 0) {
|
||||
circuit += "#"+suffix;
|
||||
}
|
||||
Message* message = m_messages->find(circuit, name, isWrite);
|
||||
if (message == NULL) {
|
||||
message = m_messages->find(circuit, name, isWrite, true);
|
||||
}
|
||||
if (message == NULL) {
|
||||
logOtherError("mqtt", "%s message %s %s not found", isWrite?"write":"read", circuit.c_str(), name.c_str());
|
||||
return;
|
||||
}
|
||||
if (!message->isPassive()) {
|
||||
result_t result = m_busHandler->readFromBus(message, data);
|
||||
if (result != RESULT_OK) {
|
||||
logOtherError("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), getResultCode(result));
|
||||
return;
|
||||
}
|
||||
logOtherNotice("mqtt", "%s %s %s: %s", isWrite?"write":"read", circuit.c_str(), name.c_str(), data.c_str());
|
||||
}
|
||||
ostringstream ostream;
|
||||
publishMessage(message, ostream);
|
||||
}
|
||||
|
||||
void MqttHandler::run() {
|
||||
time_t lastTaskRun, now, start, lastSignal = 0;
|
||||
bool signal = false;
|
||||
string signalTopic = m_globalTopic+"signal";
|
||||
string uptimeTopic = m_globalTopic+"uptime";
|
||||
ostringstream updates;
|
||||
time_t lastTaskRun, now, start, lastSignal = 0;
|
||||
bool signal = false;
|
||||
string signalTopic = m_globalTopic+"signal";
|
||||
string uptimeTopic = m_globalTopic+"uptime";
|
||||
ostringstream updates;
|
||||
|
||||
time(&now);
|
||||
start = lastTaskRun = now;
|
||||
publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION);
|
||||
publishTopic(m_globalTopic+"running", "true");
|
||||
publishTopic(signalTopic, "false");
|
||||
mosquitto_message_callback_set(m_mosquitto, on_message);
|
||||
string subTopic = getTopic(NULL)+"#";
|
||||
mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0);
|
||||
while (isRunning()) {
|
||||
handleTraffic();
|
||||
time(&now);
|
||||
if (now < start) {
|
||||
// clock skew
|
||||
if (now < lastSignal) {
|
||||
lastSignal -= lastTaskRun-now;
|
||||
}
|
||||
lastTaskRun = now;
|
||||
} else if (now > lastTaskRun+15) {
|
||||
if (m_busHandler->hasSignal()) {
|
||||
lastSignal = now;
|
||||
if (!signal) {
|
||||
signal = true;
|
||||
publishTopic(signalTopic, "true");
|
||||
}
|
||||
} else {
|
||||
if (signal) {
|
||||
signal = false;
|
||||
publishTopic(signalTopic, "false");
|
||||
}
|
||||
}
|
||||
time_t uptime = now-start;
|
||||
updates.str("");
|
||||
updates.clear();
|
||||
updates << dec << static_cast<unsigned>(uptime);
|
||||
publishTopic(uptimeTopic, updates.str());
|
||||
time(&lastTaskRun);
|
||||
}
|
||||
if (!m_updatedMessages.empty()) {
|
||||
for (map<Message*, int>::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) {
|
||||
Message* message = it->first;
|
||||
updates.str("");
|
||||
updates.clear();
|
||||
updates << dec;
|
||||
publishMessage(message, updates);
|
||||
}
|
||||
m_updatedMessages.clear();
|
||||
}
|
||||
}
|
||||
time(&now);
|
||||
start = lastTaskRun = now;
|
||||
publishTopic(m_globalTopic+"version", PACKAGE_STRING "." REVISION);
|
||||
publishTopic(m_globalTopic+"running", "true");
|
||||
publishTopic(signalTopic, "false");
|
||||
mosquitto_message_callback_set(m_mosquitto, on_message);
|
||||
string subTopic = getTopic(NULL)+"#";
|
||||
mosquitto_subscribe(m_mosquitto, NULL, subTopic.c_str(), 0);
|
||||
while (isRunning()) {
|
||||
handleTraffic();
|
||||
time(&now);
|
||||
if (now < start) {
|
||||
// clock skew
|
||||
if (now < lastSignal) {
|
||||
lastSignal -= lastTaskRun-now;
|
||||
}
|
||||
lastTaskRun = now;
|
||||
} else if (now > lastTaskRun+15) {
|
||||
if (m_busHandler->hasSignal()) {
|
||||
lastSignal = now;
|
||||
if (!signal) {
|
||||
signal = true;
|
||||
publishTopic(signalTopic, "true");
|
||||
}
|
||||
} else {
|
||||
if (signal) {
|
||||
signal = false;
|
||||
publishTopic(signalTopic, "false");
|
||||
}
|
||||
}
|
||||
time_t uptime = now-start;
|
||||
updates.str("");
|
||||
updates.clear();
|
||||
updates << dec << static_cast<unsigned>(uptime);
|
||||
publishTopic(uptimeTopic, updates.str());
|
||||
time(&lastTaskRun);
|
||||
}
|
||||
if (!m_updatedMessages.empty()) {
|
||||
for (map<Message*, int>::iterator it = m_updatedMessages.begin(); it != m_updatedMessages.end(); it++) {
|
||||
Message* message = it->first;
|
||||
updates.str("");
|
||||
updates.clear();
|
||||
updates << dec;
|
||||
publishMessage(message, updates);
|
||||
}
|
||||
m_updatedMessages.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MqttHandler::handleTraffic() {
|
||||
if (m_mosquitto) {
|
||||
mosquitto_loop(m_mosquitto, -1
|
||||
if (m_mosquitto) {
|
||||
mosquitto_loop(m_mosquitto, -1
|
||||
#if (LIBMOSQUITTO_MAJOR >= 1)
|
||||
, 1
|
||||
, 1
|
||||
#endif
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
string MqttHandler::getTopic(Message* message, signed char fieldIndex) {
|
||||
ostringstream ret;
|
||||
for (size_t i = 0; i < m_topicStrs.size(); i++) {
|
||||
ret << m_topicStrs[i];
|
||||
if (!message) {
|
||||
break;
|
||||
}
|
||||
if (i < m_topicCols.size()) {
|
||||
if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) {
|
||||
ret << message->getFieldName(fieldIndex);
|
||||
} else {
|
||||
message->dumpColumn(ret, m_topicCols[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret.str();
|
||||
ostringstream ret;
|
||||
for (size_t i = 0; i < m_topicStrs.size(); i++) {
|
||||
ret << m_topicStrs[i];
|
||||
if (!message) {
|
||||
break;
|
||||
}
|
||||
if (i < m_topicCols.size()) {
|
||||
if (m_topicCols[i] == COLUMN_FIELDS && fieldIndex >= 0) {
|
||||
ret << message->getFieldName(fieldIndex);
|
||||
} else {
|
||||
message->dumpColumn(ret, m_topicCols[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
void MqttHandler::publishMessage(Message* message, ostringstream& updates) {
|
||||
result_t result = message->decodeLastData(updates);
|
||||
if (result != RESULT_OK) {
|
||||
logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result));
|
||||
return;
|
||||
}
|
||||
if (m_publishByField) {
|
||||
signed char index = 0;
|
||||
istringstream input(updates.str());
|
||||
string token;
|
||||
while (getline(input, token, UI_FIELD_SEPARATOR)) {
|
||||
string topic = getTopic(message, index);
|
||||
publishTopic(topic, token);
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
publishTopic(getTopic(message), updates.str());
|
||||
}
|
||||
result_t result = message->decodeLastData(updates);
|
||||
if (result != RESULT_OK) {
|
||||
logOtherError("mqtt", "decode %s %s: %s", message->getCircuit().c_str(), message->getName().c_str(), getResultCode(result));
|
||||
return;
|
||||
}
|
||||
if (m_publishByField) {
|
||||
signed char index = 0;
|
||||
istringstream input(updates.str());
|
||||
string token;
|
||||
while (getline(input, token, UI_FIELD_SEPARATOR)) {
|
||||
string topic = getTopic(message, index);
|
||||
publishTopic(topic, token);
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
publishTopic(getTopic(message), updates.str());
|
||||
}
|
||||
}
|
||||
|
||||
void MqttHandler::publishTopic(string topic, string data, bool retain) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+59
-59
@@ -55,81 +55,81 @@ DataHandler* mqtthandler_register(BusHandler* busHandler, MessageMap* messages);
|
||||
* The main class supporting MQTT data handling.
|
||||
*/
|
||||
class MqttHandler : public DataSink, public DataSource, public Thread {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
*/
|
||||
MqttHandler(BusHandler* busHandler, MessageMap* messages);
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param busHandler the @a BusHandler instance.
|
||||
* @param messages the @a MessageMap instance.
|
||||
*/
|
||||
MqttHandler(BusHandler* busHandler, MessageMap* messages);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~MqttHandler();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~MqttHandler();
|
||||
|
||||
// @copydoc
|
||||
virtual void start();
|
||||
// @copydoc
|
||||
virtual void start();
|
||||
|
||||
/**
|
||||
* Notify the handler of a received MQTT message.
|
||||
* @param topic the topic string.
|
||||
* @param data the data string.
|
||||
*/
|
||||
void notifyTopic(string topic, string data);
|
||||
/**
|
||||
* Notify the handler of a received MQTT message.
|
||||
* @param topic the topic string.
|
||||
* @param data the data string.
|
||||
*/
|
||||
void notifyTopic(string topic, string data);
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void run();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void run();
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called regularly to handle MQTT traffic.
|
||||
*/
|
||||
void handleTraffic();
|
||||
private:
|
||||
/**
|
||||
* Called regularly to handle MQTT traffic.
|
||||
*/
|
||||
void handleTraffic();
|
||||
|
||||
/**
|
||||
* Build the MQTT topic string for the @a Message.
|
||||
* @param message the @a Message to build the topic string for.
|
||||
* @param fieldIndex the optional field index for the field column, or -1.
|
||||
* @return the topic string.
|
||||
*/
|
||||
string getTopic(Message* message, signed char fieldIndex = -1);
|
||||
/**
|
||||
* Build the MQTT topic string for the @a Message.
|
||||
* @param message the @a Message to build the topic string for.
|
||||
* @param fieldIndex the optional field index for the field column, or -1.
|
||||
* @return the topic string.
|
||||
*/
|
||||
string getTopic(Message* message, signed char fieldIndex = -1);
|
||||
|
||||
/**
|
||||
* Prepare a @a Message and publish as topic.
|
||||
* @param message the @a Message to publish.
|
||||
* @param updates the @a ostringstream for preparation.
|
||||
*/
|
||||
void publishMessage(Message* message, ostringstream& updates);
|
||||
/**
|
||||
* Prepare a @a Message and publish as topic.
|
||||
* @param message the @a Message to publish.
|
||||
* @param updates the @a ostringstream for preparation.
|
||||
*/
|
||||
void publishMessage(Message* message, ostringstream& updates);
|
||||
|
||||
/**
|
||||
* Publish a topic update to MQTT.
|
||||
* @param topic the topic string.
|
||||
* @param data the data string.
|
||||
* @param retain whether the topic shall be retained.
|
||||
*/
|
||||
void publishTopic(string topic, string data, bool retain = true);
|
||||
/**
|
||||
* Publish a topic update to MQTT.
|
||||
* @param topic the topic string.
|
||||
* @param data the data string.
|
||||
* @param retain whether the topic shall be retained.
|
||||
*/
|
||||
void publishTopic(string topic, string data, bool retain = true);
|
||||
|
||||
/** the @a MessageMap instance. */
|
||||
MessageMap* m_messages;
|
||||
/** the @a MessageMap instance. */
|
||||
MessageMap* m_messages;
|
||||
|
||||
/** the MQTT topic string parts. */
|
||||
vector<string> m_topicStrs;
|
||||
/** the MQTT topic string parts. */
|
||||
vector<string> m_topicStrs;
|
||||
|
||||
/** the MQTT topic column parts. */
|
||||
vector<size_t> m_topicCols;
|
||||
/** the MQTT topic column parts. */
|
||||
vector<size_t> m_topicCols;
|
||||
|
||||
/** the global topic prefix. */
|
||||
string m_globalTopic;
|
||||
/** the global topic prefix. */
|
||||
string m_globalTopic;
|
||||
|
||||
/** whether to publish a separate topic for each message field. */
|
||||
bool m_publishByField;
|
||||
/** whether to publish a separate topic for each message field. */
|
||||
bool m_publishByField;
|
||||
|
||||
/** the mosquitto structure if initialized, or NULL. */
|
||||
struct mosquitto* m_mosquitto;
|
||||
/** the mosquitto structure if initialized, or NULL. */
|
||||
struct mosquitto* m_mosquitto;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+198
-198
@@ -17,12 +17,12 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "network.h"
|
||||
#ifdef HAVE_PPOLL
|
||||
# include <poll.h>
|
||||
# include <poll.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "log.h"
|
||||
@@ -32,271 +32,271 @@ namespace ebusd {
|
||||
int Connection::m_ids = 0;
|
||||
|
||||
void Connection::run() {
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
|
||||
// set timeout
|
||||
tdiff.tv_sec = 2;
|
||||
tdiff.tv_nsec = 0;
|
||||
int notifyFD = m_notify.notifyFD();
|
||||
int sockFD = m_socket->getFD();
|
||||
// set timeout
|
||||
tdiff.tv_sec = 2;
|
||||
tdiff.tv_nsec = 0;
|
||||
int notifyFD = m_notify.notifyFD();
|
||||
int sockFD = m_socket->getFD();
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
int nfds = 2;
|
||||
struct pollfd fds[nfds];
|
||||
int nfds = 2;
|
||||
struct pollfd fds[nfds];
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
fds[0].fd = notifyFD;
|
||||
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
|
||||
fds[0].fd = notifyFD;
|
||||
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
|
||||
|
||||
fds[1].fd = sockFD;
|
||||
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
|
||||
fds[1].fd = sockFD;
|
||||
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD;
|
||||
fd_set checkfds, exceptfds;
|
||||
int maxfd = (notifyFD > sockFD) ? notifyFD : sockFD;
|
||||
fd_set checkfds, exceptfds;
|
||||
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(notifyFD, &checkfds);
|
||||
FD_SET(sockFD, &checkfds);
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(notifyFD, &checkfds);
|
||||
FD_SET(sockFD, &checkfds);
|
||||
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(notifyFD, &exceptfds);
|
||||
FD_SET(sockFD, &exceptfds);
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(notifyFD, &exceptfds);
|
||||
FD_SET(sockFD, &exceptfds);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool closed = false;
|
||||
NetMessage message(m_isHttp);
|
||||
bool closed = false;
|
||||
NetMessage message(m_isHttp);
|
||||
|
||||
while (!closed) {
|
||||
while (!closed) {
|
||||
#ifdef HAVE_PPOLL
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL);
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &tdiff, NULL);
|
||||
#endif
|
||||
#endif
|
||||
bool newData = false;
|
||||
if (ret != 0) {
|
||||
bool newData = false;
|
||||
if (ret != 0) {
|
||||
#ifdef HAVE_PPOLL
|
||||
// new data from notify
|
||||
if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) {
|
||||
break;
|
||||
}
|
||||
// new data from socket
|
||||
newData = fds[1].revents & POLLIN;
|
||||
closed = fds[1].revents & POLLRDHUP;
|
||||
// new data from notify
|
||||
if (ret < 0 || (fds[0].revents & (POLLIN | POLLERR | POLLHUP | POLLRDHUP)) || (fds[1].revents & (POLLERR | POLLHUP))) {
|
||||
break;
|
||||
}
|
||||
// new data from socket
|
||||
newData = fds[1].revents & POLLIN;
|
||||
closed = fds[1].revents & POLLRDHUP;
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// new data from notify
|
||||
if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) {
|
||||
break;
|
||||
}
|
||||
// new data from socket
|
||||
newData = FD_ISSET(sockFD, &readfds);
|
||||
closed = FD_ISSET(sockFD, &exceptfds);
|
||||
// new data from notify
|
||||
if (ret < 0 || FD_ISSET(notifyFD, &readfds) || FD_ISSET(notifyFD, &exceptfds)) {
|
||||
break;
|
||||
}
|
||||
// new data from socket
|
||||
newData = FD_ISSET(sockFD, &readfds);
|
||||
closed = FD_ISSET(sockFD, &exceptfds);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (newData || message.isListening()) {
|
||||
char data[256];
|
||||
if (newData || message.isListening()) {
|
||||
char data[256];
|
||||
|
||||
if (!m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
if (newData) {
|
||||
size_t datalen = m_socket->recv(data, sizeof(data)-1);
|
||||
if (!m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
if (newData) {
|
||||
size_t datalen = m_socket->recv(data, sizeof(data)-1);
|
||||
|
||||
// remove closed socket
|
||||
if (datalen <= 0) {
|
||||
break;
|
||||
}
|
||||
data[datalen] = '\0';
|
||||
} else {
|
||||
data[0] = '\0';
|
||||
}
|
||||
// remove closed socket
|
||||
if (datalen <= 0) {
|
||||
break;
|
||||
}
|
||||
data[datalen] = '\0';
|
||||
} else {
|
||||
data[0] = '\0';
|
||||
}
|
||||
|
||||
// decode client data
|
||||
if (message.add(data)) {
|
||||
m_netQueue->push(&message);
|
||||
// decode client data
|
||||
if (message.add(data)) {
|
||||
m_netQueue->push(&message);
|
||||
|
||||
// wait for result
|
||||
logDebug(lf_network, "[%05d] wait for result", getID());
|
||||
string result = message.getResult();
|
||||
// wait for result
|
||||
logDebug(lf_network, "[%05d] wait for result", getID());
|
||||
string result = message.getResult();
|
||||
|
||||
if (!m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
m_socket->send(result.c_str(), result.size());
|
||||
}
|
||||
if (!m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
m_socket->send(result.c_str(), result.size());
|
||||
}
|
||||
|
||||
if (message.isDisconnect() || !m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.isDisconnect() || !m_socket->isValid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete m_socket;
|
||||
m_socket = NULL;
|
||||
logInfo(lf_network, "[%05d] connection closed", getID());
|
||||
delete m_socket;
|
||||
m_socket = NULL;
|
||||
logInfo(lf_network, "[%05d] connection closed", getID());
|
||||
}
|
||||
|
||||
|
||||
Network::Network(const bool local, const uint16_t port, const uint16_t httpPort, Queue<NetMessage*>* netQueue)
|
||||
: Thread(), m_netQueue(netQueue), m_listening(false) {
|
||||
m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0");
|
||||
: Thread(), m_netQueue(netQueue), m_listening(false) {
|
||||
m_tcpServer = new TCPServer(port, local ? "127.0.0.1" : "0.0.0.0");
|
||||
|
||||
if (m_tcpServer != NULL && m_tcpServer->start() == 0) {
|
||||
m_listening = true;
|
||||
}
|
||||
if (httpPort > 0) {
|
||||
m_httpServer = new TCPServer(httpPort, "0.0.0.0");
|
||||
m_httpServer->start();
|
||||
} else {
|
||||
m_httpServer = NULL;
|
||||
}
|
||||
if (m_tcpServer != NULL && m_tcpServer->start() == 0) {
|
||||
m_listening = true;
|
||||
}
|
||||
if (httpPort > 0) {
|
||||
m_httpServer = new TCPServer(httpPort, "0.0.0.0");
|
||||
m_httpServer->start();
|
||||
} else {
|
||||
m_httpServer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Network::~Network() {
|
||||
stop();
|
||||
while (!m_connections.empty()) {
|
||||
Connection* connection = m_connections.back();
|
||||
m_connections.pop_back();
|
||||
connection->stop();
|
||||
connection->join();
|
||||
delete connection;
|
||||
}
|
||||
stop();
|
||||
while (!m_connections.empty()) {
|
||||
Connection* connection = m_connections.back();
|
||||
m_connections.pop_back();
|
||||
connection->stop();
|
||||
connection->join();
|
||||
delete connection;
|
||||
}
|
||||
|
||||
if (m_tcpServer != NULL) {
|
||||
delete m_tcpServer;
|
||||
}
|
||||
if (m_httpServer != NULL) {
|
||||
delete m_httpServer;
|
||||
}
|
||||
join();
|
||||
if (m_tcpServer != NULL) {
|
||||
delete m_tcpServer;
|
||||
}
|
||||
if (m_httpServer != NULL) {
|
||||
delete m_httpServer;
|
||||
}
|
||||
join();
|
||||
}
|
||||
|
||||
void Network::run() {
|
||||
if (!m_listening) {
|
||||
return;
|
||||
}
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
if (!m_listening) {
|
||||
return;
|
||||
}
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
|
||||
// set timeout
|
||||
tdiff.tv_sec = 1;
|
||||
tdiff.tv_nsec = 0;
|
||||
// set timeout
|
||||
tdiff.tv_sec = 1;
|
||||
tdiff.tv_nsec = 0;
|
||||
#ifdef HAVE_PPOLL
|
||||
int socketCount = m_httpServer ? 2 : 1;
|
||||
int nfds = 1+socketCount;
|
||||
struct pollfd fds[nfds];
|
||||
int socketCount = m_httpServer ? 2 : 1;
|
||||
int nfds = 1+socketCount;
|
||||
struct pollfd fds[nfds];
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
fds[0].fd = m_notify.notifyFD();
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].fd = m_notify.notifyFD();
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
fds[1].fd = m_tcpServer->getFD();
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].fd = m_tcpServer->getFD();
|
||||
fds[1].events = POLLIN;
|
||||
|
||||
if (m_httpServer) {
|
||||
fds[2].fd = m_httpServer->getFD();
|
||||
fds[2].events = POLLIN;
|
||||
}
|
||||
if (m_httpServer) {
|
||||
fds[2].fd = m_httpServer->getFD();
|
||||
fds[2].events = POLLIN;
|
||||
}
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
int maxfd;
|
||||
fd_set checkfds;
|
||||
int maxfd;
|
||||
fd_set checkfds;
|
||||
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(m_notify.notifyFD(), &checkfds);
|
||||
FD_SET(m_tcpServer->getFD(), &checkfds);
|
||||
if (m_httpServer) {
|
||||
FD_SET(m_httpServer->getFD(), &checkfds);
|
||||
}
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(m_notify.notifyFD(), &checkfds);
|
||||
FD_SET(m_tcpServer->getFD(), &checkfds);
|
||||
if (m_httpServer) {
|
||||
FD_SET(m_httpServer->getFD(), &checkfds);
|
||||
}
|
||||
|
||||
maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ?
|
||||
m_notify.notifyFD() : m_tcpServer->getFD();
|
||||
if (m_httpServer && m_httpServer->getFD() > maxfd) {
|
||||
maxfd = m_httpServer->getFD();
|
||||
}
|
||||
maxfd = (m_notify.notifyFD() > m_tcpServer->getFD()) ?
|
||||
m_notify.notifyFD() : m_tcpServer->getFD();
|
||||
if (m_httpServer && m_httpServer->getFD() > maxfd) {
|
||||
maxfd = m_httpServer->getFD();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
while (true) {
|
||||
while (true) {
|
||||
#ifdef HAVE_PPOLL
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
#endif
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
cleanConnections();
|
||||
continue;
|
||||
}
|
||||
bool newData = false, isHttp = false;
|
||||
if (ret == 0) {
|
||||
cleanConnections();
|
||||
continue;
|
||||
}
|
||||
bool newData = false, isHttp = false;
|
||||
#ifdef HAVE_PPOLL
|
||||
// new data from notify
|
||||
if (fds[0].revents & POLLIN) {
|
||||
return;
|
||||
}
|
||||
// new data from socket
|
||||
if (fds[1].revents & POLLIN) {
|
||||
newData = true;
|
||||
} else if (m_httpServer && fds[2].revents & POLLIN) {
|
||||
newData = isHttp = true;
|
||||
}
|
||||
// new data from notify
|
||||
if (fds[0].revents & POLLIN) {
|
||||
return;
|
||||
}
|
||||
// new data from socket
|
||||
if (fds[1].revents & POLLIN) {
|
||||
newData = true;
|
||||
} else if (m_httpServer && fds[2].revents & POLLIN) {
|
||||
newData = isHttp = true;
|
||||
}
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// new data from notify
|
||||
if (FD_ISSET(m_notify.notifyFD(), &readfds)) {
|
||||
return;
|
||||
}
|
||||
// new data from socket
|
||||
if (FD_ISSET(m_tcpServer->getFD(), &readfds)) {
|
||||
newData = true;
|
||||
} else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) {
|
||||
newData = isHttp = true;
|
||||
}
|
||||
// new data from notify
|
||||
if (FD_ISSET(m_notify.notifyFD(), &readfds)) {
|
||||
return;
|
||||
}
|
||||
// new data from socket
|
||||
if (FD_ISSET(m_tcpServer->getFD(), &readfds)) {
|
||||
newData = true;
|
||||
} else if (m_httpServer && FD_ISSET(m_httpServer->getFD(), &readfds)) {
|
||||
newData = isHttp = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (newData) {
|
||||
TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket();
|
||||
if (socket == NULL) {
|
||||
continue;
|
||||
}
|
||||
Connection* connection = new Connection(socket, isHttp, m_netQueue);
|
||||
if (connection == NULL) {
|
||||
continue;
|
||||
}
|
||||
connection->start("connection");
|
||||
m_connections.push_back(connection);
|
||||
logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str());
|
||||
}
|
||||
}
|
||||
if (newData) {
|
||||
TCPSocket* socket = (isHttp ? m_httpServer : m_tcpServer)->newSocket();
|
||||
if (socket == NULL) {
|
||||
continue;
|
||||
}
|
||||
Connection* connection = new Connection(socket, isHttp, m_netQueue);
|
||||
if (connection == NULL) {
|
||||
continue;
|
||||
}
|
||||
connection->start("connection");
|
||||
m_connections.push_back(connection);
|
||||
logInfo(lf_network, "[%05d] %s connection opened %s", connection->getID(), isHttp ? "HTTP" : "client", socket->getIP().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Network::cleanConnections() {
|
||||
list<Connection*>::iterator c_it;
|
||||
for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) {
|
||||
if (!(*c_it)->isRunning()) {
|
||||
Connection* connection = *c_it;
|
||||
c_it = m_connections.erase(c_it);
|
||||
delete connection;
|
||||
logDebug(lf_network, "dead connection removed - %d", m_connections.size());
|
||||
}
|
||||
}
|
||||
list<Connection*>::iterator c_it;
|
||||
for (c_it = m_connections.begin(); c_it != m_connections.end(); c_it++) {
|
||||
if (!(*c_it)->isRunning()) {
|
||||
Connection* connection = *c_it;
|
||||
c_it = m_connections.erase(c_it);
|
||||
delete connection;
|
||||
logDebug(lf_network, "dead connection removed - %d", m_connections.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+208
-208
@@ -39,271 +39,271 @@ class Connection;
|
||||
* Class for data/message transfer between @a Connection and @a MainLoop.
|
||||
*/
|
||||
class NetMessage {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param isHttp whether this is a HTTP message.
|
||||
*/
|
||||
explicit NetMessage(const bool isHttp)
|
||||
: m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param isHttp whether this is a HTTP message.
|
||||
*/
|
||||
explicit NetMessage(const bool isHttp)
|
||||
: m_isHttp(isHttp), m_resultSet(false), m_disconnect(false), m_listening(false), m_listenSince(0) {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~NetMessage() {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~NetMessage() {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param src the object to copy from.
|
||||
*/
|
||||
NetMessage(const NetMessage& src);
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param src the object to copy from.
|
||||
*/
|
||||
NetMessage(const NetMessage& src);
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Add request data received from the client.
|
||||
* @param request the request data from the client.
|
||||
* @return true when the request is complete and the response shall be prepared.
|
||||
*/
|
||||
bool add(string request) {
|
||||
if (request.length() > 0) {
|
||||
request.erase(remove(request.begin(), request.end(), '\r'), request.end());
|
||||
m_request.append(request);
|
||||
}
|
||||
size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n");
|
||||
if (pos != string::npos) {
|
||||
if (m_isHttp) {
|
||||
pos = m_request.find("\n");
|
||||
m_request.resize(pos); // reduce to first line
|
||||
// typical first line: GET /ehp/outsidetemp HTTP/1.1
|
||||
pos = m_request.rfind(" HTTP/");
|
||||
if (pos != string::npos) {
|
||||
m_request.resize(pos); // remove "HTTP/x.x" suffix
|
||||
}
|
||||
pos = 0;
|
||||
while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) {
|
||||
unsigned int value1, value2;
|
||||
if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) {
|
||||
break;
|
||||
}
|
||||
m_request[pos] = static_cast<char>(((value1&0x0f) << 4) | (value2&0x0f));
|
||||
m_request.erase(pos+1, 2);
|
||||
}
|
||||
} else if (pos+1 == m_request.length()) {
|
||||
m_request.resize(pos); // reduce to complete lines
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return m_request.length() == 0 && m_listening;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Add request data received from the client.
|
||||
* @param request the request data from the client.
|
||||
* @return true when the request is complete and the response shall be prepared.
|
||||
*/
|
||||
bool add(string request) {
|
||||
if (request.length() > 0) {
|
||||
request.erase(remove(request.begin(), request.end(), '\r'), request.end());
|
||||
m_request.append(request);
|
||||
}
|
||||
size_t pos = m_request.find(m_isHttp ? "\n\n" : "\n");
|
||||
if (pos != string::npos) {
|
||||
if (m_isHttp) {
|
||||
pos = m_request.find("\n");
|
||||
m_request.resize(pos); // reduce to first line
|
||||
// typical first line: GET /ehp/outsidetemp HTTP/1.1
|
||||
pos = m_request.rfind(" HTTP/");
|
||||
if (pos != string::npos) {
|
||||
m_request.resize(pos); // remove "HTTP/x.x" suffix
|
||||
}
|
||||
pos = 0;
|
||||
while ((pos=m_request.find('%', pos)) != string::npos && pos+2 <= m_request.length()) {
|
||||
unsigned int value1, value2;
|
||||
if (sscanf("%1x%1x", m_request.c_str()+pos+1, &value1, &value2) < 2) {
|
||||
break;
|
||||
}
|
||||
m_request[pos] = static_cast<char>(((value1&0x0f) << 4) | (value2&0x0f));
|
||||
m_request.erase(pos+1, 2);
|
||||
}
|
||||
} else if (pos+1 == m_request.length()) {
|
||||
m_request.resize(pos); // reduce to complete lines
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return m_request.length() == 0 && m_listening;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this is a HTTP message.
|
||||
* @return whether this is a HTTP message.
|
||||
*/
|
||||
bool isHttp() const { return m_isHttp; }
|
||||
/**
|
||||
* Return whether this is a HTTP message.
|
||||
* @return whether this is a HTTP message.
|
||||
*/
|
||||
bool isHttp() const { return m_isHttp; }
|
||||
|
||||
/**
|
||||
* Return the request string.
|
||||
* @return the request string.
|
||||
*/
|
||||
string getRequest() const { return m_request; }
|
||||
/**
|
||||
* Return the request string.
|
||||
* @return the request string.
|
||||
*/
|
||||
string getRequest() const { return m_request; }
|
||||
|
||||
/**
|
||||
* Wait for the result being set and return the result string.
|
||||
* @return the result string.
|
||||
*/
|
||||
string getResult() {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
/**
|
||||
* Wait for the result being set and return the result string.
|
||||
* @return the result string.
|
||||
*/
|
||||
string getResult() {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
|
||||
while (!m_resultSet)
|
||||
pthread_cond_wait(&m_cond, &m_mutex);
|
||||
while (!m_resultSet)
|
||||
pthread_cond_wait(&m_cond, &m_mutex);
|
||||
|
||||
m_request.clear();
|
||||
string result = m_result;
|
||||
m_result.clear();
|
||||
m_resultSet = false;
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
m_request.clear();
|
||||
string result = m_result;
|
||||
m_result.clear();
|
||||
m_resultSet = false;
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result string and notify the waiting thread.
|
||||
* @param result the result string.
|
||||
* @param listening whether the client is in listening mode.
|
||||
* @param listenUntil the end time to which to updates were added (exclusive).
|
||||
* @param disconnect true when the client shall be disconnected.
|
||||
*/
|
||||
void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_result = result;
|
||||
m_disconnect = disconnect;
|
||||
m_listening = listening;
|
||||
m_listenSince = listenUntil;
|
||||
m_resultSet = true;
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
/**
|
||||
* Set the result string and notify the waiting thread.
|
||||
* @param result the result string.
|
||||
* @param listening whether the client is in listening mode.
|
||||
* @param listenUntil the end time to which to updates were added (exclusive).
|
||||
* @param disconnect true when the client shall be disconnected.
|
||||
*/
|
||||
void setResult(const string result, const bool listening, const time_t listenUntil, const bool disconnect) {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_result = result;
|
||||
m_disconnect = disconnect;
|
||||
m_listening = listening;
|
||||
m_listenSince = listenUntil;
|
||||
m_resultSet = true;
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the client is in listening mode.
|
||||
* @param listenSince set to the start time from which to add updates (inclusive).
|
||||
* @return whether the client is in listening mode.
|
||||
*/
|
||||
bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; }
|
||||
/**
|
||||
* Return whether the client is in listening mode.
|
||||
* @param listenSince set to the start time from which to add updates (inclusive).
|
||||
* @return whether the client is in listening mode.
|
||||
*/
|
||||
bool isListening(time_t* listenSince=NULL) { if (listenSince) { *listenSince = m_listenSince; } return m_listening; }
|
||||
|
||||
/**
|
||||
* Return whether the client shall be disconnected.
|
||||
* @return true when the client shall be disconnected.
|
||||
*/
|
||||
bool isDisconnect() { return m_disconnect; }
|
||||
/**
|
||||
* Return whether the client shall be disconnected.
|
||||
* @return true when the client shall be disconnected.
|
||||
*/
|
||||
bool isDisconnect() { return m_disconnect; }
|
||||
|
||||
|
||||
private:
|
||||
/** whether this is a HTTP message. */
|
||||
const bool m_isHttp;
|
||||
private:
|
||||
/** whether this is a HTTP message. */
|
||||
const bool m_isHttp;
|
||||
|
||||
/** the request string. */
|
||||
string m_request;
|
||||
/** the request string. */
|
||||
string m_request;
|
||||
|
||||
/** whether the result was already set. */
|
||||
bool m_resultSet;
|
||||
/** whether the result was already set. */
|
||||
bool m_resultSet;
|
||||
|
||||
/** the result string. */
|
||||
string m_result;
|
||||
/** the result string. */
|
||||
string m_result;
|
||||
|
||||
/** set to true when the client shall be disconnected. */
|
||||
bool m_disconnect;
|
||||
/** set to true when the client shall be disconnected. */
|
||||
bool m_disconnect;
|
||||
|
||||
/** mutex variable for exclusive lock. */
|
||||
pthread_mutex_t m_mutex;
|
||||
/** mutex variable for exclusive lock. */
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
/** condition variable for exclusive lock. */
|
||||
pthread_cond_t m_cond;
|
||||
/** condition variable for exclusive lock. */
|
||||
pthread_cond_t m_cond;
|
||||
|
||||
/** whether the client is in listening mode. */
|
||||
bool m_listening;
|
||||
/** whether the client is in listening mode. */
|
||||
bool m_listening;
|
||||
|
||||
/** start timestamp of listening update. */
|
||||
time_t m_listenSince;
|
||||
/** start timestamp of listening update. */
|
||||
time_t m_listenSince;
|
||||
};
|
||||
|
||||
/**
|
||||
* class connection which handle client and baseloop communication.
|
||||
*/
|
||||
class Connection : public Thread {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param socket the @a TCPSocket for communication.
|
||||
* @param isHttp whether this is a HTTP message.
|
||||
* @param netQueue the reference to the @a NetMessage @a Queue.
|
||||
*/
|
||||
Connection(TCPSocket* socket, const bool isHttp, Queue<NetMessage*>* netQueue)
|
||||
: Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) {
|
||||
m_id = ++m_ids;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param socket the @a TCPSocket for communication.
|
||||
* @param isHttp whether this is a HTTP message.
|
||||
* @param netQueue the reference to the @a NetMessage @a Queue.
|
||||
*/
|
||||
Connection(TCPSocket* socket, const bool isHttp, Queue<NetMessage*>* netQueue)
|
||||
: Thread(), m_isHttp(isHttp), m_socket(socket), m_netQueue(netQueue) {
|
||||
m_id = ++m_ids;
|
||||
}
|
||||
|
||||
virtual ~Connection() { if (m_socket) delete m_socket; }
|
||||
/**
|
||||
* endless loop for connection instance.
|
||||
*/
|
||||
virtual void run();
|
||||
virtual ~Connection() { if (m_socket) delete m_socket; }
|
||||
/**
|
||||
* endless loop for connection instance.
|
||||
*/
|
||||
virtual void run();
|
||||
|
||||
/**
|
||||
* Stop this connection.
|
||||
*/
|
||||
virtual void stop() { m_notify.notify(); Thread::stop(); }
|
||||
/**
|
||||
* Stop this connection.
|
||||
*/
|
||||
virtual void stop() { m_notify.notify(); Thread::stop(); }
|
||||
|
||||
/**
|
||||
* Return the ID of this connection.
|
||||
* @return the ID of this connection.
|
||||
*/
|
||||
int getID() { return m_id; }
|
||||
/**
|
||||
* Return the ID of this connection.
|
||||
* @return the ID of this connection.
|
||||
*/
|
||||
int getID() { return m_id; }
|
||||
|
||||
|
||||
private:
|
||||
/** whether this is a HTTP connection. */
|
||||
const bool m_isHttp;
|
||||
private:
|
||||
/** whether this is a HTTP connection. */
|
||||
const bool m_isHttp;
|
||||
|
||||
/** the @a TCPSocket for communication. */
|
||||
TCPSocket* m_socket;
|
||||
/** the @a TCPSocket for communication. */
|
||||
TCPSocket* m_socket;
|
||||
|
||||
/** the reference to the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*>* m_netQueue;
|
||||
/** the reference to the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*>* m_netQueue;
|
||||
|
||||
/** notification object for shutdown procedure. */
|
||||
Notify m_notify;
|
||||
/** notification object for shutdown procedure. */
|
||||
Notify m_notify;
|
||||
|
||||
/** the ID of this connection. */
|
||||
int m_id;
|
||||
/** the ID of this connection. */
|
||||
int m_id;
|
||||
|
||||
/** the IF of the last opened connection. */
|
||||
static int m_ids;
|
||||
/** the IF of the last opened connection. */
|
||||
static int m_ids;
|
||||
};
|
||||
|
||||
/**
|
||||
* class network which listening on tcp socket for incoming connections.
|
||||
*/
|
||||
class Network : public Thread {
|
||||
public:
|
||||
/**
|
||||
* create a network instance and listening for incoming connections.
|
||||
* @param local true to accept connections only for local host.
|
||||
* @param port the port to listen for command line connections.
|
||||
* @param httpPort the port to listen for HTTP connections, or 0.
|
||||
* @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);
|
||||
public:
|
||||
/**
|
||||
* create a network instance and listening for incoming connections.
|
||||
* @param local true to accept connections only for local host.
|
||||
* @param port the port to listen for command line connections.
|
||||
* @param httpPort the port to listen for HTTP connections, or 0.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~Network();
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~Network();
|
||||
|
||||
/**
|
||||
* endless loop for network instance.
|
||||
*/
|
||||
virtual void run();
|
||||
/**
|
||||
* endless loop for network instance.
|
||||
*/
|
||||
virtual void run();
|
||||
|
||||
/**
|
||||
* shutdown network subsystem.
|
||||
*/
|
||||
void stop() const { m_notify.notify(); usleep(100000); }
|
||||
/**
|
||||
* shutdown network subsystem.
|
||||
*/
|
||||
void stop() const { m_notify.notify(); usleep(100000); }
|
||||
|
||||
|
||||
private:
|
||||
/** the list of active @a Connection instances. */
|
||||
list<Connection*> m_connections;
|
||||
private:
|
||||
/** the list of active @a Connection instances. */
|
||||
list<Connection*> m_connections;
|
||||
|
||||
/** the reference to the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*>* m_netQueue;
|
||||
/** the reference to the @a NetMessage @a Queue. */
|
||||
Queue<NetMessage*>* m_netQueue;
|
||||
|
||||
/** the command line @a TCPServer instance. */
|
||||
TCPServer* m_tcpServer;
|
||||
/** the command line @a TCPServer instance. */
|
||||
TCPServer* m_tcpServer;
|
||||
|
||||
/** the HTTP @a TCPServer instance, or NULL. */
|
||||
TCPServer* m_httpServer;
|
||||
/** the HTTP @a TCPServer instance, or NULL. */
|
||||
TCPServer* m_httpServer;
|
||||
|
||||
/** @a Notify object for shutdown procedure. */
|
||||
Notify m_notify;
|
||||
/** @a Notify object for shutdown procedure. */
|
||||
Notify m_notify;
|
||||
|
||||
/** true if this instance is listening */
|
||||
bool m_listening;
|
||||
/** true if this instance is listening */
|
||||
bool m_listening;
|
||||
|
||||
/**
|
||||
* clean inactive connections from container.
|
||||
*/
|
||||
void cleanConnections();
|
||||
/**
|
||||
* clean inactive connections from container.
|
||||
*/
|
||||
void cleanConnections();
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
namespace ebusd {
|
||||
|
||||
bool libebus_contrib_register() {
|
||||
contrib_tem_register();
|
||||
return true;
|
||||
contrib_tem_register();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "tem.h"
|
||||
@@ -36,108 +36,108 @@ using std::setw;
|
||||
using std::dec;
|
||||
|
||||
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) {
|
||||
if (divisor == 0) {
|
||||
divisor = 1;
|
||||
}
|
||||
if (bitCount == 0) {
|
||||
bitCount = m_bitCount;
|
||||
}
|
||||
if (divisor == 1 && bitCount == 16) {
|
||||
derived = this;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
if (divisor == 0) {
|
||||
divisor = 1;
|
||||
}
|
||||
if (bitCount == 0) {
|
||||
bitCount = m_bitCount;
|
||||
}
|
||||
if (divisor == 1 && bitCount == 16) {
|
||||
derived = this;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
result_t TemParamDataType::readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) {
|
||||
unsigned int value = 0;
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) {
|
||||
unsigned int value = 0;
|
||||
|
||||
result_t result = readRawValue(input, offset, length, value);
|
||||
if (result != RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
result_t result = readRawValue(input, offset, length, value);
|
||||
if (result != RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value == m_replacement) {
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << "null";
|
||||
} else {
|
||||
output << NULL_VALUE;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
int grp = 0, num = 0;
|
||||
if (isMaster) {
|
||||
grp = (value & 0x1f); // grp in bits 0...5
|
||||
num = ((value >> 8) & 0x7f); // num in bits 8...13
|
||||
} else {
|
||||
grp = ((value >> 7) & 0x1f); // grp in bits 7...11
|
||||
num = (value & 0x7f); // num in bits 0...6
|
||||
}
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num);
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill(' ') << setw(0); // reset
|
||||
return RESULT_OK;
|
||||
if (value == m_replacement) {
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << "null";
|
||||
} else {
|
||||
output << NULL_VALUE;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
int grp = 0, num = 0;
|
||||
if (isMaster) {
|
||||
grp = (value & 0x1f); // grp in bits 0...5
|
||||
num = ((value >> 8) & 0x7f); // num in bits 8...13
|
||||
} else {
|
||||
grp = ((value >> 7) & 0x1f); // grp in bits 7...11
|
||||
num = (value & 0x7f); // num in bits 0...6
|
||||
}
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num);
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill(' ') << setw(0); // reset
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t TemParamDataType::writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) {
|
||||
unsigned int value;
|
||||
int grp, num;
|
||||
string token;
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) {
|
||||
unsigned int value;
|
||||
int grp, num;
|
||||
string token;
|
||||
|
||||
const char* str = input.str().c_str();
|
||||
if (strcasecmp(str, NULL_VALUE) == 0) {
|
||||
value = m_replacement; // replacement value
|
||||
} else {
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
char* strEnd = NULL;
|
||||
grp = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
strEnd = NULL;
|
||||
num = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
if (isMaster) {
|
||||
value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13
|
||||
} else {
|
||||
value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6
|
||||
}
|
||||
}
|
||||
if (value < getMinValue() || value > getMaxValue()) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
return writeRawValue(value, offset, length, output, usedLength);
|
||||
const char* str = input.str().c_str();
|
||||
if (strcasecmp(str, NULL_VALUE) == 0) {
|
||||
value = m_replacement; // replacement value
|
||||
} else {
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
char* strEnd = NULL;
|
||||
grp = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
strEnd = NULL;
|
||||
num = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
if (isMaster) {
|
||||
value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13
|
||||
} else {
|
||||
value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6
|
||||
}
|
||||
}
|
||||
if (value < getMinValue() || value > getMaxValue()) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
return writeRawValue(value, offset, length, output, usedLength);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+17
-17
@@ -41,26 +41,26 @@ namespace ebusd {
|
||||
* data.
|
||||
*/
|
||||
class TemParamDataType : public NumberDataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
*/
|
||||
explicit TemParamDataType(const string id)
|
||||
: NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
*/
|
||||
explicit TemParamDataType(const string id)
|
||||
: NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
// @copydoc
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,167 +28,167 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
DataType* type = DataTypeList::getInstance()->get("TEM_P");
|
||||
if (type == NULL) {
|
||||
cout << "datatype not registered" << endl;
|
||||
return 1;
|
||||
}
|
||||
DataType* type = DataTypeList::getInstance()->get("TEM_P");
|
||||
if (type == NULL) {
|
||||
cout << "datatype not registered" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
string checks[][5] = {
|
||||
// entry: definition, decoded value, master data, slave data, flags
|
||||
// definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
{"x,,TEM_P", "04-033", "10fe0700020421", "00", ""},
|
||||
{"x,,TEM_P", "00-000", "10fe0700020000", "00", ""},
|
||||
{"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""},
|
||||
{"x,,TEM_P", "-", "10fe070002ffff", "00", ""},
|
||||
{"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"},
|
||||
string checks[][5] = {
|
||||
// entry: definition, decoded value, master data, slave data, flags
|
||||
// definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
{"x,,TEM_P", "04-033", "10fe0700020421", "00", ""},
|
||||
{"x,,TEM_P", "00-000", "10fe0700020000", "00", ""},
|
||||
{"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""},
|
||||
{"x,,TEM_P", "-", "10fe070002ffff", "00", ""},
|
||||
{"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"},
|
||||
|
||||
{"x,,TEM_P", "04-033", "1015070000", "022102", ""},
|
||||
{"x,,TEM_P", "00-000", "1015070000", "020000", ""},
|
||||
{"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""},
|
||||
{"x,,TEM_P", "-", "1015070000", "02ffff", ""},
|
||||
{"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"},
|
||||
};
|
||||
DataFieldTemplates* templates = new DataFieldTemplates();
|
||||
DataField* fields = NULL;
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
istringstream isstr(check[0]);
|
||||
string expectStr = check[1];
|
||||
SymbolString mstr(false);
|
||||
result_t result = mstr.parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
SymbolString sstr(false);
|
||||
result = sstr.parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
string flags = check[4];
|
||||
bool isSet = flags.find('s') != string::npos;
|
||||
bool failedRead = flags.find('r') != string::npos;
|
||||
bool failedReadMatch = flags.find('R') != string::npos;
|
||||
bool failedWrite = flags.find('w') != string::npos;
|
||||
bool failedWriteMatch = flags.find('W') != string::npos;
|
||||
string item;
|
||||
vector<string> entries;
|
||||
{"x,,TEM_P", "04-033", "1015070000", "022102", ""},
|
||||
{"x,,TEM_P", "00-000", "1015070000", "020000", ""},
|
||||
{"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""},
|
||||
{"x,,TEM_P", "-", "1015070000", "02ffff", ""},
|
||||
{"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"},
|
||||
};
|
||||
DataFieldTemplates* templates = new DataFieldTemplates();
|
||||
DataField* fields = NULL;
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
istringstream isstr(check[0]);
|
||||
string expectStr = check[1];
|
||||
SymbolString mstr(false);
|
||||
result_t result = mstr.parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
SymbolString sstr(false);
|
||||
result = sstr.parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
string flags = check[4];
|
||||
bool isSet = flags.find('s') != string::npos;
|
||||
bool failedRead = flags.find('r') != string::npos;
|
||||
bool failedReadMatch = flags.find('R') != string::npos;
|
||||
bool failedWrite = flags.find('w') != string::npos;
|
||||
bool failedWriteMatch = flags.find('W') != string::npos;
|
||||
string item;
|
||||
vector<string> entries;
|
||||
|
||||
while (getline(isstr, item, FIELD_SEPARATOR))
|
||||
entries.push_back(item);
|
||||
while (getline(isstr, item, FIELD_SEPARATOR))
|
||||
entries.push_back(item);
|
||||
|
||||
if (fields != NULL) {
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1])));
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (fields == NULL) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\"=\"";
|
||||
fields->dump(cout);
|
||||
cout << "\": create OK" << endl;
|
||||
if (fields != NULL) {
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1])));
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (fields == NULL) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\"=\"";
|
||||
fields->dump(cout);
|
||||
cout << "\": create OK" << endl;
|
||||
|
||||
ostringstream output;
|
||||
SymbolString writeMstr(false);
|
||||
result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
SymbolString writeSstr(false);
|
||||
result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty());
|
||||
}
|
||||
if (failedRead) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0;
|
||||
verify(failedReadMatch, "read", check[2], match, expectStr, output.str());
|
||||
}
|
||||
ostringstream output;
|
||||
SymbolString writeMstr(false);
|
||||
result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
SymbolString writeSstr(false);
|
||||
result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty());
|
||||
}
|
||||
if (failedRead) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0;
|
||||
verify(failedReadMatch, "read", check[2], match, expectStr, output.str());
|
||||
}
|
||||
|
||||
istringstream input(expectStr);
|
||||
result = fields->write(input, pt_masterData, writeMstr, 0);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->write(input, pt_slaveData, writeSstr, 0);
|
||||
}
|
||||
if (failedWrite) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " write " << fields->getName() << " >" << expectStr
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
istringstream input(expectStr);
|
||||
result = fields->write(input, pt_masterData, writeMstr, 0);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->write(input, pt_slaveData, writeSstr, 0);
|
||||
}
|
||||
if (failedWrite) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " write " << fields->getName() << " >" << expectStr
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
|
||||
delete templates;
|
||||
return error ? 1 : 0;
|
||||
delete templates;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
+877
-877
File diff suppressed because it is too large
Load Diff
+483
-483
File diff suppressed because it is too large
Load Diff
+934
-934
File diff suppressed because it is too large
Load Diff
+341
-341
@@ -89,9 +89,9 @@ static const unsigned int OF_JSON = 0x10; //!< JSON format.
|
||||
|
||||
/** the message part in which a data field is stored. */
|
||||
enum PartType {
|
||||
pt_any, //!< stored in any data (master or slave)
|
||||
pt_masterData, //!< stored in master data
|
||||
pt_slaveData, //!< stored in slave data
|
||||
pt_any, //!< stored in any data (master or slave)
|
||||
pt_masterData, //!< stored in master data
|
||||
pt_slaveData, //!< stored in slave data
|
||||
};
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
class DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 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).
|
||||
*/
|
||||
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) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 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).
|
||||
*/
|
||||
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) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataType() { }
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataType() { }
|
||||
|
||||
/**
|
||||
* @return the type identifier.
|
||||
*/
|
||||
string getId() const { return m_id; }
|
||||
/**
|
||||
* @return the type identifier.
|
||||
*/
|
||||
string getId() const { return m_id; }
|
||||
|
||||
/**
|
||||
* @return the number of bits (maximum length if #ADJ flag is set).
|
||||
*/
|
||||
unsigned char getBitCount() const { return m_bitCount; }
|
||||
/**
|
||||
* @return the number of bits (maximum length if #ADJ flag is set).
|
||||
*/
|
||||
unsigned char getBitCount() const { return m_bitCount; }
|
||||
|
||||
/**
|
||||
* Check whether a flag is set.
|
||||
* @param flag the flag to check (like #BCD).
|
||||
* @return whether the flag is set.
|
||||
*/
|
||||
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
|
||||
/**
|
||||
* Check whether a flag is set.
|
||||
* @param flag the flag to check (like #BCD).
|
||||
* @return whether the flag is set.
|
||||
*/
|
||||
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
|
||||
|
||||
/**
|
||||
* @return whether this type is ignored.
|
||||
*/
|
||||
bool isIgnored() const { return hasFlag(IGN); }
|
||||
/**
|
||||
* @return whether this type is ignored.
|
||||
*/
|
||||
bool isIgnored() const { return hasFlag(IGN); }
|
||||
|
||||
/**
|
||||
* @return whether this type has an adjustable length.
|
||||
*/
|
||||
bool isAdjustableLength() const { return hasFlag(ADJ); }
|
||||
/**
|
||||
* @return whether this type has an adjustable length.
|
||||
*/
|
||||
bool isAdjustableLength() const { return hasFlag(ADJ); }
|
||||
|
||||
/**
|
||||
* @return whether this field is derived from @a NumberDataType.
|
||||
*/
|
||||
bool isNumeric() const { return hasFlag(NUM); }
|
||||
/**
|
||||
* @return whether this field is derived from @a NumberDataType.
|
||||
*/
|
||||
bool isNumeric() const { return hasFlag(NUM); }
|
||||
|
||||
/**
|
||||
* @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; }
|
||||
/**
|
||||
* @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; }
|
||||
|
||||
/**
|
||||
* Dump the type identifier with the specified length and optionally the
|
||||
* divisor to the output (@a FIELD_SEPARATOR is always appended!).
|
||||
* @param output the @a ostream to dump to.
|
||||
* @param length the number of symbols to read/write.
|
||||
* @return true when a non-default divisor was written to the output.
|
||||
*/
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
/**
|
||||
* Dump the type identifier with the specified length and optionally the
|
||||
* divisor to the output (@a FIELD_SEPARATOR is always appended!).
|
||||
* @param output the @a ostream to dump to.
|
||||
* @param length the number of symbols to read/write.
|
||||
* @return true when a non-default divisor was written to the output.
|
||||
*/
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
|
||||
/**
|
||||
* 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 offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param value the variable in which to store the numeric raw value.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value) = 0;
|
||||
/**
|
||||
* 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 offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param value the variable in which to store the numeric raw value.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value) = 0;
|
||||
|
||||
/**
|
||||
* Internal method for reading the field from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param output the ostringstream to append the formatted value to.
|
||||
* @param outputFormat the @a OutputFormat options to use.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||
/**
|
||||
* Internal method for reading the field from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param output the ostringstream to append the formatted value to.
|
||||
* @param outputFormat the @a OutputFormat options to use.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||
|
||||
/**
|
||||
* Internal method for writing the field to a @a SymbolString.
|
||||
* @param input the @a istringstream to parse the formatted value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @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 isMaster whether the @a SymbolString is the master part.
|
||||
* @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.
|
||||
*/
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0;
|
||||
/**
|
||||
* Internal method for writing the field to a @a SymbolString.
|
||||
* @param input the @a istringstream to parse the formatted value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @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 isMaster whether the @a SymbolString is the master part.
|
||||
* @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.
|
||||
*/
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
/** the type identifier. */
|
||||
const string m_id;
|
||||
protected:
|
||||
/** the type identifier. */
|
||||
const string m_id;
|
||||
|
||||
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
|
||||
const unsigned char m_bitCount;
|
||||
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
|
||||
const unsigned char m_bitCount;
|
||||
|
||||
/** the combination of flags (like #BCD). */
|
||||
const uint16_t m_flags;
|
||||
/** the combination of flags (like #BCD). */
|
||||
const uint16_t m_flags;
|
||||
|
||||
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
|
||||
const unsigned int m_replacement;
|
||||
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
|
||||
const unsigned int m_replacement;
|
||||
};
|
||||
|
||||
|
||||
@@ -273,43 +273,43 @@ class DataType {
|
||||
* A string based @a DataType.
|
||||
*/
|
||||
class StringDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value).
|
||||
* @param isHex true for hex digits instead of characters.
|
||||
*/
|
||||
StringDataType(const string id, const unsigned char bitCount, const uint16_t flags,
|
||||
const unsigned int replacement, bool isHex = false)
|
||||
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value).
|
||||
* @param isHex true for hex digits instead of characters.
|
||||
*/
|
||||
StringDataType(const string id, const unsigned char bitCount, const uint16_t flags,
|
||||
const unsigned int replacement, bool isHex = false)
|
||||
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~StringDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~StringDataType() {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** true for hex digits instead of characters. */
|
||||
const bool m_isHex;
|
||||
private:
|
||||
/** true for hex digits instead of characters. */
|
||||
const bool m_isHex;
|
||||
};
|
||||
|
||||
|
||||
@@ -317,66 +317,66 @@ class StringDataType : public DataType {
|
||||
* A date/time based @a DataType.
|
||||
*/
|
||||
class DateTimeDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value.
|
||||
* @param hasDate true if date part is present.
|
||||
* @param hasTime true if time part is present.
|
||||
* @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,
|
||||
const bool hasDate, const bool hasTime, const int16_t resolution)
|
||||
: DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @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 flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value.
|
||||
* @param hasDate true if date part is present.
|
||||
* @param hasTime true if time part is present.
|
||||
* @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,
|
||||
const bool hasDate, const bool hasTime, const int16_t resolution)
|
||||
: DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DateTimeDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DateTimeDataType() {}
|
||||
|
||||
/**
|
||||
* @return true if date part is present.
|
||||
*/
|
||||
bool hasDate() const { return m_hasDate; }
|
||||
/**
|
||||
* @return true if date part is present.
|
||||
*/
|
||||
bool hasDate() const { return m_hasDate; }
|
||||
|
||||
/**
|
||||
* @return true if time part is present.
|
||||
*/
|
||||
bool hasTime() const { return m_hasTime; }
|
||||
/**
|
||||
* @return true if time part is present.
|
||||
*/
|
||||
bool hasTime() const { return m_hasTime; }
|
||||
|
||||
/**
|
||||
* @return the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
int16_t getResolution() const { return m_resolution; }
|
||||
/**
|
||||
* @return the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
int16_t getResolution() const { return m_resolution; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** true if date part is present. */
|
||||
const bool m_hasDate;
|
||||
private:
|
||||
/** true if date part is present. */
|
||||
const bool m_hasDate;
|
||||
|
||||
/** true if time part is present. */
|
||||
const bool m_hasTime;
|
||||
/** true if time part is present. */
|
||||
const bool m_hasTime;
|
||||
|
||||
/** the resolution in minutes for time types, or 1. */
|
||||
const int16_t m_resolution;
|
||||
/** the resolution in minutes for time types, or 1. */
|
||||
const int16_t m_resolution;
|
||||
};
|
||||
|
||||
|
||||
@@ -384,135 +384,135 @@ class DateTimeDataType : public DataType {
|
||||
* A number based @a DataType.
|
||||
*/
|
||||
class NumberDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance for multiple of 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (no replacement if equal to minValue).
|
||||
* @param minValue the minimum raw value.
|
||||
* @param maxValue the maximum raw value.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
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)
|
||||
: 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) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance for multiple of 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (no replacement if equal to minValue).
|
||||
* @param minValue the minimum raw value.
|
||||
* @param maxValue the maximum raw value.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
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)
|
||||
: 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.
|
||||
* @param id the type identifier.
|
||||
* @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 replacement the replacement value (no replacement if zero).
|
||||
* @param firstBit the offset to the first bit.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
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) {}
|
||||
/**
|
||||
* Constructs a new instance for less than 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @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 replacement the replacement value (no replacement if zero).
|
||||
* @param firstBit the offset to the first bit.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
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) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~NumberDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~NumberDataType() {}
|
||||
|
||||
/**
|
||||
* Calculate the precision from the divisor.
|
||||
*
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
static unsigned char calcPrecision(const int divisor);
|
||||
/**
|
||||
* Calculate the precision from the divisor.
|
||||
*
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
static unsigned char calcPrecision(const int divisor);
|
||||
|
||||
// @copydoc
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
// @copydoc
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
|
||||
/**
|
||||
* Derive a new @a NumberDataType from this.
|
||||
* @param divisor the extra divisor (negative for reciprocal) to apply, or
|
||||
* 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,
|
||||
* 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
|
||||
* not necessary.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
/**
|
||||
* Derive a new @a NumberDataType from this.
|
||||
* @param divisor the extra divisor (negative for reciprocal) to apply, or
|
||||
* 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,
|
||||
* 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
|
||||
* not necessary.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
|
||||
/**
|
||||
* @return the minimum raw value.
|
||||
*/
|
||||
unsigned int getMinValue() const { return m_minValue; }
|
||||
/**
|
||||
* @return the minimum raw value.
|
||||
*/
|
||||
unsigned int getMinValue() const { return m_minValue; }
|
||||
|
||||
/**
|
||||
* @return the maximum raw value.
|
||||
*/
|
||||
unsigned int getMaxValue() const { return m_maxValue; }
|
||||
/**
|
||||
* @return the maximum raw value.
|
||||
*/
|
||||
unsigned int getMaxValue() const { return m_maxValue; }
|
||||
|
||||
/**
|
||||
* @return the divisor (negative for reciprocal).
|
||||
*/
|
||||
int getDivisor() const { return m_divisor; }
|
||||
/**
|
||||
* @return the divisor (negative for reciprocal).
|
||||
*/
|
||||
int getDivisor() const { return m_divisor; }
|
||||
|
||||
/**
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
unsigned char getPrecision() const { return m_precision; }
|
||||
/**
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
unsigned char getPrecision() const { return m_precision; }
|
||||
|
||||
/**
|
||||
* @return the offset to the first bit.
|
||||
*/
|
||||
int16_t getFirstBit() const { return m_firstBit; }
|
||||
/**
|
||||
* @return the offset to the first bit.
|
||||
*/
|
||||
int16_t getFirstBit() const { return m_firstBit; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
/**
|
||||
* Internal method for writing the numeric raw value to a @a SymbolString.
|
||||
* @param value the numeric raw value to write.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @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 usedLength the variable in which to store the used length in bytes,
|
||||
* or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeRawValue(unsigned int value,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, unsigned char* usedLength = NULL);
|
||||
/**
|
||||
* Internal method for writing the numeric raw value to a @a SymbolString.
|
||||
* @param value the numeric raw value to write.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @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 usedLength the variable in which to store the used length in bytes,
|
||||
* or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeRawValue(unsigned int value,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, unsigned char* usedLength = NULL);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** the minimum raw value. */
|
||||
const unsigned int m_minValue;
|
||||
private:
|
||||
/** the minimum raw value. */
|
||||
const unsigned int m_minValue;
|
||||
|
||||
/** the maximum raw value. */
|
||||
const unsigned int m_maxValue;
|
||||
/** the maximum raw value. */
|
||||
const unsigned int m_maxValue;
|
||||
|
||||
/** the divisor (negative for reciprocal). */
|
||||
const int m_divisor;
|
||||
/** the divisor (negative for reciprocal). */
|
||||
const int m_divisor;
|
||||
|
||||
/** the precision for formatting the value. */
|
||||
const unsigned char m_precision;
|
||||
/** the precision for formatting the value. */
|
||||
const unsigned char m_precision;
|
||||
|
||||
/** the offset to the first bit. */
|
||||
const int16_t m_firstBit;
|
||||
/** the offset to the first bit. */
|
||||
const int16_t m_firstBit;
|
||||
|
||||
/** the base @a NumberDataType for derived instances. */
|
||||
NumberDataType* m_baseType;
|
||||
/** the base @a NumberDataType for derived instances. */
|
||||
NumberDataType* m_baseType;
|
||||
};
|
||||
|
||||
|
||||
@@ -520,71 +520,71 @@ class NumberDataType : public DataType {
|
||||
* A map of base @a DataType instances.
|
||||
*/
|
||||
class DataTypeList {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance and registers the known base data types.
|
||||
*/
|
||||
DataTypeList();
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance and registers the known base data types.
|
||||
*/
|
||||
DataTypeList();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataTypeList() {
|
||||
clear();
|
||||
}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataTypeList() {
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
* @return the singleton @a DataTypeList instance.
|
||||
*/
|
||||
static DataTypeList* getInstance();
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
* @return the singleton @a DataTypeList instance.
|
||||
*/
|
||||
static DataTypeList* getInstance();
|
||||
|
||||
/**
|
||||
* Removes all @a DataType instances.
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* Removes all @a DataType instances.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Adds a @a DataType instance to this map.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
* Note: the caller may not free the added instance on success.
|
||||
*/
|
||||
result_t add(DataType* dataType);
|
||||
/**
|
||||
* Adds a @a DataType instance to this map.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
* Note: the caller may not free the added instance on success.
|
||||
*/
|
||||
result_t add(DataType* dataType);
|
||||
|
||||
/**
|
||||
* Adds a @a DataType instance for later cleanup.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
*/
|
||||
void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); }
|
||||
/**
|
||||
* Adds a @a DataType instance for later cleanup.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
*/
|
||||
void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); }
|
||||
|
||||
/**
|
||||
* Gets the @a DataType instance with the specified ID.
|
||||
* @param id the ID string (excluding optional length suffix).
|
||||
* @param length the length in bytes, or 0 for default.
|
||||
* @return the @a DataType instance, or NULL if not available.
|
||||
* Note: the caller may not free the instance.
|
||||
*/
|
||||
DataType* get(const string id, const unsigned char length = 0);
|
||||
/**
|
||||
* Gets the @a DataType instance with the specified ID.
|
||||
* @param id the ID string (excluding optional length suffix).
|
||||
* @param length the length in bytes, or 0 for default.
|
||||
* @return the @a DataType instance, or NULL if not available.
|
||||
* Note: the caller may not free the instance.
|
||||
*/
|
||||
DataType* get(const string id, const unsigned char length = 0);
|
||||
|
||||
|
||||
private:
|
||||
/** the known @a DataType instances by ID only. */
|
||||
map<string, DataType*> m_typesById;
|
||||
private:
|
||||
/** the known @a DataType instances by ID only. */
|
||||
map<string, DataType*> m_typesById;
|
||||
|
||||
/** the known @a DataType instances by ID and length (i.e. "ID:BITS").
|
||||
* Note: adjustable length types are stored by ID only. */
|
||||
map<string, DataType*> m_typesByIdLength;
|
||||
/** the known @a DataType instances by ID and length (i.e. "ID:BITS").
|
||||
* Note: adjustable length types are stored by ID only. */
|
||||
map<string, DataType*> m_typesByIdLength;
|
||||
|
||||
/** the @a DataType instances to cleanup. */
|
||||
list<DataType*> m_cleanupTypes;
|
||||
/** the @a DataType instances to cleanup. */
|
||||
list<DataType*> m_cleanupTypes;
|
||||
|
||||
/** the singleton instance. */
|
||||
static DataTypeList s_instance;
|
||||
/** the singleton instance. */
|
||||
static DataTypeList s_instance;
|
||||
|
||||
#ifdef HAVE_CONTRIB
|
||||
/** true when contributed datatypes were successfully initialized. */
|
||||
static bool s_contrib_initialized;
|
||||
/** true when contributed datatypes were successfully initialized. */
|
||||
static bool s_contrib_initialized;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+218
-218
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "device.h"
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_PPOLL
|
||||
# include <poll.h>
|
||||
# include <poll.h>
|
||||
#endif
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -39,289 +39,289 @@
|
||||
namespace ebusd {
|
||||
|
||||
Device::~Device() {
|
||||
close();
|
||||
close();
|
||||
}
|
||||
|
||||
Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) {
|
||||
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
|
||||
char* in = strdup(name);
|
||||
bool udp = false;
|
||||
char* addrpos = in;
|
||||
char* portpos = strchr(addrpos, ':');
|
||||
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
|
||||
addrpos += 4;
|
||||
portpos = strchr(addrpos, ':');
|
||||
}
|
||||
if (portpos == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid protocol or missing port
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
free(in);
|
||||
return NULL; // invalid port
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
|
||||
*portpos = 0;
|
||||
if (inet_aton(addrpos, &address.sin_addr) == 0) {
|
||||
struct hostent* h = gethostbyname(addrpos);
|
||||
if (h == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid host
|
||||
}
|
||||
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
|
||||
}
|
||||
free(in);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons((uint16_t)port);
|
||||
return new NetworkDevice(name, address, readOnly, initialSend, udp);
|
||||
}
|
||||
return new SerialDevice(name, checkDevice, readOnly, initialSend);
|
||||
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
|
||||
char* in = strdup(name);
|
||||
bool udp = false;
|
||||
char* addrpos = in;
|
||||
char* portpos = strchr(addrpos, ':');
|
||||
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
|
||||
addrpos += 4;
|
||||
portpos = strchr(addrpos, ':');
|
||||
}
|
||||
if (portpos == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid protocol or missing port
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
free(in);
|
||||
return NULL; // invalid port
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
|
||||
*portpos = 0;
|
||||
if (inet_aton(addrpos, &address.sin_addr) == 0) {
|
||||
struct hostent* h = gethostbyname(addrpos);
|
||||
if (h == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid host
|
||||
}
|
||||
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
|
||||
}
|
||||
free(in);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons((uint16_t)port);
|
||||
return new NetworkDevice(name, address, readOnly, initialSend, udp);
|
||||
}
|
||||
return new SerialDevice(name, checkDevice, readOnly, initialSend);
|
||||
}
|
||||
|
||||
void Device::close() {
|
||||
if (m_fd != -1) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
if (m_fd != -1) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::isValid() {
|
||||
if (m_fd == -1) {
|
||||
return false;
|
||||
}
|
||||
if (m_checkDevice) {
|
||||
checkDevice();
|
||||
}
|
||||
return m_fd != -1;
|
||||
if (m_fd == -1) {
|
||||
return false;
|
||||
}
|
||||
if (m_checkDevice) {
|
||||
checkDevice();
|
||||
}
|
||||
return m_fd != -1;
|
||||
}
|
||||
|
||||
result_t Device::send(const unsigned char value) {
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_readOnly || write(value) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, false);
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_readOnly || write(value) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, false);
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t Device::recv(const unsigned int timeout, unsigned char& value) {
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (!available() && timeout > 0) {
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (!available() && timeout > 0) {
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
|
||||
// set select timeout
|
||||
tdiff.tv_sec = timeout/1000000;
|
||||
tdiff.tv_nsec = (timeout%1000000)*1000;
|
||||
// set select timeout
|
||||
tdiff.tv_sec = timeout/1000000;
|
||||
tdiff.tv_nsec = (timeout%1000000)*1000;
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
int nfds = 1;
|
||||
struct pollfd fds[nfds];
|
||||
int nfds = 1;
|
||||
struct pollfd fds[nfds];
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
fds[0].fd = m_fd;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].fd = m_fd;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
fd_set readfds;
|
||||
fd_set readfds;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(m_fd, &readfds);
|
||||
FD_ZERO(&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
|
||||
ret = 1; // ignore timeout if neither ppoll nor pselect are available
|
||||
ret = 1; // ignore timeout if neither ppoll nor pselect are available
|
||||
#endif
|
||||
#endif
|
||||
if (ret == -1) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return RESULT_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
if (ret == -1) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return RESULT_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// directly read byte from device
|
||||
ssize_t nbytes = read(value);
|
||||
if (nbytes == 0) {
|
||||
return RESULT_ERR_EOF;
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, true);
|
||||
}
|
||||
return RESULT_OK;
|
||||
// directly read byte from device
|
||||
ssize_t nbytes = read(value);
|
||||
if (nbytes == 0) {
|
||||
return RESULT_ERR_EOF;
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, true);
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
result_t SerialDevice::open() {
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
struct termios newSettings;
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
struct termios newSettings;
|
||||
|
||||
// open file descriptor
|
||||
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
|
||||
// open file descriptor
|
||||
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
|
||||
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (isatty(m_fd) == 0) {
|
||||
close();
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (isatty(m_fd) == 0) {
|
||||
close();
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
if (flock(m_fd, LOCK_EX|LOCK_NB)) {
|
||||
close();
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (flock(m_fd, LOCK_EX|LOCK_NB)) {
|
||||
close();
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
|
||||
// save current settings
|
||||
tcgetattr(m_fd, &m_oldSettings);
|
||||
// save current settings
|
||||
tcgetattr(m_fd, &m_oldSettings);
|
||||
|
||||
// create new settings
|
||||
memset(&newSettings, '\0', sizeof(newSettings));
|
||||
// create new settings
|
||||
memset(&newSettings, '\0', sizeof(newSettings));
|
||||
|
||||
newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD);
|
||||
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
|
||||
newSettings.c_iflag |= IGNPAR; // ignore parity errors
|
||||
newSettings.c_oflag &= ~OPOST;
|
||||
newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD);
|
||||
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
|
||||
newSettings.c_iflag |= IGNPAR; // ignore parity errors
|
||||
newSettings.c_oflag &= ~OPOST;
|
||||
|
||||
// non-canonical mode: read() blocks until at least one byte is available
|
||||
newSettings.c_cc[VMIN] = 1;
|
||||
newSettings.c_cc[VTIME] = 0;
|
||||
// non-canonical mode: read() blocks until at least one byte is available
|
||||
newSettings.c_cc[VMIN] = 1;
|
||||
newSettings.c_cc[VTIME] = 0;
|
||||
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIFLUSH);
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIFLUSH);
|
||||
|
||||
// activate new settings of serial device
|
||||
tcsetattr(m_fd, TCSAFLUSH, &newSettings);
|
||||
// activate new settings of serial device
|
||||
tcsetattr(m_fd, TCSAFLUSH, &newSettings);
|
||||
|
||||
// set serial device into blocking mode
|
||||
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
|
||||
// set serial device into blocking mode
|
||||
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
|
||||
|
||||
if (m_initialSend && write(ESC) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (m_initialSend && write(ESC) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void SerialDevice::close() {
|
||||
if (m_fd != -1) {
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIOFLUSH);
|
||||
if (m_fd != -1) {
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIOFLUSH);
|
||||
|
||||
// restore previous settings of the device
|
||||
tcsetattr(m_fd, TCSANOW, &m_oldSettings);
|
||||
}
|
||||
Device::close();
|
||||
// restore previous settings of the device
|
||||
tcsetattr(m_fd, TCSANOW, &m_oldSettings);
|
||||
}
|
||||
Device::close();
|
||||
}
|
||||
|
||||
void SerialDevice::checkDevice() {
|
||||
int port;
|
||||
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
|
||||
close();
|
||||
}
|
||||
int port;
|
||||
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result_t NetworkDevice::open() {
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int ret;
|
||||
if (m_udp) {
|
||||
struct sockaddr_in address = m_address;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address));
|
||||
} else {
|
||||
int value = 1;
|
||||
ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
value = 1;
|
||||
setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
|
||||
}
|
||||
if (ret < 0) {
|
||||
close();
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int cnt;
|
||||
if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) {
|
||||
// skip buffered input
|
||||
unsigned char buf[256];
|
||||
while (::read(m_fd, &buf, 256) > 0) {
|
||||
}
|
||||
}
|
||||
if (m_bufSize == 0) {
|
||||
m_bufSize = MAX_LEN+1;
|
||||
m_buffer = (unsigned char*)malloc(m_bufSize);
|
||||
if (!m_buffer) {
|
||||
m_bufSize = 0;
|
||||
}
|
||||
}
|
||||
m_bufLen = 0;
|
||||
if (m_initialSend && write(ESC) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int ret;
|
||||
if (m_udp) {
|
||||
struct sockaddr_in address = m_address;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address));
|
||||
} else {
|
||||
int value = 1;
|
||||
ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
value = 1;
|
||||
setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
|
||||
}
|
||||
if (ret < 0) {
|
||||
close();
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int cnt;
|
||||
if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) {
|
||||
// skip buffered input
|
||||
unsigned char buf[256];
|
||||
while (::read(m_fd, &buf, 256) > 0) {
|
||||
}
|
||||
}
|
||||
if (m_bufSize == 0) {
|
||||
m_bufSize = MAX_LEN+1;
|
||||
m_buffer = (unsigned char*)malloc(m_bufSize);
|
||||
if (!m_buffer) {
|
||||
m_bufSize = 0;
|
||||
}
|
||||
}
|
||||
m_bufLen = 0;
|
||||
if (m_initialSend && write(ESC) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void NetworkDevice::checkDevice() {
|
||||
unsigned char value;
|
||||
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
if (c == 0 || (c < 0 && errno != EAGAIN)) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
close();
|
||||
}
|
||||
unsigned char value;
|
||||
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
if (c == 0 || (c < 0 && errno != EAGAIN)) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkDevice::available() {
|
||||
return m_buffer && m_bufLen > 0;
|
||||
return m_buffer && m_bufLen > 0;
|
||||
}
|
||||
|
||||
ssize_t NetworkDevice::write(const unsigned char value) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
return Device::write(value);
|
||||
m_bufLen = 0; // flush read buffer
|
||||
return Device::write(value);
|
||||
}
|
||||
|
||||
ssize_t NetworkDevice::read(unsigned char& value) {
|
||||
if (available()) {
|
||||
value = m_buffer[m_bufPos];
|
||||
m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize);
|
||||
m_bufLen--;
|
||||
return 1;
|
||||
}
|
||||
if (m_bufSize > 0) {
|
||||
ssize_t size = ::read(m_fd, m_buffer, m_bufSize);
|
||||
if (size <= 0) {
|
||||
return size;
|
||||
}
|
||||
value = m_buffer[0];
|
||||
m_bufPos = 1;
|
||||
m_bufLen = (unsigned char)(size-1);
|
||||
return size;
|
||||
}
|
||||
return Device::read(value);
|
||||
if (available()) {
|
||||
value = m_buffer[m_bufPos];
|
||||
m_bufPos = (unsigned char)((m_bufPos+1)%m_bufSize);
|
||||
m_bufLen--;
|
||||
return 1;
|
||||
}
|
||||
if (m_bufSize > 0) {
|
||||
ssize_t size = ::read(m_fd, m_buffer, m_bufSize);
|
||||
if (size <= 0) {
|
||||
return size;
|
||||
}
|
||||
value = m_buffer[0];
|
||||
m_bufPos = 1;
|
||||
m_bufLen = (unsigned char)(size-1);
|
||||
return size;
|
||||
}
|
||||
return Device::read(value);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+177
-177
@@ -42,18 +42,18 @@ namespace ebusd {
|
||||
* Interface for listening to data received on/sent to a device.
|
||||
*/
|
||||
class DeviceListener {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DeviceListener() {}
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DeviceListener() {}
|
||||
|
||||
/**
|
||||
* Listener method that is called when a data byte was received/sent.
|
||||
* @param byte the data byte received/sent.
|
||||
* @param received @a true on reception, @a false on sending.
|
||||
*/
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract
|
||||
/**
|
||||
* Listener method that is called when a data byte was received/sent.
|
||||
* @param byte the data byte received/sent.
|
||||
* @param received @a true on reception, @a false on sending.
|
||||
*/
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract
|
||||
};
|
||||
|
||||
|
||||
@@ -61,227 +61,227 @@ class DeviceListener {
|
||||
* The base class for accessing an eBUS.
|
||||
*/
|
||||
class Device {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @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)
|
||||
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
|
||||
m_listener(NULL) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @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)
|
||||
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
|
||||
m_listener(NULL) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Device();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Device();
|
||||
|
||||
/**
|
||||
* 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 checkDevice whether to regularly check the device availability (only for serial devices).
|
||||
* @param readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
* @return the new @a Device, or NULL on error.
|
||||
* 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);
|
||||
/**
|
||||
* 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 checkDevice whether to regularly check the device availability (only for serial devices).
|
||||
* @param readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
* @return the new @a Device, or NULL on error.
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Get the transfer latency of this device.
|
||||
* @return the transfer latency in microseconds.
|
||||
*/
|
||||
virtual unsigned int getLatency() const { return 0; }
|
||||
/**
|
||||
* Get the transfer latency of this device.
|
||||
* @return the transfer latency in microseconds.
|
||||
*/
|
||||
virtual unsigned int getLatency() const { return 0; }
|
||||
|
||||
/**
|
||||
* Open the file descriptor.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t open() = 0; // abstract
|
||||
/**
|
||||
* Open the file descriptor.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t open() = 0; // abstract
|
||||
|
||||
/**
|
||||
* Close the file descriptor if opened.
|
||||
*/
|
||||
virtual void close();
|
||||
/**
|
||||
* Close the file descriptor if opened.
|
||||
*/
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* Write a single byte to the device.
|
||||
* @param value the byte value to write.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
result_t send(const unsigned char value);
|
||||
/**
|
||||
* Write a single byte to the device.
|
||||
* @param value the byte value to write.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
result_t send(const unsigned char value);
|
||||
|
||||
/**
|
||||
* Read a single byte from the device.
|
||||
* @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.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
result_t recv(const unsigned int timeout, unsigned char& value);
|
||||
/**
|
||||
* Read a single byte from the device.
|
||||
* @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.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
result_t recv(const unsigned int timeout, unsigned char& value);
|
||||
|
||||
/**
|
||||
* Return the device name.
|
||||
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
*/
|
||||
const char* getName() { return m_name; }
|
||||
/**
|
||||
* Return the device name.
|
||||
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
*/
|
||||
const char* getName() { return m_name; }
|
||||
|
||||
/**
|
||||
* Return whether the device is opened and available.
|
||||
* @return whether the device is opened and available.
|
||||
*/
|
||||
bool isValid();
|
||||
/**
|
||||
* Return whether the device is opened and available.
|
||||
* @return whether the device is opened and available.
|
||||
*/
|
||||
bool isValid();
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
/**
|
||||
* Set the @a DeviceListener.
|
||||
* @param listener the @a DeviceListener.
|
||||
*/
|
||||
void setListener(DeviceListener* listener) { m_listener = listener; }
|
||||
/**
|
||||
* Set the @a DeviceListener.
|
||||
* @param listener the @a DeviceListener.
|
||||
*/
|
||||
void setListener(DeviceListener* listener) { m_listener = listener; }
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Check if the device is still available and close it if not.
|
||||
*/
|
||||
virtual void checkDevice() = 0; // abstract
|
||||
protected:
|
||||
/**
|
||||
* Check if the device is still available and close it if not.
|
||||
*/
|
||||
virtual void checkDevice() = 0; // abstract
|
||||
|
||||
/**
|
||||
* Check whether a byte is available immediately (without waiting).
|
||||
* @return true when a a byte is available immediately.
|
||||
*/
|
||||
virtual bool available() { return false; }
|
||||
/**
|
||||
* Check whether a byte is available immediately (without waiting).
|
||||
* @return true when a a byte is available immediately.
|
||||
*/
|
||||
virtual bool available() { return false; }
|
||||
|
||||
/**
|
||||
* Write a single byte.
|
||||
* @param value the byte value to write.
|
||||
* @return the number of bytes written, or -1 on error.
|
||||
*/
|
||||
virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); }
|
||||
/**
|
||||
* Write a single byte.
|
||||
* @param value the byte value to write.
|
||||
* @return the number of bytes written, or -1 on error.
|
||||
*/
|
||||
virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); }
|
||||
|
||||
/**
|
||||
* Read a single byte.
|
||||
* @param value the reference in which the read byte value is stored.
|
||||
* @return the number of bytes read, or -1 on error.
|
||||
*/
|
||||
virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); }
|
||||
/**
|
||||
* Read a single byte.
|
||||
* @param value the reference in which the read byte value is stored.
|
||||
* @return the number of bytes read, or -1 on error.
|
||||
*/
|
||||
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). */
|
||||
const char* m_name;
|
||||
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
|
||||
const char* m_name;
|
||||
|
||||
/** whether to regularly check the device availability (only for serial devices). */
|
||||
const bool m_checkDevice;
|
||||
/** whether to regularly check the device availability (only for serial devices). */
|
||||
const bool m_checkDevice;
|
||||
|
||||
/** whether to allow read access to the device only. */
|
||||
const bool m_readOnly;
|
||||
/** whether to allow read access to the device only. */
|
||||
const bool m_readOnly;
|
||||
|
||||
/** whether to send an initial @a ESC symbol in @a open(). */
|
||||
const bool m_initialSend;
|
||||
/** whether to send an initial @a ESC symbol in @a open(). */
|
||||
const bool m_initialSend;
|
||||
|
||||
/** the opened file descriptor, or -1. */
|
||||
int m_fd;
|
||||
/** the opened file descriptor, or -1. */
|
||||
int m_fd;
|
||||
|
||||
|
||||
private:
|
||||
/** the @a DeviceListener, or NULL. */
|
||||
DeviceListener* m_listener;
|
||||
private:
|
||||
/** the @a DeviceListener, or NULL. */
|
||||
DeviceListener* m_listener;
|
||||
};
|
||||
|
||||
/**
|
||||
* The @a Device for directly connected serial interfaces (tty).
|
||||
*/
|
||||
class SerialDevice : public Device {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @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)
|
||||
: Device(name, checkDevice, readOnly, initialSend) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @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)
|
||||
: Device(name, checkDevice, readOnly, initialSend) {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
|
||||
// @copydoc
|
||||
virtual void close();
|
||||
// @copydoc
|
||||
virtual void close();
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
|
||||
|
||||
private:
|
||||
/** the previous settings of the device for restoring. */
|
||||
termios m_oldSettings;
|
||||
private:
|
||||
/** the previous settings of the device for restoring. */
|
||||
termios m_oldSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* The @a Device for remote network interfaces.
|
||||
*/
|
||||
class NetworkDevice : public Device {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
* @param udp true for UDP, false to TCP.
|
||||
*/
|
||||
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
|
||||
const bool udp)
|
||||
: Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp),
|
||||
m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
* @param udp true for UDP, false to TCP.
|
||||
*/
|
||||
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
|
||||
const bool udp)
|
||||
: Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp),
|
||||
m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
|
||||
|
||||
// @copydoc
|
||||
virtual unsigned int getLatency() const { return 10000; }
|
||||
// @copydoc
|
||||
virtual unsigned int getLatency() const { return 10000; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
|
||||
// @copydoc
|
||||
virtual bool available();
|
||||
// @copydoc
|
||||
virtual bool available();
|
||||
|
||||
// @copydoc
|
||||
virtual ssize_t write(const unsigned char value);
|
||||
// @copydoc
|
||||
virtual ssize_t write(const unsigned char value);
|
||||
|
||||
// @copydoc
|
||||
virtual ssize_t read(unsigned char& value);
|
||||
// @copydoc
|
||||
virtual ssize_t read(unsigned char& value);
|
||||
|
||||
|
||||
private:
|
||||
/** the socket address of the device. */
|
||||
const struct sockaddr_in m_address;
|
||||
private:
|
||||
/** the socket address of the device. */
|
||||
const struct sockaddr_in m_address;
|
||||
|
||||
/** true for UDP, false to TCP. */
|
||||
const bool m_udp;
|
||||
/** true for UDP, false to TCP. */
|
||||
const bool m_udp;
|
||||
|
||||
/** the buffer memory, or NULL. */
|
||||
unsigned char* m_buffer;
|
||||
/** the buffer memory, or NULL. */
|
||||
unsigned char* m_buffer;
|
||||
|
||||
/** the buffer size. */
|
||||
unsigned char m_bufSize;
|
||||
/** the buffer size. */
|
||||
unsigned char m_bufSize;
|
||||
|
||||
/** the buffer fill length. */
|
||||
unsigned char m_bufLen;
|
||||
/** the buffer fill length. */
|
||||
unsigned char m_bufLen;
|
||||
|
||||
/** the buffer read position. */
|
||||
unsigned char m_bufPos;
|
||||
/** the buffer read position. */
|
||||
unsigned char m_bufPos;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+298
-298
@@ -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.
|
||||
*/
|
||||
class FileReader {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
*/
|
||||
explicit FileReader(bool supportsDefaults)
|
||||
: m_supportsDefaults(supportsDefaults) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
*/
|
||||
explicit FileReader(bool supportsDefaults)
|
||||
: m_supportsDefaults(supportsDefaults) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~FileReader() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~FileReader() {}
|
||||
|
||||
/**
|
||||
* Read the definitions from a file.
|
||||
* @param filename the name of the file being read.
|
||||
* @param verbose whether to verbosely log problems.
|
||||
* @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 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.
|
||||
*/
|
||||
virtual result_t readFromFile(const string filename, bool verbose = false,
|
||||
string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") {
|
||||
ifstream ifs;
|
||||
ifs.open(filename.c_str(), ifstream::in);
|
||||
if (!ifs.is_open()) {
|
||||
m_lastError = filename;
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
size_t lastSep = filename.find_last_of('/');
|
||||
if (lastSep != string::npos) { // potential destination address, matches "^ZZ."
|
||||
// extract defaultDest, defaultCircuit, defaultSuffix from filename:
|
||||
// ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv
|
||||
unsigned char checkDest;
|
||||
string checkIdent, useCircuit, useSuffix;
|
||||
unsigned int checkSw, checkHw;
|
||||
if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) {
|
||||
defaultDest = filename.substr(lastSep+1, 2);
|
||||
if (!useCircuit.empty()) {
|
||||
defaultCircuit = useCircuit;
|
||||
}
|
||||
if (!useSuffix.empty()) {
|
||||
defaultSuffix = useSuffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
vector< vector<string> > defaults;
|
||||
while (splitFields(ifs, row, lineNo)) {
|
||||
if (row.empty()) {
|
||||
continue;
|
||||
}
|
||||
result_t result;
|
||||
vector<string>::iterator it = row.begin();
|
||||
const vector<string>::iterator end = row.end();
|
||||
if (m_supportsDefaults) {
|
||||
if (row[0][0] == '*') {
|
||||
row[0] = row[0].substr(1);
|
||||
result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
if (result == RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
if (!verbose) {
|
||||
ifs.close();
|
||||
ostringstream error;
|
||||
error << filename << ":" << static_cast<unsigned>(lineNo);
|
||||
if (m_lastError.length() > 0) {
|
||||
error << ": " << m_lastError;
|
||||
}
|
||||
m_lastError = error.str();
|
||||
return result;
|
||||
}
|
||||
if (m_lastError.length() > 0) {
|
||||
cout << m_lastError << endl;
|
||||
}
|
||||
printErrorPos(cout, row.begin(), end, it, filename, lineNo, result);
|
||||
} else if (!verbose) {
|
||||
m_lastError = "";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read the definitions from a file.
|
||||
* @param filename the name of the file being read.
|
||||
* @param verbose whether to verbosely log problems.
|
||||
* @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 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.
|
||||
*/
|
||||
virtual result_t readFromFile(const string filename, bool verbose = false,
|
||||
string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") {
|
||||
ifstream ifs;
|
||||
ifs.open(filename.c_str(), ifstream::in);
|
||||
if (!ifs.is_open()) {
|
||||
m_lastError = filename;
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
size_t lastSep = filename.find_last_of('/');
|
||||
if (lastSep != string::npos) { // potential destination address, matches "^ZZ."
|
||||
// extract defaultDest, defaultCircuit, defaultSuffix from filename:
|
||||
// ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv
|
||||
unsigned char checkDest;
|
||||
string checkIdent, useCircuit, useSuffix;
|
||||
unsigned int checkSw, checkHw;
|
||||
if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) {
|
||||
defaultDest = filename.substr(lastSep+1, 2);
|
||||
if (!useCircuit.empty()) {
|
||||
defaultCircuit = useCircuit;
|
||||
}
|
||||
if (!useSuffix.empty()) {
|
||||
defaultSuffix = useSuffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
vector< vector<string> > defaults;
|
||||
while (splitFields(ifs, row, lineNo)) {
|
||||
if (row.empty()) {
|
||||
continue;
|
||||
}
|
||||
result_t result;
|
||||
vector<string>::iterator it = row.begin();
|
||||
const vector<string>::iterator end = row.end();
|
||||
if (m_supportsDefaults) {
|
||||
if (row[0][0] == '*') {
|
||||
row[0] = row[0].substr(1);
|
||||
result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
if (result == RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
if (!verbose) {
|
||||
ifs.close();
|
||||
ostringstream error;
|
||||
error << filename << ":" << static_cast<unsigned>(lineNo);
|
||||
if (m_lastError.length() > 0) {
|
||||
error << ": " << m_lastError;
|
||||
}
|
||||
m_lastError = error.str();
|
||||
return result;
|
||||
}
|
||||
if (m_lastError.length() > 0) {
|
||||
cout << m_lastError << endl;
|
||||
}
|
||||
printErrorPos(cout, row.begin(), end, it, filename, lineNo, result);
|
||||
} else if (!verbose) {
|
||||
m_lastError = "";
|
||||
}
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
return RESULT_OK;
|
||||
}
|
||||
ifs.close();
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a @a string describing the last error position.
|
||||
* @return a @a string describing the last error position.
|
||||
*/
|
||||
virtual string getLastError() { return m_lastError; }
|
||||
/**
|
||||
* Return a @a string describing the last error position.
|
||||
* @return a @a string describing the last error position.
|
||||
*/
|
||||
virtual string getLastError() { return m_lastError; }
|
||||
|
||||
/**
|
||||
* Add a default row that was read from a file.
|
||||
* @param defaults the list to add the default row to.
|
||||
* @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 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 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 lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
|
||||
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) {
|
||||
defaults.push_back(row);
|
||||
begin = row.end();
|
||||
return RESULT_OK;
|
||||
}
|
||||
/**
|
||||
* Add a default row that was read from a file.
|
||||
* @param defaults the list to add the default row to.
|
||||
* @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 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 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 lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
|
||||
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) {
|
||||
defaults.push_back(row);
|
||||
begin = row.end();
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a definition that was read from a file.
|
||||
* @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 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 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 filename the name of 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.
|
||||
*/
|
||||
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,
|
||||
const string& filename, unsigned int lineNo) = 0;
|
||||
/**
|
||||
* Add a definition that was read from a file.
|
||||
* @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 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 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 filename the name of 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.
|
||||
*/
|
||||
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,
|
||||
const string& filename, unsigned int lineNo) = 0;
|
||||
|
||||
/**
|
||||
* Left and right trim the string.
|
||||
* @param str the @a string to trim.
|
||||
*/
|
||||
static void trim(string& str) {
|
||||
size_t pos = str.find_first_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(0, pos);
|
||||
}
|
||||
pos = str.find_last_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(pos+1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Left and right trim the string.
|
||||
* @param str the @a string to trim.
|
||||
*/
|
||||
static void trim(string& str) {
|
||||
size_t pos = str.find_first_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(0, pos);
|
||||
}
|
||||
pos = str.find_last_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all upper case characters in the string to lower case.
|
||||
* @param str the @a string to convert.
|
||||
*/
|
||||
static void tolower(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
}
|
||||
/**
|
||||
* Convert all upper case characters in the string to lower case.
|
||||
* @param str the @a string to convert.
|
||||
*/
|
||||
static void tolower(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the next line(s) from the @a istring into fields.
|
||||
* @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 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.
|
||||
*/
|
||||
static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) {
|
||||
row.clear();
|
||||
string line;
|
||||
bool quotedText = false, wasQuoted = false;
|
||||
ostringstream field;
|
||||
char prev = FIELD_SEPARATOR;
|
||||
bool empty = true, read = false;
|
||||
while (getline(ifs, line)) {
|
||||
read = true;
|
||||
lineNo++;
|
||||
trim(line);
|
||||
/**
|
||||
* Split the next line(s) from the @a istring into fields.
|
||||
* @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 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.
|
||||
*/
|
||||
static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) {
|
||||
row.clear();
|
||||
string line;
|
||||
bool quotedText = false, wasQuoted = false;
|
||||
ostringstream field;
|
||||
char prev = FIELD_SEPARATOR;
|
||||
bool empty = true, read = false;
|
||||
while (getline(ifs, line)) {
|
||||
read = true;
|
||||
lineNo++;
|
||||
trim(line);
|
||||
|
||||
size_t length = line.length();
|
||||
if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) {
|
||||
continue; // skip empty lines and comments
|
||||
}
|
||||
for (size_t pos = 0; pos < length; pos++) {
|
||||
char ch = line[pos];
|
||||
switch (ch) {
|
||||
case FIELD_SEPARATOR:
|
||||
if (quotedText) {
|
||||
field << ch;
|
||||
} else {
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
empty &= str.empty();
|
||||
row.push_back(str);
|
||||
field.str("");
|
||||
wasQuoted = false;
|
||||
}
|
||||
break;
|
||||
case TEXT_SEPARATOR:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote
|
||||
field << ch;
|
||||
quotedText = true;
|
||||
} else if (quotedText) {
|
||||
quotedText = false;
|
||||
} else if (prev == FIELD_SEPARATOR) {
|
||||
quotedText = wasQuoted = true;
|
||||
} else {
|
||||
field << ch;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) {
|
||||
field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text
|
||||
quotedText = true;
|
||||
} else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) {
|
||||
field << VALUE_SEPARATOR;
|
||||
}
|
||||
field << ch;
|
||||
break;
|
||||
}
|
||||
prev = ch;
|
||||
}
|
||||
if (!quotedText) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
if (empty && str.empty()) {
|
||||
row.clear();
|
||||
return read;
|
||||
}
|
||||
row.push_back(str);
|
||||
return true;
|
||||
}
|
||||
size_t length = line.length();
|
||||
if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) {
|
||||
continue; // skip empty lines and comments
|
||||
}
|
||||
for (size_t pos = 0; pos < length; pos++) {
|
||||
char ch = line[pos];
|
||||
switch (ch) {
|
||||
case FIELD_SEPARATOR:
|
||||
if (quotedText) {
|
||||
field << ch;
|
||||
} else {
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
empty &= str.empty();
|
||||
row.push_back(str);
|
||||
field.str("");
|
||||
wasQuoted = false;
|
||||
}
|
||||
break;
|
||||
case TEXT_SEPARATOR:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote
|
||||
field << ch;
|
||||
quotedText = true;
|
||||
} else if (quotedText) {
|
||||
quotedText = false;
|
||||
} else if (prev == FIELD_SEPARATOR) {
|
||||
quotedText = wasQuoted = true;
|
||||
} else {
|
||||
field << ch;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) {
|
||||
field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text
|
||||
quotedText = true;
|
||||
} else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) {
|
||||
field << VALUE_SEPARATOR;
|
||||
}
|
||||
field << ch;
|
||||
break;
|
||||
}
|
||||
prev = ch;
|
||||
}
|
||||
if (!quotedText) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
if (empty && str.empty()) {
|
||||
row.clear();
|
||||
return read;
|
||||
}
|
||||
row.push_back(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 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 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).
|
||||
* @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,
|
||||
string& suffix, unsigned int& software, unsigned int& hardware) {
|
||||
ident = circuit = suffix = "";
|
||||
software = hardware = UINT_MAX;
|
||||
if (name.length() > 4 && name.substr(name.length()-4) == ".csv") {
|
||||
name = name.substr(0, name.length()-3); // including trailing "."
|
||||
}
|
||||
size_t pos = name.find('.');
|
||||
if (pos != 2) {
|
||||
return false; // missing "ZZ."
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL);
|
||||
if (result != RESULT_OK || !isValidAddress(dest)) {
|
||||
return false; // invalid "ZZ"
|
||||
}
|
||||
name.erase(0, pos);
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".SW"); // check for ".SWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "SWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".HW"); // check for ".HWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "HWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
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 ".."
|
||||
ident = circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".CIRCUIT."
|
||||
if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) {
|
||||
circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".SUFFIX."
|
||||
}
|
||||
if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') {
|
||||
suffix = name.substr(0, 2);
|
||||
name.erase(0, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 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 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 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 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).
|
||||
* @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,
|
||||
string& suffix, unsigned int& software, unsigned int& hardware) {
|
||||
ident = circuit = suffix = "";
|
||||
software = hardware = UINT_MAX;
|
||||
if (name.length() > 4 && name.substr(name.length()-4) == ".csv") {
|
||||
name = name.substr(0, name.length()-3); // including trailing "."
|
||||
}
|
||||
size_t pos = name.find('.');
|
||||
if (pos != 2) {
|
||||
return false; // missing "ZZ."
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL);
|
||||
if (result != RESULT_OK || !isValidAddress(dest)) {
|
||||
return false; // invalid "ZZ"
|
||||
}
|
||||
name.erase(0, pos);
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".SW"); // check for ".SWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "SWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".HW"); // check for ".HWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "HWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
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 ".."
|
||||
ident = circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".CIRCUIT."
|
||||
if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) {
|
||||
circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".SUFFIX."
|
||||
}
|
||||
if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') {
|
||||
suffix = name.substr(0, 2);
|
||||
name.erase(0, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/** whether this instance supports rows with defaults (starting with a star). */
|
||||
bool m_supportsDefaults;
|
||||
private:
|
||||
/** whether this instance supports rows with defaults (starting with a star). */
|
||||
bool m_supportsDefaults;
|
||||
|
||||
|
||||
protected:
|
||||
/** a @a string describing the last error position. */
|
||||
string m_lastError;
|
||||
protected:
|
||||
/** a @a string describing the last error position. */
|
||||
string m_lastError;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+1871
-1871
File diff suppressed because it is too large
Load Diff
+1080
-1080
File diff suppressed because it is too large
Load Diff
+33
-33
@@ -22,40 +22,40 @@
|
||||
namespace ebusd {
|
||||
|
||||
const char* getResultCode(result_t resultCode) {
|
||||
switch (resultCode) {
|
||||
case RESULT_OK: return "done";
|
||||
case RESULT_CONTINUE: return "continue";
|
||||
case RESULT_EMPTY: return "empty";
|
||||
case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error";
|
||||
case RESULT_ERR_DEVICE: return "ERR: generic device error";
|
||||
case RESULT_ERR_SEND: return "ERR: send error";
|
||||
case RESULT_ERR_ESC: return "ERR: invalid escape sequence";
|
||||
case RESULT_ERR_TIMEOUT: return "ERR: read timeout";
|
||||
case RESULT_ERR_NOTFOUND: return "ERR: element not found";
|
||||
case RESULT_ERR_EOF: return "ERR: end of input reached";
|
||||
case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument";
|
||||
case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument";
|
||||
case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address";
|
||||
case RESULT_ERR_INVALID_POS: return "ERR: invalid position";
|
||||
case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range";
|
||||
case RESULT_ERR_INVALID_PART: return "ERR: invalid part type";
|
||||
case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type";
|
||||
case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list";
|
||||
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
|
||||
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
|
||||
case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost";
|
||||
case RESULT_ERR_CRC: return "ERR: CRC error";
|
||||
case RESULT_ERR_ACK: return "ERR: ACK error";
|
||||
case RESULT_ERR_NAK: return "ERR: NAK received";
|
||||
case RESULT_ERR_NO_SIGNAL: return "ERR: no signal";
|
||||
case RESULT_ERR_SYN: return "ERR: SYN received";
|
||||
switch (resultCode) {
|
||||
case RESULT_OK: return "done";
|
||||
case RESULT_CONTINUE: return "continue";
|
||||
case RESULT_EMPTY: return "empty";
|
||||
case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error";
|
||||
case RESULT_ERR_DEVICE: return "ERR: generic device error";
|
||||
case RESULT_ERR_SEND: return "ERR: send error";
|
||||
case RESULT_ERR_ESC: return "ERR: invalid escape sequence";
|
||||
case RESULT_ERR_TIMEOUT: return "ERR: read timeout";
|
||||
case RESULT_ERR_NOTFOUND: return "ERR: element not found";
|
||||
case RESULT_ERR_EOF: return "ERR: end of input reached";
|
||||
case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument";
|
||||
case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument";
|
||||
case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address";
|
||||
case RESULT_ERR_INVALID_POS: return "ERR: invalid position";
|
||||
case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range";
|
||||
case RESULT_ERR_INVALID_PART: return "ERR: invalid part type";
|
||||
case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type";
|
||||
case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list";
|
||||
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
|
||||
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
|
||||
case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost";
|
||||
case RESULT_ERR_CRC: return "ERR: CRC error";
|
||||
case RESULT_ERR_ACK: return "ERR: ACK error";
|
||||
case RESULT_ERR_NAK: return "ERR: NAK received";
|
||||
case RESULT_ERR_NO_SIGNAL: return "ERR: no signal";
|
||||
case RESULT_ERR_SYN: return "ERR: SYN received";
|
||||
|
||||
default:
|
||||
if (resultCode >= 0) {
|
||||
return "done: unknown result code";
|
||||
}
|
||||
return "ERR: unknown result code";
|
||||
}
|
||||
default:
|
||||
if (resultCode >= 0) {
|
||||
return "done: unknown result code";
|
||||
}
|
||||
return "ERR: unknown result code";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+26
-26
@@ -31,37 +31,37 @@ namespace ebusd {
|
||||
|
||||
/** type for result code. */
|
||||
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_EMPTY = 2, //!< empty result
|
||||
RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received)
|
||||
RESULT_EMPTY = 2, //!< empty result
|
||||
|
||||
RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal)
|
||||
RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal)
|
||||
RESULT_ERR_SEND = -3, //!< send error
|
||||
RESULT_ERR_ESC = -4, //!< invalid escape sequence
|
||||
RESULT_ERR_TIMEOUT = -5, //!< read timeout
|
||||
RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal)
|
||||
RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal)
|
||||
RESULT_ERR_SEND = -3, //!< send error
|
||||
RESULT_ERR_ESC = -4, //!< invalid escape sequence
|
||||
RESULT_ERR_TIMEOUT = -5, //!< read timeout
|
||||
|
||||
RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable
|
||||
RESULT_ERR_EOF = -7, //!< end of input reached
|
||||
RESULT_ERR_INVALID_ARG = -8, //!< invalid argument
|
||||
RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument
|
||||
RESULT_ERR_INVALID_ADDR = -10, //!< invalid address
|
||||
RESULT_ERR_INVALID_POS = -11, //!< invalid position
|
||||
RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range
|
||||
RESULT_ERR_INVALID_PART = -13, //!< invalid part type value
|
||||
RESULT_ERR_MISSING_TYPE = -14, //!< missing data type
|
||||
RESULT_ERR_INVALID_LIST = -15, //!< invalid value list
|
||||
RESULT_ERR_DUPLICATE = -16, //!< duplicate entry
|
||||
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
|
||||
RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable
|
||||
RESULT_ERR_EOF = -7, //!< end of input reached
|
||||
RESULT_ERR_INVALID_ARG = -8, //!< invalid argument
|
||||
RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument
|
||||
RESULT_ERR_INVALID_ADDR = -10, //!< invalid address
|
||||
RESULT_ERR_INVALID_POS = -11, //!< invalid position
|
||||
RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range
|
||||
RESULT_ERR_INVALID_PART = -13, //!< invalid part type value
|
||||
RESULT_ERR_MISSING_TYPE = -14, //!< missing data type
|
||||
RESULT_ERR_INVALID_LIST = -15, //!< invalid value list
|
||||
RESULT_ERR_DUPLICATE = -16, //!< duplicate entry
|
||||
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
|
||||
|
||||
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
|
||||
RESULT_ERR_CRC = -19, //!< CRC error
|
||||
RESULT_ERR_ACK = -20, //!< ACK error
|
||||
RESULT_ERR_NAK = -21, //!< NAK received
|
||||
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
|
||||
RESULT_ERR_CRC = -19, //!< CRC error
|
||||
RESULT_ERR_ACK = -20, //!< ACK error
|
||||
RESULT_ERR_NAK = -21, //!< NAK received
|
||||
|
||||
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
|
||||
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
|
||||
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
|
||||
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
|
||||
};
|
||||
|
||||
|
||||
|
||||
+178
-178
@@ -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.
|
||||
*/
|
||||
static const unsigned char CRC_LOOKUP_TABLE[] = {
|
||||
0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
|
||||
0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
|
||||
0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
|
||||
0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
|
||||
0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
|
||||
0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
|
||||
0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
|
||||
0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
|
||||
0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
|
||||
0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
|
||||
0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
|
||||
0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
|
||||
0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
|
||||
0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
|
||||
0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
|
||||
0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
|
||||
0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
|
||||
0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
|
||||
0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
|
||||
0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
|
||||
0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
|
||||
0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
|
||||
0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
|
||||
0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
|
||||
0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
|
||||
0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
|
||||
0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
|
||||
0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
|
||||
0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
|
||||
0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
|
||||
0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
|
||||
0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
|
||||
};
|
||||
|
||||
|
||||
void SymbolString::addAll(const SymbolString& str, bool skipLastSymbol) {
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
bool isEscaped = str.m_unescapeState == 0;
|
||||
vector<unsigned char> data = str.m_data;
|
||||
size_t end = data.size();
|
||||
if (end > 0 && skipLastSymbol) {
|
||||
end--;
|
||||
}
|
||||
for (size_t i = 0; i < end; i++) {
|
||||
push_back(data[i], isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
bool isEscaped = str.m_unescapeState == 0;
|
||||
vector<unsigned char> data = str.m_data;
|
||||
size_t end = data.size();
|
||||
if (end > 0 && skipLastSymbol) {
|
||||
end--;
|
||||
}
|
||||
for (size_t i = 0; i < end; i++) {
|
||||
push_back(data[i], isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
}
|
||||
|
||||
result_t SymbolString::parseHex(const string& str, const bool isEscaped) {
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
for (size_t i = 0; i < str.size(); i += 2) {
|
||||
char* strEnd = NULL;
|
||||
const char* strBegin = str.substr(i, 2).c_str();
|
||||
unsigned long value = strtoul(strBegin, &strEnd, 16);
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
for (size_t i = 0; i < str.size(); i += 2) {
|
||||
char* strEnd = NULL;
|
||||
const char* strBegin = str.substr(i, 2).c_str();
|
||||
unsigned long value = strtoul(strBegin, &strEnd, 16);
|
||||
|
||||
if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
push_back((unsigned char)value, isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
push_back((unsigned char)value, isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
const string SymbolString::getDataStr(const bool unescape, const bool skipLastSymbol) {
|
||||
ostringstream sstr;
|
||||
bool previousEscape = false;
|
||||
ostringstream sstr;
|
||||
bool previousEscape = false;
|
||||
|
||||
for (size_t i = 0; i < m_data.size(); i++) {
|
||||
unsigned char value = m_data[i];
|
||||
if (m_unescapeState == 0 && unescape && previousEscape) {
|
||||
if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
if (value == 0x00) {
|
||||
sstr << "a9"; // ESC
|
||||
} else if (value == 0x01) {
|
||||
sstr << "aa"; // SYN
|
||||
} else {
|
||||
sstr << "XX"; // invalid escape sequence
|
||||
}
|
||||
}
|
||||
previousEscape = false;
|
||||
} else if (m_unescapeState == 0 && unescape && value == ESC) {
|
||||
previousEscape = true; // escape sequence not yet finished
|
||||
} else if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
sstr << nouppercase << setw(2) << hex
|
||||
<< setfill('0') << static_cast<unsigned>(value);
|
||||
}
|
||||
}
|
||||
return sstr.str();
|
||||
for (size_t i = 0; i < m_data.size(); i++) {
|
||||
unsigned char value = m_data[i];
|
||||
if (m_unescapeState == 0 && unescape && previousEscape) {
|
||||
if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
if (value == 0x00) {
|
||||
sstr << "a9"; // ESC
|
||||
} else if (value == 0x01) {
|
||||
sstr << "aa"; // SYN
|
||||
} else {
|
||||
sstr << "XX"; // invalid escape sequence
|
||||
}
|
||||
}
|
||||
previousEscape = false;
|
||||
} else if (m_unescapeState == 0 && unescape && value == ESC) {
|
||||
previousEscape = true; // escape sequence not yet finished
|
||||
} else if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
sstr << nouppercase << setw(2) << hex
|
||||
<< setfill('0') << static_cast<unsigned>(value);
|
||||
}
|
||||
}
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
result_t SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) {
|
||||
if (m_unescapeState == 0) { // store escaped data
|
||||
if (!isEscaped && value == ESC) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x00);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
}
|
||||
} else if (!isEscaped && value == SYN) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x01);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
}
|
||||
} else {
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (!isEscaped) {
|
||||
if (m_unescapeState != 1) {
|
||||
return RESULT_ERR_ESC; // invalid unescape state
|
||||
}
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
if (value == ESC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
} else if (value == SYN) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
} else {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (m_unescapeState != 1) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
if (value == 0x00) {
|
||||
m_data.push_back(ESC);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (value == 0x01) {
|
||||
m_data.push_back(SYN);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_ESC; // invalid escape sequence
|
||||
}
|
||||
if (value == ESC) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_unescapeState = 2;
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_data.push_back(value);
|
||||
return RESULT_OK;
|
||||
if (m_unescapeState == 0) { // store escaped data
|
||||
if (!isEscaped && value == ESC) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x00);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
}
|
||||
} else if (!isEscaped && value == SYN) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x01);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
}
|
||||
} else {
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (!isEscaped) {
|
||||
if (m_unescapeState != 1) {
|
||||
return RESULT_ERR_ESC; // invalid unescape state
|
||||
}
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
if (value == ESC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
} else if (value == SYN) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
} else {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (m_unescapeState != 1) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
if (value == 0x00) {
|
||||
m_data.push_back(ESC);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (value == 0x01) {
|
||||
m_data.push_back(SYN);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_ESC; // invalid escape sequence
|
||||
}
|
||||
if (value == ESC) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_unescapeState = 2;
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_data.push_back(value);
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
unsigned char getMasterPartIndex(unsigned char bits) {
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return 1;
|
||||
case 0x1:
|
||||
return 2;
|
||||
case 0x3:
|
||||
return 3;
|
||||
case 0x7:
|
||||
return 4;
|
||||
case 0xF:
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return 1;
|
||||
case 0x1:
|
||||
return 2;
|
||||
case 0x3:
|
||||
return 3;
|
||||
case 0x7:
|
||||
return 4;
|
||||
case 0xF:
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMaster(unsigned char addr) {
|
||||
return getMasterPartIndex(addr & 0x0F) > 0
|
||||
&& getMasterPartIndex((addr & 0xF0)>>4) > 0;
|
||||
return getMasterPartIndex(addr & 0x0F) > 0
|
||||
&& getMasterPartIndex((addr & 0xF0)>>4) > 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (isMaster(addr)) {
|
||||
return (unsigned char)(addr+5);
|
||||
}
|
||||
if (isValidAddress(addr, false)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
if (isMaster(addr)) {
|
||||
return (unsigned char)(addr+5);
|
||||
}
|
||||
if (isValidAddress(addr, false)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
}
|
||||
|
||||
unsigned char getMasterAddress(unsigned char addr) {
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
addr = (unsigned char)(addr+256-5);
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
addr = (unsigned char)(addr+256-5);
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
}
|
||||
|
||||
unsigned char getMasterNumber(unsigned char addr) {
|
||||
unsigned char priority = getMasterPartIndex(addr & 0x0F);
|
||||
if (priority == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4);
|
||||
if (index == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (unsigned char)(5*(priority-1) + index);
|
||||
unsigned char priority = getMasterPartIndex(addr & 0x0F);
|
||||
if (priority == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4);
|
||||
if (index == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (unsigned char)(5*(priority-1) + index);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
+117
-117
@@ -79,141 +79,141 @@ static const unsigned char BROADCAST = 0xFE; //!< the broadcast destination addr
|
||||
* A string of escaped or unescaped bus symbols.
|
||||
*/
|
||||
class SymbolString {
|
||||
public:
|
||||
/**
|
||||
* Creates a new empty escaped or unescaped instance.
|
||||
* @param escaped whether to create an escaped instance.
|
||||
*/
|
||||
explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {}
|
||||
public:
|
||||
/**
|
||||
* Creates a new empty escaped or unescaped instance.
|
||||
* @param escaped whether to create an escaped instance.
|
||||
*/
|
||||
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.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
*/
|
||||
void addAll(const SymbolString& str, const bool skipLastSymbol = false);
|
||||
/**
|
||||
* Add all symbols from the other @a SymbolString and the calculated CRC if escaped.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
*/
|
||||
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.
|
||||
* @param str the hex @a string.
|
||||
* @param isEscaped whether the hex string is escaped.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
result_t parseHex(const string& str, const bool isEscaped = false);
|
||||
/**
|
||||
* 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 isEscaped whether the hex string is escaped.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
result_t parseHex(const string& str, const bool isEscaped = false);
|
||||
|
||||
/**
|
||||
* Return the symbols as hex string.
|
||||
* @param unescape whether to unescape an escaped instance.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
* @return the symbols as hex string.
|
||||
*/
|
||||
const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true);
|
||||
/**
|
||||
* Return the symbols as hex string.
|
||||
* @param unescape whether to unescape an escaped instance.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
* @return the symbols as hex string.
|
||||
*/
|
||||
const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true);
|
||||
|
||||
/**
|
||||
* Return a reference to the symbol at the specified index.
|
||||
* @param index the index of the symbol to return.
|
||||
* @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]; }
|
||||
/**
|
||||
* Return a reference to the symbol at the specified index.
|
||||
* @param index the index of the symbol to return.
|
||||
* @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]; }
|
||||
|
||||
/**
|
||||
* Return whether this instance is equal to the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols).
|
||||
*/
|
||||
bool operator == (SymbolString& other) { return m_unescapeState == other.m_unescapeState && m_data == other.m_data; }
|
||||
/**
|
||||
* Return whether this instance is equal to the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols).
|
||||
*/
|
||||
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.
|
||||
* @param other 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; }
|
||||
/**
|
||||
* Return whether this instance is different from the other instance.
|
||||
* @param other 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; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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,
|
||||
* 2 if this instance only differs from the other instance in the first byte (the master address).
|
||||
*/
|
||||
int compareMaster(SymbolString& other) {
|
||||
if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) {
|
||||
return 1;
|
||||
}
|
||||
if (m_data == other.m_data) {
|
||||
return 0;
|
||||
}
|
||||
if (m_data.size() == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* @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,
|
||||
* 2 if this instance only differs from the other instance in the first byte (the master address).
|
||||
*/
|
||||
int compareMaster(SymbolString& other) {
|
||||
if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) {
|
||||
return 1;
|
||||
}
|
||||
if (m_data == other.m_data) {
|
||||
return 0;
|
||||
}
|
||||
if (m_data.size() == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary.
|
||||
* @param value the symbol to append.
|
||||
* @param isEscaped whether the symbol is escaped.
|
||||
* @param updateCRC whether to update the calculated CRC in @a m_crc.
|
||||
* @return RESULT_OK if another symbol was appended,
|
||||
* RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received,
|
||||
* RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected.
|
||||
*/
|
||||
result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true);
|
||||
/**
|
||||
* Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary.
|
||||
* @param value the symbol to append.
|
||||
* @param isEscaped whether the symbol is escaped.
|
||||
* @param updateCRC whether to update the calculated CRC in @a m_crc.
|
||||
* @return RESULT_OK if another symbol was appended,
|
||||
* RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received,
|
||||
* RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected.
|
||||
*/
|
||||
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 available symbols.
|
||||
*/
|
||||
unsigned char size() const { return (unsigned char)m_data.size(); }
|
||||
/**
|
||||
* Return the number of symbols in this symbol string.
|
||||
* @return the number of available symbols.
|
||||
*/
|
||||
unsigned char size() const { return (unsigned char)m_data.size(); }
|
||||
|
||||
/**
|
||||
* Return the calculated CRC.
|
||||
* @return the calculated CRC.
|
||||
*/
|
||||
unsigned char getCRC() const { return m_crc; }
|
||||
/**
|
||||
* Return the calculated CRC.
|
||||
* @return the calculated CRC.
|
||||
*/
|
||||
unsigned char getCRC() const { return m_crc; }
|
||||
|
||||
/**
|
||||
* Clear the symbols.
|
||||
*/
|
||||
void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; }
|
||||
/**
|
||||
* Clear the symbols.
|
||||
*/
|
||||
void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; }
|
||||
|
||||
/**
|
||||
* Clear the symbols and adjust the escape mode.
|
||||
* @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; }
|
||||
/**
|
||||
* Clear the symbols and adjust the escape mode.
|
||||
* @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; }
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
*/
|
||||
SymbolString(const SymbolString& str)
|
||||
: m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {}
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
*/
|
||||
SymbolString(const SymbolString& str)
|
||||
: 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.
|
||||
* @param value the (escaped) value to add to the calculated CRC in @a m_crc.
|
||||
*/
|
||||
void addCRC(const unsigned char 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.
|
||||
*/
|
||||
void addCRC(const unsigned char value);
|
||||
|
||||
/** the string of bus symbols. */
|
||||
vector<unsigned char> m_data;
|
||||
/** the string of bus symbols. */
|
||||
vector<unsigned char> m_data;
|
||||
|
||||
/**
|
||||
* 0 if the symbols in @a m_data are escaped,
|
||||
* 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol,
|
||||
* 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol.
|
||||
*/
|
||||
int m_unescapeState;
|
||||
/**
|
||||
* 0 if the symbols in @a m_data are escaped,
|
||||
* 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol,
|
||||
* 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol.
|
||||
*/
|
||||
int m_unescapeState;
|
||||
|
||||
/** the calculated CRC. */
|
||||
unsigned char m_crc;
|
||||
/** the calculated CRC. */
|
||||
unsigned char m_crc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+581
-581
File diff suppressed because it is too large
Load Diff
@@ -24,39 +24,39 @@ using namespace std;
|
||||
using namespace ebusd;
|
||||
|
||||
int main() {
|
||||
Device* device = Device::create("/dev/ttyUSB20", true, false, false);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device" << endl;
|
||||
return -1;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != RESULT_OK) {
|
||||
cout << "open failed: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
if (!device->isValid()) {
|
||||
cout << "device not available." << endl;
|
||||
}
|
||||
int count = 0;
|
||||
Device* device = Device::create("/dev/ttyUSB20", true, false, false);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device" << endl;
|
||||
return -1;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != RESULT_OK) {
|
||||
cout << "open failed: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
if (!device->isValid()) {
|
||||
cout << "device not available." << endl;
|
||||
}
|
||||
int count = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned char byte = 0;
|
||||
result = device->recv(0, byte);
|
||||
while (1) {
|
||||
unsigned char byte = 0;
|
||||
result = device->recv(0, byte);
|
||||
|
||||
if (result == RESULT_OK) {
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (result == RESULT_OK) {
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
device->close();
|
||||
device->close();
|
||||
|
||||
if (!device->isValid()) {
|
||||
cout << "close successful." << endl;
|
||||
}
|
||||
}
|
||||
if (!device->isValid()) {
|
||||
cout << "close successful." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
delete device;
|
||||
delete device;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,70 +28,70 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
istringstream ifs(
|
||||
"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 4 col 1,\"line 4 col 2 part 1\n"
|
||||
"line 4 col 2 part 2\",line 4 col 3\n"
|
||||
",,,\n"
|
||||
"line 6 col 1,,line 6 col 3\n"
|
||||
"line 8 col 1,\"line 8 col 2 part 1;\n"
|
||||
"line 8 col 2 part 2\",line 8 col 3\n"
|
||||
);
|
||||
string resultlines[][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 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 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"},
|
||||
};
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
istringstream ifs(
|
||||
"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 4 col 1,\"line 4 col 2 part 1\n"
|
||||
"line 4 col 2 part 2\",line 4 col 3\n"
|
||||
",,,\n"
|
||||
"line 6 col 1,,line 6 col 3\n"
|
||||
"line 8 col 1,\"line 8 col 2 part 1;\n"
|
||||
"line 8 col 2 part 2\",line 8 col 3\n"
|
||||
);
|
||||
string resultlines[][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 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 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"},
|
||||
};
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
|
||||
while (FileReader::splitFields(ifs, row, lineNo)) {
|
||||
cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl;
|
||||
string resultline[3] = resultlines[lineNo-1];
|
||||
if (row.empty()) {
|
||||
cout << " result empty";
|
||||
if (resultline[0] == "") {
|
||||
cout << ": OK" << endl;
|
||||
} else {
|
||||
cout << ": error" << endl;
|
||||
error = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (vector<string>::iterator it = row.begin(); it != row.end(); it++) {
|
||||
string got = *it;
|
||||
string expect = resultline[distance(row.begin(), it)];
|
||||
ostringstream type;
|
||||
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);
|
||||
}
|
||||
}
|
||||
while (FileReader::splitFields(ifs, row, lineNo)) {
|
||||
cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl;
|
||||
string resultline[3] = resultlines[lineNo-1];
|
||||
if (row.empty()) {
|
||||
cout << " result empty";
|
||||
if (resultline[0] == "") {
|
||||
cout << ": OK" << endl;
|
||||
} else {
|
||||
cout << ": error" << endl;
|
||||
error = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (vector<string>::iterator it = row.begin(); it != row.end(); it++) {
|
||||
string got = *it;
|
||||
string expect = resultline[distance(row.begin(), it)];
|
||||
ostringstream type;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return error ? 1 : 0;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
+377
-377
@@ -27,397 +27,397 @@
|
||||
using namespace ebusd;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
DataFieldTemplates* templates = NULL;
|
||||
|
||||
namespace ebusd {
|
||||
DataFieldTemplates* getTemplates(const string filename) {
|
||||
if (filename == "") { // avoid compiler warning
|
||||
return templates;
|
||||
}
|
||||
return templates;
|
||||
if (filename == "") { // avoid compiler warning
|
||||
return templates;
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
|
||||
// field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// template: name,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values
|
||||
string checks[][5] = {
|
||||
// "message", "decoded", "master", "slave", "flags"
|
||||
{"date,HDA:3,,,Datum", "", "", "", "template"},
|
||||
{"time,VTI,,,", "", "", "", "template"},
|
||||
{"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"},
|
||||
{"temp,D2C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp1,D1C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp2,D2B,,°C,Temperatur", "", "", "", "template"},
|
||||
{"power,UCH,,kW", "", "", "", "template"},
|
||||
{"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"},
|
||||
{"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"},
|
||||
{"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"},
|
||||
{"tempsensor,temp;sensor,,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,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", "\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,,,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,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "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,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", "", "ff15b50900", "00", "m"},
|
||||
{"w,,,,,,b505,2d", "", "", "", "defaults"},
|
||||
{"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;10,b509,0d2800,,,time", "", "", "", "c"},
|
||||
{"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,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"},
|
||||
{"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"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", "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"},
|
||||
{"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"},
|
||||
{"", "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,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,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" },
|
||||
{"r,ehp,,,,08,b509,0d", "", "", "", "defaults" },
|
||||
{"w,ehp,,,,08,b509,0e", "", "", "", "defaults" },
|
||||
{"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" },
|
||||
{"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" },
|
||||
{"[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\"", "=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,,,,,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;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,,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;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,,,,,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;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,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"},
|
||||
};
|
||||
templates = new DataFieldTemplates();
|
||||
MessageMap* messages = new MessageMap();
|
||||
vector< vector<string> > defaultsRows;
|
||||
map<string, Condition*> &conditions = messages->getConditions();
|
||||
Message* message = NULL;
|
||||
vector<Message*> deleteMessages;
|
||||
vector<SymbolString*> mstrs;
|
||||
vector<SymbolString*> sstrs;
|
||||
mstrs.resize(1);
|
||||
sstrs.resize(1);
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
string inputStr = check[1];
|
||||
string flags = check[4];
|
||||
bool isTemplate = flags == "template";
|
||||
bool isCondition = flags == "condition";
|
||||
bool isDefaults = isCondition || flags == "defaults";
|
||||
bool dontMap = flags.find('m') != string::npos;
|
||||
bool onlyMap = flags.find('M') != string::npos;
|
||||
bool failedCreate = 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 decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos;
|
||||
bool withMessageDump = flags.find('N') != string::npos;
|
||||
bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos);
|
||||
bool failedPrepare = flags.find('p') != string::npos;
|
||||
bool failedPrepareMatch = flags.find('P') != string::npos;
|
||||
bool multi = flags.find('*') != string::npos;
|
||||
bool withInput = flags.find('i') != string::npos;
|
||||
result_t result = RESULT_EMPTY;
|
||||
vector<string> entries;
|
||||
istringstream ifs(check[0]);
|
||||
unsigned int lineNo = 0;
|
||||
if (!FileReader::splitFields(ifs, entries, lineNo)) {
|
||||
entries.clear();
|
||||
}
|
||||
if (isTemplate) {
|
||||
// store new template
|
||||
DataField* fields = NULL;
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, false, true, false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": create template OK" << endl;
|
||||
result = templates->add(fields, "", true);
|
||||
if (result == RESULT_OK) {
|
||||
cout << " store template OK" << endl;
|
||||
} else {
|
||||
cout << " store template error: " << getResultCode(result) << endl;
|
||||
delete fields;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isDefaults) {
|
||||
// store defaults or condition
|
||||
vector<string>::iterator it = entries.begin();
|
||||
size_t oldSize = conditions.size();
|
||||
result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": read defaults OK" << endl;
|
||||
if (isCondition) {
|
||||
if (conditions.size() == oldSize) {
|
||||
cout << " create condition error" << endl;
|
||||
} else {
|
||||
result = messages->resolveConditions();
|
||||
if (result != RESULT_OK) {
|
||||
cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl;
|
||||
} else {
|
||||
cout << " resolve conditions OK" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isChain) {
|
||||
size_t pos = 0;
|
||||
string token;
|
||||
istringstream stream(check[2]);
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= mstrs.size()) {
|
||||
mstrs.resize(pos+1);
|
||||
} else if (mstrs[pos] != NULL) {
|
||||
delete mstrs[pos];
|
||||
}
|
||||
mstrs[pos] = new SymbolString(false);
|
||||
result = mstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
pos = 0;
|
||||
stream.str(check[3]);
|
||||
stream.clear();
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= sstrs.size()) {
|
||||
sstrs.resize(pos+1);
|
||||
} else if (sstrs[pos] != NULL) {
|
||||
delete sstrs[pos];
|
||||
}
|
||||
sstrs[pos] = new SymbolString(false);
|
||||
result = sstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (mstrs[0] != NULL) {
|
||||
delete mstrs[0];
|
||||
}
|
||||
mstrs[0] = new SymbolString(false);
|
||||
result = mstrs[0]->parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
if (sstrs[0] != NULL) {
|
||||
delete sstrs[0];
|
||||
}
|
||||
sstrs[0] = new SymbolString(false);
|
||||
result = sstrs[0]->parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
|
||||
// field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// template: name,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values
|
||||
string checks[][5] = {
|
||||
// "message", "decoded", "master", "slave", "flags"
|
||||
{"date,HDA:3,,,Datum", "", "", "", "template"},
|
||||
{"time,VTI,,,", "", "", "", "template"},
|
||||
{"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"},
|
||||
{"temp,D2C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp1,D1C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp2,D2B,,°C,Temperatur", "", "", "", "template"},
|
||||
{"power,UCH,,kW", "", "", "", "template"},
|
||||
{"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"},
|
||||
{"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"},
|
||||
{"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"},
|
||||
{"tempsensor,temp;sensor,,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,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", "\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,,,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,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "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,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", "", "ff15b50900", "00", "m"},
|
||||
{"w,,,,,,b505,2d", "", "", "", "defaults"},
|
||||
{"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;10,b509,0d2800,,,time", "", "", "", "c"},
|
||||
{"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,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"},
|
||||
{"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"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", "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"},
|
||||
{"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"},
|
||||
{"", "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,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,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" },
|
||||
{"r,ehp,,,,08,b509,0d", "", "", "", "defaults" },
|
||||
{"w,ehp,,,,08,b509,0e", "", "", "", "defaults" },
|
||||
{"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" },
|
||||
{"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" },
|
||||
{"[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\"", "=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,,,,,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;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,,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;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,,,,,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;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,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"},
|
||||
};
|
||||
templates = new DataFieldTemplates();
|
||||
MessageMap* messages = new MessageMap();
|
||||
vector< vector<string> > defaultsRows;
|
||||
map<string, Condition*> &conditions = messages->getConditions();
|
||||
Message* message = NULL;
|
||||
vector<Message*> deleteMessages;
|
||||
vector<SymbolString*> mstrs;
|
||||
vector<SymbolString*> sstrs;
|
||||
mstrs.resize(1);
|
||||
sstrs.resize(1);
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
string inputStr = check[1];
|
||||
string flags = check[4];
|
||||
bool isTemplate = flags == "template";
|
||||
bool isCondition = flags == "condition";
|
||||
bool isDefaults = isCondition || flags == "defaults";
|
||||
bool dontMap = flags.find('m') != string::npos;
|
||||
bool onlyMap = flags.find('M') != string::npos;
|
||||
bool failedCreate = 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 decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos;
|
||||
bool withMessageDump = flags.find('N') != string::npos;
|
||||
bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos);
|
||||
bool failedPrepare = flags.find('p') != string::npos;
|
||||
bool failedPrepareMatch = flags.find('P') != string::npos;
|
||||
bool multi = flags.find('*') != string::npos;
|
||||
bool withInput = flags.find('i') != string::npos;
|
||||
result_t result = RESULT_EMPTY;
|
||||
vector<string> entries;
|
||||
istringstream ifs(check[0]);
|
||||
unsigned int lineNo = 0;
|
||||
if (!FileReader::splitFields(ifs, entries, lineNo)) {
|
||||
entries.clear();
|
||||
}
|
||||
if (isTemplate) {
|
||||
// store new template
|
||||
DataField* fields = NULL;
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, false, true, false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": create template OK" << endl;
|
||||
result = templates->add(fields, "", true);
|
||||
if (result == RESULT_OK) {
|
||||
cout << " store template OK" << endl;
|
||||
} else {
|
||||
cout << " store template error: " << getResultCode(result) << endl;
|
||||
delete fields;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isDefaults) {
|
||||
// store defaults or condition
|
||||
vector<string>::iterator it = entries.begin();
|
||||
size_t oldSize = conditions.size();
|
||||
result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": read defaults OK" << endl;
|
||||
if (isCondition) {
|
||||
if (conditions.size() == oldSize) {
|
||||
cout << " create condition error" << endl;
|
||||
} else {
|
||||
result = messages->resolveConditions();
|
||||
if (result != RESULT_OK) {
|
||||
cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl;
|
||||
} else {
|
||||
cout << " resolve conditions OK" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isChain) {
|
||||
size_t pos = 0;
|
||||
string token;
|
||||
istringstream stream(check[2]);
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= mstrs.size()) {
|
||||
mstrs.resize(pos+1);
|
||||
} else if (mstrs[pos] != NULL) {
|
||||
delete mstrs[pos];
|
||||
}
|
||||
mstrs[pos] = new SymbolString(false);
|
||||
result = mstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
pos = 0;
|
||||
stream.str(check[3]);
|
||||
stream.clear();
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= sstrs.size()) {
|
||||
sstrs.resize(pos+1);
|
||||
} else if (sstrs[pos] != NULL) {
|
||||
delete sstrs[pos];
|
||||
}
|
||||
sstrs[pos] = new SymbolString(false);
|
||||
result = sstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (mstrs[0] != NULL) {
|
||||
delete mstrs[0];
|
||||
}
|
||||
mstrs[0] = new SymbolString(false);
|
||||
result = mstrs[0]->parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
if (sstrs[0] != NULL) {
|
||||
delete sstrs[0];
|
||||
}
|
||||
sstrs[0] = new SymbolString(false);
|
||||
result = sstrs[0]->parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
message = messages->find(*mstrs[0]);
|
||||
if (message == NULL) {
|
||||
cout << "\"" << check[2] << "\": find error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[2] << "\": find OK" << endl;
|
||||
} else {
|
||||
vector<string>::iterator it = entries.begin();
|
||||
string types = *it;
|
||||
Condition* condition = NULL;
|
||||
result = messages->readConditions(types, "no file", condition);
|
||||
if (result == RESULT_OK) {
|
||||
*it = types;
|
||||
result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages);
|
||||
}
|
||||
if (failedCreate) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": failed create OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: "
|
||||
<< getResultCode(result) << endl;
|
||||
printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result);
|
||||
continue;
|
||||
}
|
||||
if (deleteMessages.size() == 0) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
continue;
|
||||
}
|
||||
if (multi && deleteMessages.size() == 1) {
|
||||
cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl;
|
||||
continue;
|
||||
}
|
||||
if (!multi && deleteMessages.size() > 1) {
|
||||
cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\": create OK" << endl;
|
||||
if (!dontMap) {
|
||||
result_t result = RESULT_OK;
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
result_t result = messages->add(deleteMessage);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": add error: "
|
||||
<< getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
cout << " map OK" << endl;
|
||||
message = deleteMessages.front();
|
||||
deleteMessages.clear();
|
||||
if (onlyMap) {
|
||||
continue;
|
||||
}
|
||||
Message* foundMessage = messages->find(*mstrs[0]);
|
||||
if (foundMessage == message) {
|
||||
cout << " find OK" << endl;
|
||||
} else if (foundMessage == NULL) {
|
||||
cout << " find error: NULL" << endl;
|
||||
} else {
|
||||
cout << " find error: different" << endl;
|
||||
}
|
||||
} else {
|
||||
message = deleteMessages.front();
|
||||
}
|
||||
}
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
message = messages->find(*mstrs[0]);
|
||||
if (message == NULL) {
|
||||
cout << "\"" << check[2] << "\": find error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[2] << "\": find OK" << endl;
|
||||
} else {
|
||||
vector<string>::iterator it = entries.begin();
|
||||
string types = *it;
|
||||
Condition* condition = NULL;
|
||||
result = messages->readConditions(types, "no file", condition);
|
||||
if (result == RESULT_OK) {
|
||||
*it = types;
|
||||
result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages);
|
||||
}
|
||||
if (failedCreate) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": failed create OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: "
|
||||
<< getResultCode(result) << endl;
|
||||
printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result);
|
||||
continue;
|
||||
}
|
||||
if (deleteMessages.size() == 0) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
continue;
|
||||
}
|
||||
if (multi && deleteMessages.size() == 1) {
|
||||
cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl;
|
||||
continue;
|
||||
}
|
||||
if (!multi && deleteMessages.size() > 1) {
|
||||
cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\": create OK" << endl;
|
||||
if (!dontMap) {
|
||||
result_t result = RESULT_OK;
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
result_t result = messages->add(deleteMessage);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": add error: "
|
||||
<< getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
cout << " map OK" << endl;
|
||||
message = deleteMessages.front();
|
||||
deleteMessages.clear();
|
||||
if (onlyMap) {
|
||||
continue;
|
||||
}
|
||||
Message* foundMessage = messages->find(*mstrs[0]);
|
||||
if (foundMessage == message) {
|
||||
cout << " find OK" << endl;
|
||||
} else if (foundMessage == NULL) {
|
||||
cout << " find error: NULL" << endl;
|
||||
} else {
|
||||
cout << " find error: different" << endl;
|
||||
}
|
||||
} else {
|
||||
message = deleteMessages.front();
|
||||
}
|
||||
}
|
||||
|
||||
if (message->isPassive() || decode) {
|
||||
ostringstream output;
|
||||
for (unsigned char index = 0; index < message->getCount(); index++) {
|
||||
message->storeLastData(*mstrs[index], *sstrs[index]);
|
||||
}
|
||||
if (withMessageDump && !decodeJson) {
|
||||
message->dump(output, NULL, true);
|
||||
output << ": ";
|
||||
}
|
||||
result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl;
|
||||
bool match = inputStr == output.str();
|
||||
verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str());
|
||||
}
|
||||
if (!message->isPassive() && (withInput || !decode)) {
|
||||
istringstream input(inputStr);
|
||||
SymbolString writeMstr(false);
|
||||
result = message->prepareMaster(0xff, writeMstr, input);
|
||||
if (failedPrepare) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " \"" << inputStr << "\": failed prepare OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (message->isPassive() || decode) {
|
||||
ostringstream output;
|
||||
for (unsigned char index = 0; index < message->getCount(); index++) {
|
||||
message->storeLastData(*mstrs[index], *sstrs[index]);
|
||||
}
|
||||
if (withMessageDump && !decodeJson) {
|
||||
message->dump(output, NULL, true);
|
||||
output << ": ";
|
||||
}
|
||||
result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl;
|
||||
bool match = inputStr == output.str();
|
||||
verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str());
|
||||
}
|
||||
if (!message->isPassive() && (withInput || !decode)) {
|
||||
istringstream input(inputStr);
|
||||
SymbolString writeMstr(false);
|
||||
result = message->prepareMaster(0xff, writeMstr, input);
|
||||
if (failedPrepare) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " \"" << inputStr << "\": failed prepare OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": prepare error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << inputStr << "\": prepare OK" << endl;
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": prepare error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << inputStr << "\": prepare OK" << endl;
|
||||
|
||||
bool match = writeMstr == *mstrs[0];
|
||||
verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false));
|
||||
}
|
||||
}
|
||||
bool match = writeMstr == *mstrs[0];
|
||||
verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
|
||||
delete templates;
|
||||
delete messages;
|
||||
for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
return 0;
|
||||
delete templates;
|
||||
delete messages;
|
||||
for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,70 +27,70 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SymbolString sstr(true);
|
||||
SymbolString sstr(true);
|
||||
|
||||
if (argc > 1) {
|
||||
result_t result = sstr.parseHex(argv[1], true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
unsigned char gotCrc = sstr.getCRC();
|
||||
cout << "calculated CRC: 0x"
|
||||
<< nouppercase << setw(2) << hex << setfill('0')
|
||||
<< static_cast<unsigned>(gotCrc) << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
string gotStr, expectStr;
|
||||
result_t result = sstr.parseHex("10feb5050427a915aa", false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177";
|
||||
verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77;
|
||||
ostringstream ostr;
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc);
|
||||
expectStr = ostr.str();
|
||||
ostr.str("");
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc);
|
||||
gotStr = ostr.str();
|
||||
verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr);
|
||||
if (argc > 1) {
|
||||
result_t result = sstr.parseHex(argv[1], true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
unsigned char gotCrc = sstr.getCRC();
|
||||
cout << "calculated CRC: 0x"
|
||||
<< nouppercase << setw(2) << hex << setfill('0')
|
||||
<< static_cast<unsigned>(gotCrc) << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
string gotStr, expectStr;
|
||||
result_t result = sstr.parseHex("10feb5050427a915aa", false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177";
|
||||
verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77;
|
||||
ostringstream ostr;
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc);
|
||||
expectStr = ostr.str();
|
||||
ostr.str("");
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc);
|
||||
gotStr = ostr.str();
|
||||
verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr);
|
||||
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
}
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
}
|
||||
|
||||
sstr = SymbolString(false);
|
||||
result = sstr.parseHex("10feb5050427a90015a90177", true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse unescaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr);
|
||||
}
|
||||
sstr = SymbolString(false);
|
||||
result = sstr.parseHex("10feb5050427a90015a90177", true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse unescaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr);
|
||||
}
|
||||
|
||||
return error ? 1 : 0;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
+11
-11
@@ -18,8 +18,8 @@
|
||||
|
||||
#include "clock.h"
|
||||
#ifdef __MACH__
|
||||
# include <mach/clock.h>
|
||||
# include <mach/mach.h>
|
||||
# include <mach/clock.h>
|
||||
# include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#ifdef __MACH__
|
||||
@@ -29,15 +29,15 @@ static clock_serv_t clockServ;
|
||||
|
||||
void clockGettime(struct timespec* t) {
|
||||
#ifdef __MACH__
|
||||
if (!clockInitialized) {
|
||||
clockInitialized = true;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ);
|
||||
}
|
||||
mach_timespec_t mts;
|
||||
clock_get_time(clockServ, &mts);
|
||||
t->tv_sec = mts.tv_sec;
|
||||
t->tv_nsec = mts.tv_nsec;
|
||||
if (!clockInitialized) {
|
||||
clockInitialized = true;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clockServ);
|
||||
}
|
||||
mach_timespec_t mts;
|
||||
clock_get_time(clockServ, &mts);
|
||||
t->tv_sec = mts.tv_sec;
|
||||
t->tv_nsec = mts.tv_nsec;
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, t);
|
||||
clock_gettime(CLOCK_REALTIME, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
+101
-101
@@ -27,23 +27,23 @@
|
||||
|
||||
/** the name of each @a LogFacility. */
|
||||
static const char *facilityNames[] = {
|
||||
"main",
|
||||
"network",
|
||||
"bus",
|
||||
"update",
|
||||
"other",
|
||||
"all",
|
||||
NULL
|
||||
"main",
|
||||
"network",
|
||||
"bus",
|
||||
"update",
|
||||
"other",
|
||||
"all",
|
||||
NULL
|
||||
};
|
||||
|
||||
/** the name of each @a LogLevel. */
|
||||
static const char* levelNames[] = {
|
||||
"none",
|
||||
"error",
|
||||
"notice",
|
||||
"info",
|
||||
"debug",
|
||||
NULL
|
||||
"none",
|
||||
"error",
|
||||
"notice",
|
||||
"info",
|
||||
"debug",
|
||||
NULL
|
||||
};
|
||||
|
||||
/** 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;
|
||||
|
||||
bool setLogFacilities(const char* facilities) {
|
||||
char *input = strdup(facilities);
|
||||
char *opt = reinterpret_cast<char*>(input), *value = NULL;
|
||||
int newFacilites = 0;
|
||||
while (*opt) {
|
||||
int val = getsubopt(&opt, (char *const *)facilityNames, &value);
|
||||
if (val < 0 || val > lf_COUNT || value) {
|
||||
free(input);
|
||||
return false;
|
||||
}
|
||||
if (val == lf_COUNT) {
|
||||
newFacilites = LF_ALL;
|
||||
} else {
|
||||
newFacilites |= 1 << val;
|
||||
}
|
||||
}
|
||||
//s_lastFacilities = newFacilites;
|
||||
s_logFacilites = newFacilites;
|
||||
free(input);
|
||||
return true;
|
||||
char *input = strdup(facilities);
|
||||
char *opt = reinterpret_cast<char*>(input), *value = NULL;
|
||||
int newFacilites = 0;
|
||||
while (*opt) {
|
||||
int val = getsubopt(&opt, (char *const *)facilityNames, &value);
|
||||
if (val < 0 || val > lf_COUNT || value) {
|
||||
free(input);
|
||||
return false;
|
||||
}
|
||||
if (val == lf_COUNT) {
|
||||
newFacilites = LF_ALL;
|
||||
} else {
|
||||
newFacilites |= 1 << val;
|
||||
}
|
||||
}
|
||||
//s_lastFacilities = newFacilites;
|
||||
s_logFacilites = newFacilites;
|
||||
free(input);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getLogFacilities(char* buffer) {
|
||||
if (s_logFacilites == LF_ALL) {
|
||||
return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0;
|
||||
}
|
||||
*buffer = 0; // for strcat to work
|
||||
bool found = false;
|
||||
size_t len = 0;
|
||||
for (int val = 0; val < lf_COUNT; val++) {
|
||||
if (s_logFacilites&(1 << val)) {
|
||||
if (found) {
|
||||
len += snprintf(buffer+len, 48-len, ",");
|
||||
}
|
||||
found = true;
|
||||
len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (s_logFacilites == LF_ALL) {
|
||||
return snprintf(buffer, 48, "%s", facilityNames[lf_COUNT]) != 0;
|
||||
}
|
||||
*buffer = 0; // for strcat to work
|
||||
bool found = false;
|
||||
size_t len = 0;
|
||||
for (int val = 0; val < lf_COUNT; val++) {
|
||||
if (s_logFacilites&(1 << val)) {
|
||||
if (found) {
|
||||
len += snprintf(buffer+len, 48-len, ",");
|
||||
}
|
||||
found = true;
|
||||
len += snprintf(buffer+len, 48-len, "%s", facilityNames[val]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setLogLevel(const char* level) {
|
||||
char *input = strdup(level);
|
||||
char *opt = reinterpret_cast<char*>(input), *value = NULL;
|
||||
int newLevel = 0;
|
||||
if (*opt) {
|
||||
int val = getsubopt(&opt, (char *const *)levelNames, &value);
|
||||
if (val < 0 || val >= ll_COUNT || value || *opt) {
|
||||
free(input);
|
||||
return false;
|
||||
}
|
||||
newLevel = val;
|
||||
}
|
||||
s_logLevel = (LogLevel)newLevel;
|
||||
free(input);
|
||||
return true;
|
||||
char *input = strdup(level);
|
||||
char *opt = reinterpret_cast<char*>(input), *value = NULL;
|
||||
int newLevel = 0;
|
||||
if (*opt) {
|
||||
int val = getsubopt(&opt, (char *const *)levelNames, &value);
|
||||
if (val < 0 || val >= ll_COUNT || value || *opt) {
|
||||
free(input);
|
||||
return false;
|
||||
}
|
||||
newLevel = val;
|
||||
}
|
||||
s_logLevel = (LogLevel)newLevel;
|
||||
free(input);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* getLogLevel() {
|
||||
return levelNames[s_logLevel];
|
||||
return levelNames[s_logLevel];
|
||||
}
|
||||
|
||||
bool setLogFile(const char* filename) {
|
||||
FILE* newFile = fopen(filename, "a");
|
||||
if (newFile == NULL) {
|
||||
return false;
|
||||
}
|
||||
closeLogFile();
|
||||
s_logFile = newFile;
|
||||
return true;
|
||||
FILE* newFile = fopen(filename, "a");
|
||||
if (newFile == NULL) {
|
||||
return false;
|
||||
}
|
||||
closeLogFile();
|
||||
s_logFile = newFile;
|
||||
return true;
|
||||
}
|
||||
|
||||
void closeLogFile() {
|
||||
if (s_logFile != NULL) {
|
||||
if (s_logFile != stdout) {
|
||||
fclose(s_logFile);
|
||||
}
|
||||
s_logFile = NULL;
|
||||
}
|
||||
if (s_logFile != NULL) {
|
||||
if (s_logFile != stdout) {
|
||||
fclose(s_logFile);
|
||||
}
|
||||
s_logFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool needsLog(const LogFacility facility, const LogLevel level) {
|
||||
return ((s_logFacilites & (1 << facility)) != 0)
|
||||
&& (s_logLevel >= level);
|
||||
return ((s_logFacilites & (1 << facility)) != 0)
|
||||
&& (s_logLevel >= level);
|
||||
}
|
||||
|
||||
void logWrite(const char* facility, const char* level, const char* message, va_list ap) {
|
||||
struct timespec ts;
|
||||
struct tm td;
|
||||
clockGettime(&ts);
|
||||
localtime_r(&ts.tv_sec, &td);
|
||||
char* buf;
|
||||
if (vasprintf(&buf, message, ap) >= 0 && buf) {
|
||||
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_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
|
||||
facility, level, buf);
|
||||
fflush(s_logFile);
|
||||
}
|
||||
if (buf) {
|
||||
free(buf);
|
||||
}
|
||||
struct timespec ts;
|
||||
struct tm td;
|
||||
clockGettime(&ts);
|
||||
localtime_r(&ts.tv_sec, &td);
|
||||
char* buf;
|
||||
if (vasprintf(&buf, message, ap) >= 0 && buf) {
|
||||
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_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
|
||||
facility, level, buf);
|
||||
fflush(s_logFile);
|
||||
}
|
||||
if (buf) {
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
logWrite(facilityNames[facility], levelNames[level], message, ap);
|
||||
va_end(ap);
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
logWrite(facilityNames[facility], levelNames[level], message, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void logWrite(const char* facility, const LogLevel level, const char* message, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
logWrite(facility, levelNames[level], message, ap);
|
||||
va_end(ap);
|
||||
va_list ap;
|
||||
va_start(ap, message);
|
||||
logWrite(facility, levelNames[level], message, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
+12
-12
@@ -23,12 +23,12 @@
|
||||
|
||||
/** the available log facilities. */
|
||||
enum LogFacility {
|
||||
lf_main = 0, //!< main loop
|
||||
lf_network, //!< network related
|
||||
lf_bus, //!< eBUS related
|
||||
lf_update, //!< updates found while listening to the bus
|
||||
lf_other, //!< all other log facilities
|
||||
lf_COUNT = 5 //!< number of available log facilities
|
||||
lf_main = 0, //!< main loop
|
||||
lf_network, //!< network related
|
||||
lf_bus, //!< eBUS related
|
||||
lf_update, //!< updates found while listening to the bus
|
||||
lf_other, //!< all other log facilities
|
||||
lf_COUNT = 5 //!< number of available log facilities
|
||||
};
|
||||
|
||||
/** macro for enabling all log facilities. */
|
||||
@@ -36,12 +36,12 @@ enum LogFacility {
|
||||
|
||||
/** the available log levels. */
|
||||
enum LogLevel {
|
||||
ll_none = 0, //!< no level at all
|
||||
ll_error, //!< error message
|
||||
ll_notice, //!< important message
|
||||
ll_info, //!< informational message
|
||||
ll_debug, //!< debugging message (normally suppressed)
|
||||
ll_COUNT = 5 //!< number of available log levels
|
||||
ll_none = 0, //!< no level at all
|
||||
ll_error, //!< error message
|
||||
ll_notice, //!< important message
|
||||
ll_info, //!< informational message
|
||||
ll_debug, //!< debugging message (normally suppressed)
|
||||
ll_COUNT = 5 //!< number of available log levels
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+32
-32
@@ -28,45 +28,45 @@
|
||||
* class to notify other thread per pipe.
|
||||
*/
|
||||
class Notify {
|
||||
public:
|
||||
/**
|
||||
* constructs a new instance and do notifying.
|
||||
*/
|
||||
Notify() {
|
||||
int pipefd[2];
|
||||
int ret = pipe(pipefd);
|
||||
public:
|
||||
/**
|
||||
* constructs a new instance and do notifying.
|
||||
*/
|
||||
Notify() {
|
||||
int pipefd[2];
|
||||
int ret = pipe(pipefd);
|
||||
|
||||
if (ret == 0) {
|
||||
m_recvfd = pipefd[0];
|
||||
m_sendfd = pipefd[1];
|
||||
if (ret == 0) {
|
||||
m_recvfd = pipefd[0];
|
||||
m_sendfd = pipefd[1];
|
||||
|
||||
fcntl(m_sendfd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
fcntl(m_sendfd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~Notify() { close(m_sendfd); close(m_recvfd); }
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~Notify() { close(m_sendfd); close(m_recvfd); }
|
||||
|
||||
/**
|
||||
* file descriptor to watch for notify event.
|
||||
* @return the notification value.
|
||||
*/
|
||||
int notifyFD() { return m_recvfd; }
|
||||
/**
|
||||
* file descriptor to watch for notify event.
|
||||
* @return the notification value.
|
||||
*/
|
||||
int notifyFD() { return m_recvfd; }
|
||||
|
||||
/**
|
||||
* write notify event to file descriptor.
|
||||
* @return result of writing notification.
|
||||
*/
|
||||
int notify() const { return write(m_sendfd, "1", 1); }
|
||||
/**
|
||||
* write notify event to file descriptor.
|
||||
* @return result of writing notification.
|
||||
*/
|
||||
int notify() const { return write(m_sendfd, "1", 1); }
|
||||
|
||||
private:
|
||||
/** file descriptor to watch */
|
||||
int m_recvfd;
|
||||
private:
|
||||
/** file descriptor to watch */
|
||||
int m_recvfd;
|
||||
|
||||
/** file descriptor to notify */
|
||||
int m_sendfd;
|
||||
/** file descriptor to notify */
|
||||
int m_sendfd;
|
||||
};
|
||||
|
||||
#endif // LIB_UTILS_NOTIFY_H_
|
||||
|
||||
+104
-104
@@ -34,122 +34,122 @@ using std::list;
|
||||
*/
|
||||
template <typename T>
|
||||
class Queue {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Queue() {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Queue() {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Queue() {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Queue() {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param src the object to copy from.
|
||||
*/
|
||||
Queue(const Queue& src);
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param src the object to copy from.
|
||||
*/
|
||||
Queue(const Queue& src);
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Add an item to the end of queue.
|
||||
* @param item the item to add.
|
||||
*/
|
||||
void push(T item) {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_queue.push_back(item);
|
||||
pthread_cond_broadcast(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Add an item to the end of queue.
|
||||
* @param item the item to add.
|
||||
*/
|
||||
void push(T item) {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_queue.push_back(item);
|
||||
pthread_cond_broadcast(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return the item, or NULL if no item is available within the specified time.
|
||||
*/
|
||||
T pop(int timeout = 0) {
|
||||
T item;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (timeout > 0) {
|
||||
struct timespec t;
|
||||
clockGettime(&t);
|
||||
t.tv_sec += timeout;
|
||||
while (m_queue.empty()) {
|
||||
if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_queue.empty()) {
|
||||
item = NULL;
|
||||
} else {
|
||||
item = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
}
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return item;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* @return the item, or NULL if no item is available within the specified time.
|
||||
*/
|
||||
T pop(int timeout = 0) {
|
||||
T item;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (timeout > 0) {
|
||||
struct timespec t;
|
||||
clockGettime(&t);
|
||||
t.tv_sec += timeout;
|
||||
while (m_queue.empty()) {
|
||||
if (pthread_cond_timedwait(&m_cond, &m_mutex, &t) == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_queue.empty()) {
|
||||
item = NULL;
|
||||
} else {
|
||||
item = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
}
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 wait true to wait for the item to appear in the queue.
|
||||
* @return whether the item was removed.
|
||||
*/
|
||||
bool remove(T item, bool wait = false) {
|
||||
bool ret = false;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
do {
|
||||
size_t oldSize = m_queue.size();
|
||||
if (oldSize > 0) {
|
||||
m_queue.remove(item);
|
||||
if (m_queue.size() != oldSize) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_cond_wait(&m_cond, &m_mutex);
|
||||
} while (wait);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* 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 wait true to wait for the item to appear in the queue.
|
||||
* @return whether the item was removed.
|
||||
*/
|
||||
bool remove(T item, bool wait = false) {
|
||||
bool ret = false;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
do {
|
||||
size_t oldSize = m_queue.size();
|
||||
if (oldSize > 0) {
|
||||
m_queue.remove(item);
|
||||
if (m_queue.size() != oldSize) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_cond_wait(&m_cond, &m_mutex);
|
||||
} while (wait);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first item in the queue without removing it.
|
||||
* @return the item, or NULL if no item is available.
|
||||
*/
|
||||
T peek() {
|
||||
T item;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (m_queue.empty()) {
|
||||
item = NULL;
|
||||
} else {
|
||||
item = m_queue.front();
|
||||
}
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return item;
|
||||
}
|
||||
/**
|
||||
* Return the first item in the queue without removing it.
|
||||
* @return the item, or NULL if no item is available.
|
||||
*/
|
||||
T peek() {
|
||||
T item;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (m_queue.empty()) {
|
||||
item = NULL;
|
||||
} else {
|
||||
item = m_queue.front();
|
||||
}
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/** the queue itself */
|
||||
list<T> m_queue;
|
||||
private:
|
||||
/** the queue itself */
|
||||
list<T> m_queue;
|
||||
|
||||
/** mutex variable for exclusive lock */
|
||||
pthread_mutex_t m_mutex;
|
||||
/** mutex variable for exclusive lock */
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
/** condition variable for exclusive lock */
|
||||
pthread_cond_t m_cond;
|
||||
/** condition variable for exclusive lock */
|
||||
pthread_cond_t m_cond;
|
||||
};
|
||||
|
||||
#endif // LIB_UTILS_QUEUE_H_
|
||||
|
||||
@@ -30,59 +30,59 @@
|
||||
using std::streamsize;
|
||||
|
||||
RotateFile::~RotateFile() {
|
||||
if (m_stream) {
|
||||
fclose(m_stream);
|
||||
m_stream = NULL;
|
||||
}
|
||||
if (m_stream) {
|
||||
fclose(m_stream);
|
||||
m_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool RotateFile::setEnabled(bool enabled) {
|
||||
if (enabled == m_enabled) {
|
||||
return false;
|
||||
}
|
||||
m_enabled = enabled;
|
||||
if (m_stream) {
|
||||
fclose(m_stream);
|
||||
m_stream = NULL;
|
||||
}
|
||||
if (enabled) {
|
||||
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
|
||||
m_fileSize = 0;
|
||||
}
|
||||
return true;
|
||||
if (enabled == m_enabled) {
|
||||
return false;
|
||||
}
|
||||
m_enabled = enabled;
|
||||
if (m_stream) {
|
||||
fclose(m_stream);
|
||||
m_stream = NULL;
|
||||
}
|
||||
if (enabled) {
|
||||
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
|
||||
m_fileSize = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RotateFile::write(unsigned char* value, unsigned int size, bool received) {
|
||||
if (!m_enabled || !m_stream) {
|
||||
return;
|
||||
}
|
||||
if (m_textMode) {
|
||||
struct timespec ts;
|
||||
struct tm td;
|
||||
clockGettime(&ts);
|
||||
localtime_r(&ts.tv_sec, &td);
|
||||
fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c",
|
||||
td.tm_year+1900, td.tm_mon+1, td.tm_mday,
|
||||
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
|
||||
received ? '<' : '>');
|
||||
for (unsigned int pos = 0; pos < size; pos++) {
|
||||
fprintf(m_stream, "%2.2x ", value[pos]);
|
||||
}
|
||||
fprintf(m_stream, "\n");
|
||||
m_fileSize += 25+3*size+1;
|
||||
} else {
|
||||
fwrite(value, (streamsize)size, 1, m_stream);
|
||||
m_fileSize += size;
|
||||
}
|
||||
if ((m_fileSize%1024) == 0) {
|
||||
fflush(m_stream);
|
||||
}
|
||||
if (m_fileSize >= m_maxSize * 1024LL) {
|
||||
string oldfile = string(m_fileName)+".old";
|
||||
if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) {
|
||||
fclose(m_stream);
|
||||
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
|
||||
m_fileSize = 0;
|
||||
}
|
||||
}
|
||||
if (!m_enabled || !m_stream) {
|
||||
return;
|
||||
}
|
||||
if (m_textMode) {
|
||||
struct timespec ts;
|
||||
struct tm td;
|
||||
clockGettime(&ts);
|
||||
localtime_r(&ts.tv_sec, &td);
|
||||
fprintf(m_stream, "%04d-%02d-%02d %02d:%02d:%02d.%03ld %c",
|
||||
td.tm_year+1900, td.tm_mon+1, td.tm_mday,
|
||||
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
|
||||
received ? '<' : '>');
|
||||
for (unsigned int pos = 0; pos < size; pos++) {
|
||||
fprintf(m_stream, "%2.2x ", value[pos]);
|
||||
}
|
||||
fprintf(m_stream, "\n");
|
||||
m_fileSize += 25+3*size+1;
|
||||
} else {
|
||||
fwrite(value, (streamsize)size, 1, m_stream);
|
||||
m_fileSize += size;
|
||||
}
|
||||
if ((m_fileSize%1024) == 0) {
|
||||
fflush(m_stream);
|
||||
}
|
||||
if (m_fileSize >= m_maxSize * 1024LL) {
|
||||
string oldfile = string(m_fileName)+".old";
|
||||
if (rename(m_fileName.c_str(), oldfile.c_str()) == 0) {
|
||||
fclose(m_stream);
|
||||
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
|
||||
m_fileSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-44
@@ -35,61 +35,61 @@ using std::string;
|
||||
* Helper class for writing to a rotating file with maximum size.
|
||||
*/
|
||||
class RotateFile {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param fileName the name of the file 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.
|
||||
*/
|
||||
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) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param fileName the name of the file 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.
|
||||
*/
|
||||
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) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~RotateFile();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~RotateFile();
|
||||
|
||||
/**
|
||||
* Enable or disable writing to the file.
|
||||
* @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.
|
||||
*/
|
||||
bool setEnabled(bool enabled = true);
|
||||
/**
|
||||
* Enable or disable writing to the file.
|
||||
* @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.
|
||||
*/
|
||||
bool setEnabled(bool enabled = true);
|
||||
|
||||
/**
|
||||
* Return whether writing to the file is enabled.
|
||||
* @return whether writing to the file is enabled.
|
||||
*/
|
||||
bool isEnabled() { return m_enabled; }
|
||||
/**
|
||||
* Return whether writing to the file is enabled.
|
||||
* @return whether writing to the file is enabled.
|
||||
*/
|
||||
bool isEnabled() { return m_enabled; }
|
||||
|
||||
/**
|
||||
* Write a number of bytes to the stream.
|
||||
* @param value the pointer to the 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).
|
||||
*/
|
||||
void write(unsigned char* value, unsigned int size, bool received = true);
|
||||
/**
|
||||
* Write a number of bytes to the stream.
|
||||
* @param value the pointer to the 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).
|
||||
*/
|
||||
void write(unsigned char* value, unsigned int size, bool received = true);
|
||||
|
||||
|
||||
private:
|
||||
/** whether writing to the file is enabled. */
|
||||
bool m_enabled;
|
||||
private:
|
||||
/** whether writing to the file is enabled. */
|
||||
bool m_enabled;
|
||||
|
||||
/** the name of the file write to. */
|
||||
const string m_fileName;
|
||||
/** the name of the file write to. */
|
||||
const string m_fileName;
|
||||
|
||||
/** the maximum size of @a m_file, or 0 for infinite. */
|
||||
const unsigned int m_maxSize;
|
||||
/** the maximum size of @a m_file, or 0 for infinite. */
|
||||
const unsigned int m_maxSize;
|
||||
|
||||
/** whether to write each byte with prefixed timestamp and direction as text. */
|
||||
const bool m_textMode;
|
||||
/** whether to write each byte with prefixed timestamp and direction as text. */
|
||||
const bool m_textMode;
|
||||
|
||||
/** the @a FILE to writing to. */
|
||||
FILE* m_stream;
|
||||
/** the @a FILE to writing to. */
|
||||
FILE* m_stream;
|
||||
|
||||
/** the number of bytes already written to the @a m_file. */
|
||||
uint64_t m_fileSize;
|
||||
/** the number of bytes already written to the @a m_file. */
|
||||
uint64_t m_fileSize;
|
||||
};
|
||||
|
||||
#endif // LIB_UTILS_ROTATEFILE_H_
|
||||
|
||||
+68
-68
@@ -24,98 +24,98 @@
|
||||
#include <cstdlib>
|
||||
|
||||
TCPSocket::TCPSocket(int sfd, struct sockaddr_in* address) : m_sfd(sfd) {
|
||||
char ip[17];
|
||||
inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1);
|
||||
m_ip = ip;
|
||||
m_port = (uint16_t)ntohs(address->sin_port);
|
||||
char ip[17];
|
||||
inet_ntop(AF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, (socklen_t)sizeof(ip)-1);
|
||||
m_ip = ip;
|
||||
m_port = (uint16_t)ntohs(address->sin_port);
|
||||
}
|
||||
|
||||
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) {
|
||||
struct sockaddr_in address;
|
||||
int ret;
|
||||
struct sockaddr_in address;
|
||||
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) {
|
||||
struct hostent* he;
|
||||
if (inet_addr(server.c_str()) == INADDR_NONE) {
|
||||
struct hostent* he;
|
||||
|
||||
he = gethostbyname(server.c_str());
|
||||
if (he == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length);
|
||||
} else {
|
||||
ret = inet_aton(server.c_str(), &address.sin_addr);
|
||||
if (ret == 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
he = gethostbyname(server.c_str());
|
||||
if (he == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&address.sin_addr, he->h_addr_list[0], he->h_length);
|
||||
} else {
|
||||
ret = inet_aton(server.c_str(), &address.sin_addr);
|
||||
if (ret == 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons(port);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons(port);
|
||||
|
||||
int sfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sfd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address));
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return new TCPSocket(sfd, &address);
|
||||
int sfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sfd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
ret = ::connect(sfd, (struct sockaddr*) &address, sizeof(address));
|
||||
if (ret < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return new TCPSocket(sfd, &address);
|
||||
}
|
||||
|
||||
|
||||
int TCPServer::start() {
|
||||
if (m_listening) {
|
||||
return 0;
|
||||
}
|
||||
m_lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in address;
|
||||
if (m_listening) {
|
||||
return 0;
|
||||
}
|
||||
m_lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in address;
|
||||
|
||||
memset(&address, 0, sizeof(address));
|
||||
memset(&address, 0, sizeof(address));
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons(m_port);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons(m_port);
|
||||
|
||||
if (m_address.size() > 0) {
|
||||
inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr));
|
||||
} else {
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
int optval = 1;
|
||||
setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
if (m_address.size() > 0) {
|
||||
inet_pton(AF_INET, m_address.c_str(), &(address.sin_addr));
|
||||
} else {
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
int optval = 1;
|
||||
setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
|
||||
int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address));
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = listen(m_lfd, 5);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
m_listening = true;
|
||||
return result;
|
||||
int result = bind(m_lfd, (struct sockaddr*) &address, sizeof(address));
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = listen(m_lfd, 5);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
m_listening = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
TCPSocket* TCPServer::newSocket() {
|
||||
if (!m_listening) {
|
||||
return NULL;
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
socklen_t len = sizeof(address);
|
||||
if (!m_listening) {
|
||||
return NULL;
|
||||
}
|
||||
struct sockaddr_in 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);
|
||||
if (sfd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return new TCPSocket(sfd, &address);
|
||||
int sfd = accept(m_lfd, (struct sockaddr*) &address, &len);
|
||||
if (sfd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return new TCPSocket(sfd, &address);
|
||||
}
|
||||
|
||||
|
||||
+100
-100
@@ -38,140 +38,140 @@ using std::string;
|
||||
* class for low level tcp socket operations. (open, close, send, receive).
|
||||
*/
|
||||
class TCPSocket {
|
||||
public:
|
||||
/** grant access for friend class TCPClient */
|
||||
friend class TCPClient;
|
||||
public:
|
||||
/** grant access for friend class TCPClient */
|
||||
friend class TCPClient;
|
||||
|
||||
/** grant access for friend class TCPServer */
|
||||
friend class TCPServer;
|
||||
/** grant access for friend class TCPServer */
|
||||
friend class TCPServer;
|
||||
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~TCPSocket() { close(m_sfd); }
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~TCPSocket() { close(m_sfd); }
|
||||
|
||||
/**
|
||||
* write bytes to opened file descriptor.
|
||||
* @param buffer data to send.
|
||||
* @param len number of bytes to send.
|
||||
* @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); }
|
||||
/**
|
||||
* write bytes to opened file descriptor.
|
||||
* @param buffer data to send.
|
||||
* @param len number of bytes to send.
|
||||
* @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); }
|
||||
|
||||
/**
|
||||
* read bytes from opened file descriptor.
|
||||
* @param buffer for received bytes.
|
||||
* @param len size of the receive buffer.
|
||||
* @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); }
|
||||
/**
|
||||
* read bytes from opened file descriptor.
|
||||
* @param buffer for received bytes.
|
||||
* @param len size of the receive buffer.
|
||||
* @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); }
|
||||
|
||||
/**
|
||||
* returns the tcp port.
|
||||
* @return the tcp port.
|
||||
*/
|
||||
uint16_t getPort() const { return m_port; }
|
||||
/**
|
||||
* returns the tcp port.
|
||||
* @return the tcp port.
|
||||
*/
|
||||
uint16_t getPort() const { return m_port; }
|
||||
|
||||
/**
|
||||
* returns the ip address.
|
||||
* @return the ip address.
|
||||
*/
|
||||
string getIP() const { return m_ip; }
|
||||
/**
|
||||
* returns the ip address.
|
||||
* @return the ip address.
|
||||
*/
|
||||
string getIP() const { return m_ip; }
|
||||
|
||||
/**
|
||||
* returns the file descriptor.
|
||||
* @return the file descriptor.
|
||||
*/
|
||||
int getFD() const { return m_sfd; }
|
||||
/**
|
||||
* returns the file descriptor.
|
||||
* @return the file descriptor.
|
||||
*/
|
||||
int getFD() const { return m_sfd; }
|
||||
|
||||
/**
|
||||
* returns status of file descriptor.
|
||||
* @return true if file descriptor is valid.
|
||||
*/
|
||||
bool isValid();
|
||||
/**
|
||||
* returns status of file descriptor.
|
||||
* @return true if file descriptor is valid.
|
||||
*/
|
||||
bool isValid();
|
||||
|
||||
|
||||
private:
|
||||
/** file descriptor from tcp socket */
|
||||
int m_sfd;
|
||||
private:
|
||||
/** file descriptor from tcp socket */
|
||||
int m_sfd;
|
||||
|
||||
/** port of tcp socket */
|
||||
uint16_t m_port;
|
||||
/** port of tcp socket */
|
||||
uint16_t m_port;
|
||||
|
||||
/** ip address of tcp socket */
|
||||
string m_ip;
|
||||
/** ip address of tcp socket */
|
||||
string m_ip;
|
||||
|
||||
/**
|
||||
* private constructor, limited access only for friend classes.
|
||||
* @param sfd the file desctriptor of tcp socket.
|
||||
* @param address struct which holds the ip address.
|
||||
*/
|
||||
TCPSocket(int sfd, struct sockaddr_in* address);
|
||||
/**
|
||||
* private constructor, limited access only for friend classes.
|
||||
* @param sfd the file desctriptor of tcp socket.
|
||||
* @param address struct which holds the ip address.
|
||||
*/
|
||||
TCPSocket(int sfd, struct sockaddr_in* address);
|
||||
};
|
||||
|
||||
/**
|
||||
* class to initiate a tcp socket connection to a listening server.
|
||||
*/
|
||||
class TCPClient {
|
||||
public:
|
||||
/**
|
||||
* initiate a tcp socket connection to a listening server.
|
||||
* @param server the server name or ip address to connect.
|
||||
* @param port the tcp port.
|
||||
* @return pointer to an opened tcp socket.
|
||||
*/
|
||||
TCPSocket* connect(const string& server, const uint16_t& port);
|
||||
public:
|
||||
/**
|
||||
* initiate a tcp socket connection to a listening server.
|
||||
* @param server the server name or ip address to connect.
|
||||
* @param port the tcp port.
|
||||
* @return pointer to an opened tcp socket.
|
||||
*/
|
||||
TCPSocket* connect(const string& server, const uint16_t& port);
|
||||
};
|
||||
|
||||
/**
|
||||
* class for a tcp based network server.
|
||||
*/
|
||||
class TCPServer {
|
||||
public:
|
||||
/**
|
||||
* creates a new instance of a listening tcp server.
|
||||
* @param port the tcp port.
|
||||
* @param address the ip address.
|
||||
*/
|
||||
TCPServer(const uint16_t port, const string address)
|
||||
: m_lfd(0), m_port(port), m_address(address), m_listening(false) {}
|
||||
public:
|
||||
/**
|
||||
* creates a new instance of a listening tcp server.
|
||||
* @param port the tcp port.
|
||||
* @param address the ip address.
|
||||
*/
|
||||
TCPServer(const uint16_t port, const string address)
|
||||
: m_lfd(0), m_port(port), m_address(address), m_listening(false) {}
|
||||
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~TCPServer() { if (m_lfd > 0) {close(m_lfd);} }
|
||||
/**
|
||||
* destructor.
|
||||
*/
|
||||
~TCPServer() { if (m_lfd > 0) {close(m_lfd);} }
|
||||
|
||||
/**
|
||||
* start listening of tcp socket.
|
||||
* @return result of low level functions.
|
||||
*/
|
||||
int start();
|
||||
/**
|
||||
* start listening of tcp socket.
|
||||
* @return result of low level functions.
|
||||
*/
|
||||
int start();
|
||||
|
||||
/**
|
||||
* accept an incoming tcp connection and create a local tcp socket for communication.
|
||||
* @return pointer to an opened tcp socket.
|
||||
*/
|
||||
TCPSocket* newSocket();
|
||||
/**
|
||||
* accept an incoming tcp connection and create a local tcp socket for communication.
|
||||
* @return pointer to an opened tcp socket.
|
||||
*/
|
||||
TCPSocket* newSocket();
|
||||
|
||||
/**
|
||||
* returns the file descriptor.
|
||||
* @return the file descriptor.
|
||||
*/
|
||||
int getFD() const { return m_lfd; }
|
||||
/**
|
||||
* returns the file descriptor.
|
||||
* @return the file descriptor.
|
||||
*/
|
||||
int getFD() const { return m_lfd; }
|
||||
|
||||
|
||||
private:
|
||||
/** file descriptor from listening tcp socket */
|
||||
int m_lfd;
|
||||
private:
|
||||
/** file descriptor from listening tcp socket */
|
||||
int m_lfd;
|
||||
|
||||
/** listening tcp port */
|
||||
uint16_t m_port;
|
||||
/** listening tcp port */
|
||||
uint16_t m_port;
|
||||
|
||||
/** listening tcp socket ip address */
|
||||
string m_address;
|
||||
/** listening tcp socket ip address */
|
||||
string m_address;
|
||||
|
||||
/** true if object is already listening */
|
||||
bool m_listening;
|
||||
/** true if object is already listening */
|
||||
bool m_listening;
|
||||
};
|
||||
|
||||
#endif // LIB_UTILS_TCPSOCKET_H_
|
||||
|
||||
+46
-46
@@ -17,88 +17,88 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "thread.h"
|
||||
#include "clock.h"
|
||||
|
||||
void* Thread::runThread(void* arg) {
|
||||
reinterpret_cast<Thread*>(arg)->enter();
|
||||
return NULL;
|
||||
reinterpret_cast<Thread*>(arg)->enter();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Thread::~Thread() {
|
||||
if (m_started) {
|
||||
pthread_cancel(m_threadid);
|
||||
pthread_detach(m_threadid);
|
||||
}
|
||||
if (m_started) {
|
||||
pthread_cancel(m_threadid);
|
||||
pthread_detach(m_threadid);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::start(const char* name) {
|
||||
int result = pthread_create(&m_threadid, NULL, runThread, this);
|
||||
if (result == 0) {
|
||||
int result = pthread_create(&m_threadid, NULL, runThread, this);
|
||||
if (result == 0) {
|
||||
#ifdef HAVE_PTHREAD_SETNAME_NP
|
||||
#ifndef __MACH__
|
||||
pthread_setname_np(m_threadid, name);
|
||||
pthread_setname_np(m_threadid, name);
|
||||
#endif
|
||||
#endif
|
||||
m_started = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
m_started = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thread::join() {
|
||||
int result = -1;
|
||||
if (m_started) {
|
||||
m_stopped = true;
|
||||
result = pthread_join(m_threadid, NULL);
|
||||
if (result == 0) {
|
||||
m_started = false;
|
||||
}
|
||||
}
|
||||
return result == 0;
|
||||
int result = -1;
|
||||
if (m_started) {
|
||||
m_stopped = true;
|
||||
result = pthread_join(m_threadid, NULL);
|
||||
if (result == 0) {
|
||||
m_started = false;
|
||||
}
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
void Thread::enter() {
|
||||
m_running = true;
|
||||
run();
|
||||
m_running = false;
|
||||
m_running = true;
|
||||
run();
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
|
||||
WaitThread::WaitThread()
|
||||
: Thread() {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
: Thread() {
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
pthread_cond_init(&m_cond, NULL);
|
||||
}
|
||||
|
||||
WaitThread::~WaitThread() {
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
pthread_cond_destroy(&m_cond);
|
||||
}
|
||||
|
||||
void WaitThread::stop() {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
Thread::stop();
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
Thread::stop();
|
||||
}
|
||||
|
||||
bool WaitThread::join() {
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return Thread::join();
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_signal(&m_cond);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return Thread::join();
|
||||
}
|
||||
|
||||
bool WaitThread::Wait(int seconds) {
|
||||
struct timespec t;
|
||||
clockGettime(&t);
|
||||
t.tv_sec += seconds;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_timedwait(&m_cond, &m_mutex, &t);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return isRunning();
|
||||
struct timespec t;
|
||||
clockGettime(&t);
|
||||
t.tv_sec += seconds;
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
pthread_cond_timedwait(&m_cond, &m_mutex, &t);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
+82
-82
@@ -27,79 +27,79 @@
|
||||
* wrapper class for pthread.
|
||||
*/
|
||||
class Thread {
|
||||
public:
|
||||
/**
|
||||
* constructor.
|
||||
*/
|
||||
Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {}
|
||||
public:
|
||||
/**
|
||||
* constructor.
|
||||
*/
|
||||
Thread() : m_threadid(0), m_started(false), m_running(false), m_stopped(false) {}
|
||||
|
||||
/**
|
||||
* virtual destructor.
|
||||
*/
|
||||
virtual ~Thread();
|
||||
/**
|
||||
* virtual destructor.
|
||||
*/
|
||||
virtual ~Thread();
|
||||
|
||||
/**
|
||||
* Thread entry helper for pthread_create.
|
||||
* @param arg pointer to the @a Thread.
|
||||
* @return NULL.
|
||||
*/
|
||||
static void* runThread(void* arg);
|
||||
/**
|
||||
* Thread entry helper for pthread_create.
|
||||
* @param arg pointer to the @a Thread.
|
||||
* @return NULL.
|
||||
*/
|
||||
static void* runThread(void* arg);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual bool isRunning() { return m_running && !m_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.
|
||||
*/
|
||||
virtual bool isRunning() { return m_running && !m_stopped; }
|
||||
|
||||
/**
|
||||
* Create the native thread and set its name.
|
||||
* @param name the thread name to show in the process list.
|
||||
* @return whether the thread was started.
|
||||
*/
|
||||
virtual bool start(const char* name);
|
||||
/**
|
||||
* Create the native thread and set its name.
|
||||
* @param name the thread name to show in the process list.
|
||||
* @return whether the thread was started.
|
||||
*/
|
||||
virtual bool start(const char* name);
|
||||
|
||||
/**
|
||||
* Notify the thread that it shall stop.
|
||||
*/
|
||||
virtual void stop() { m_stopped = true; }
|
||||
/**
|
||||
* Notify the thread that it shall stop.
|
||||
*/
|
||||
virtual void stop() { m_stopped = true; }
|
||||
|
||||
/**
|
||||
* Join the thread.
|
||||
* @return whether the thread was joined.
|
||||
*/
|
||||
virtual bool join();
|
||||
/**
|
||||
* Join the thread.
|
||||
* @return whether the thread was joined.
|
||||
*/
|
||||
virtual bool join();
|
||||
|
||||
/**
|
||||
* Get the thread id.
|
||||
* @return the thread id.
|
||||
*/
|
||||
pthread_t self() { return m_threadid; }
|
||||
/**
|
||||
* Get the thread id.
|
||||
* @return the thread id.
|
||||
*/
|
||||
pthread_t self() { return m_threadid; }
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Thread entry method to be overridden by derived class.
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
protected:
|
||||
/**
|
||||
* Thread entry method to be overridden by derived class.
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Enter the Thread loop by calling run().
|
||||
*/
|
||||
void enter();
|
||||
private:
|
||||
/**
|
||||
* Enter the Thread loop by calling run().
|
||||
*/
|
||||
void enter();
|
||||
|
||||
/** own thread id */
|
||||
pthread_t m_threadid;
|
||||
/** own thread id */
|
||||
pthread_t m_threadid;
|
||||
|
||||
/** Whether the thread was started. */
|
||||
bool m_started;
|
||||
/** Whether the thread was started. */
|
||||
bool m_started;
|
||||
|
||||
/** Whether the thread is still running (i.e. in @a run() ). */
|
||||
bool m_running;
|
||||
/** Whether the thread is still running (i.e. in @a run() ). */
|
||||
bool m_running;
|
||||
|
||||
/** Whether the thread was stopped by @a stop() or @a join(). */
|
||||
bool m_stopped;
|
||||
/** Whether the thread was stopped by @a stop() or @a join(). */
|
||||
bool m_stopped;
|
||||
};
|
||||
|
||||
|
||||
@@ -107,37 +107,37 @@ class Thread {
|
||||
* A @a Thread that can be waited on.
|
||||
*/
|
||||
class WaitThread : public Thread {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
WaitThread();
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
WaitThread();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~WaitThread();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~WaitThread();
|
||||
|
||||
// @copydoc
|
||||
virtual void stop();
|
||||
// @copydoc
|
||||
virtual void stop();
|
||||
|
||||
// @copydoc
|
||||
virtual bool join();
|
||||
// @copydoc
|
||||
virtual bool join();
|
||||
|
||||
/**
|
||||
* Wait for the specified amount of time.
|
||||
* @param seconds the number of seconds to wait.
|
||||
* @return true if this @a WaitThread is still running and not yet stopped.
|
||||
*/
|
||||
bool Wait(int seconds);
|
||||
/**
|
||||
* Wait for the specified amount of time.
|
||||
* @param seconds the number of seconds to wait.
|
||||
* @return true if this @a WaitThread is still running and not yet stopped.
|
||||
*/
|
||||
bool Wait(int seconds);
|
||||
|
||||
|
||||
private:
|
||||
/** the mutex for waiting. */
|
||||
pthread_mutex_t m_mutex;
|
||||
private:
|
||||
/** the mutex for waiting. */
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
/** the condition for waiting. */
|
||||
pthread_cond_t m_cond;
|
||||
/** the condition for waiting. */
|
||||
pthread_cond_t m_cond;
|
||||
};
|
||||
|
||||
#endif // LIB_UTILS_THREAD_H_
|
||||
|
||||
+183
-183
@@ -17,13 +17,13 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <argp.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_PPOLL
|
||||
# include <poll.h>
|
||||
# include <poll.h>
|
||||
#endif
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
@@ -39,20 +39,20 @@ using std::endl;
|
||||
|
||||
/** A structure holding all program options. */
|
||||
struct options {
|
||||
const char* server; //!< ebusd server host (name or ip) [localhost]
|
||||
uint16_t port; //!< ebusd server port [8888]
|
||||
const char* server; //!< ebusd server host (name or ip) [localhost]
|
||||
uint16_t port; //!< ebusd server port [8888]
|
||||
|
||||
char* const *args; //!< arguments to pass to ebusd
|
||||
unsigned int argCount; //!< number of arguments to pass to ebusd
|
||||
char* const *args; //!< arguments to pass to ebusd
|
||||
unsigned int argCount; //!< number of arguments to pass to ebusd
|
||||
};
|
||||
|
||||
/** the program options. */
|
||||
static struct options opt = {
|
||||
"localhost", // server
|
||||
8888, // port
|
||||
"localhost", // server
|
||||
8888, // port
|
||||
|
||||
NULL, // args
|
||||
0 // argCount
|
||||
NULL, // args
|
||||
0 // argCount
|
||||
};
|
||||
|
||||
/** the version string of the program. */
|
||||
@@ -63,21 +63,21 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT"";
|
||||
|
||||
/** the documentation of the program. */
|
||||
static const char argpdoc[] =
|
||||
"Client for acessing "PACKAGE" via TCP.\n"
|
||||
"\v"
|
||||
"If given, send COMMAND together with CMDOPT options to "PACKAGE".\n"
|
||||
"Use 'help' as COMMAND for help on available "PACKAGE" commands.";
|
||||
"Client for acessing "PACKAGE" via TCP.\n"
|
||||
"\v"
|
||||
"If given, send COMMAND together with CMDOPT options to "PACKAGE".\n"
|
||||
"Use 'help' as COMMAND for help on available "PACKAGE" commands.";
|
||||
|
||||
/** the description of the accepted arguments. */
|
||||
static char argpargsdoc[] = "\nCOMMAND [CMDOPT...]";
|
||||
|
||||
/** the definition of the known program arguments. */
|
||||
static const struct argp_option argpoptions[] = {
|
||||
{NULL, 0, NULL, 0, "Options:", 1 },
|
||||
{"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 },
|
||||
{NULL, 0, NULL, 0, "Options:", 1 },
|
||||
{"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 },
|
||||
|
||||
{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.
|
||||
*/
|
||||
error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
struct options *opt = (struct options*)state->input;
|
||||
char* strEnd = NULL;
|
||||
unsigned int port;
|
||||
switch (key) {
|
||||
// Device settings:
|
||||
case 's': // --server=localhost
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid server");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->server = arg;
|
||||
break;
|
||||
case 'p': // --port=8888
|
||||
port = strtoul(arg, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) {
|
||||
argp_error(state, "invalid port");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->port = (uint16_t)port;
|
||||
break;
|
||||
case ARGP_KEY_ARGS:
|
||||
opt->args = state->argv + state->next;
|
||||
opt->argCount = state->argc - state->next;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
struct options *opt = (struct options*)state->input;
|
||||
char* strEnd = NULL;
|
||||
unsigned int port;
|
||||
switch (key) {
|
||||
// Device settings:
|
||||
case 's': // --server=localhost
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid server");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->server = arg;
|
||||
break;
|
||||
case 'p': // --port=8888
|
||||
port = strtoul(arg, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || port < 1 || port > 65535) {
|
||||
argp_error(state, "invalid port");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->port = (uint16_t)port;
|
||||
break;
|
||||
case ARGP_KEY_ARGS:
|
||||
opt->args = state->argv + state->next;
|
||||
opt->argCount = state->argc - state->next;
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string fetchData(TCPSocket* socket, bool& listening) {
|
||||
char data[1024];
|
||||
ssize_t datalen;
|
||||
ostringstream ostream;
|
||||
string message, sendmessage;
|
||||
char data[1024];
|
||||
ssize_t datalen;
|
||||
ostringstream ostream;
|
||||
string message, sendmessage;
|
||||
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
|
||||
// set timeout
|
||||
tdiff.tv_sec = 0;
|
||||
tdiff.tv_nsec = 1E8;
|
||||
// set timeout
|
||||
tdiff.tv_sec = 0;
|
||||
tdiff.tv_nsec = 1E8;
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
int nfds = 2;
|
||||
struct pollfd fds[nfds];
|
||||
int nfds = 2;
|
||||
struct pollfd fds[nfds];
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
memset(fds, 0, sizeof(fds));
|
||||
|
||||
fds[0].fd = STDIN_FILENO;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].fd = STDIN_FILENO;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
fds[1].fd = socket->getFD();
|
||||
fds[1].events = POLLIN;
|
||||
fds[1].fd = socket->getFD();
|
||||
fds[1].events = POLLIN;
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
int maxfd;
|
||||
fd_set checkfds;
|
||||
int maxfd;
|
||||
fd_set checkfds;
|
||||
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(STDIN_FILENO, &checkfds);
|
||||
FD_SET(socket->getFD(), &checkfds);
|
||||
maxfd = STDIN_FILENO;
|
||||
if (socket->getFD() > maxfd) {
|
||||
maxfd = socket->getFD();
|
||||
}
|
||||
FD_ZERO(&checkfds);
|
||||
FD_SET(STDIN_FILENO, &checkfds);
|
||||
FD_SET(socket->getFD(), &checkfds);
|
||||
maxfd = STDIN_FILENO;
|
||||
if (socket->getFD() > maxfd) {
|
||||
maxfd = socket->getFD();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
while(true) {
|
||||
while(true) {
|
||||
#ifdef HAVE_PPOLL
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
// wait for new fd event
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
// set readfds to inital checkfds
|
||||
fd_set readfds = checkfds;
|
||||
// wait for new fd event
|
||||
ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool newData = false;
|
||||
bool newInput = false;
|
||||
if (ret != 0) {
|
||||
bool newData = false;
|
||||
bool newInput = false;
|
||||
if (ret != 0) {
|
||||
#ifdef HAVE_PPOLL
|
||||
// new data from notify
|
||||
newInput = fds[0].revents & POLLIN;
|
||||
// new data from notify
|
||||
newInput = fds[0].revents & POLLIN;
|
||||
|
||||
// new data from socket
|
||||
newData = fds[1].revents & POLLIN;
|
||||
// new data from socket
|
||||
newData = fds[1].revents & POLLIN;
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
// new data from notify
|
||||
newInput = FD_ISSET(STDIN_FILENO, &readfds);
|
||||
// new data from notify
|
||||
newInput = FD_ISSET(STDIN_FILENO, &readfds);
|
||||
|
||||
// new data from socket
|
||||
newData = FD_ISSET(socket->getFD(), &readfds);
|
||||
// new data from socket
|
||||
newData = FD_ISSET(socket->getFD(), &readfds);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (newData) {
|
||||
if (socket->isValid()) {
|
||||
datalen = socket->recv(data, sizeof(data));
|
||||
if (newData) {
|
||||
if (socket->isValid()) {
|
||||
datalen = socket->recv(data, sizeof(data));
|
||||
|
||||
if (datalen < 0) {
|
||||
perror("recv");
|
||||
break;
|
||||
}
|
||||
if (datalen < 0) {
|
||||
perror("recv");
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < datalen; i++) {
|
||||
ostream << data[i];
|
||||
}
|
||||
string str = ostream.str();
|
||||
if (listening) {
|
||||
return str;
|
||||
}
|
||||
if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') {
|
||||
return str;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (newInput) {
|
||||
getline(cin, message);
|
||||
sendmessage = message+'\n';
|
||||
socket->send(sendmessage.c_str(), sendmessage.size());
|
||||
for (int i = 0; i < datalen; i++) {
|
||||
ostream << data[i];
|
||||
}
|
||||
string str = ostream.str();
|
||||
if (listening) {
|
||||
return str;
|
||||
}
|
||||
if (str.length() >= 2 && str[str.length()-2] == '\n' && str[str.length()-1] == '\n') {
|
||||
return str;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (newInput) {
|
||||
getline(cin, message);
|
||||
sendmessage = message+'\n';
|
||||
socket->send(sendmessage.c_str(), sendmessage.size());
|
||||
|
||||
if (strcasecmp(message.c_str(), "Q") == 0
|
||||
|| strcasecmp(message.c_str(), "QUIT") == 0
|
||||
|| strcasecmp(message.c_str(), "STOP") == 0) {
|
||||
exit(EXIT_SUCCESS);
|
||||
return "";
|
||||
}
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
if (strcasecmp(message.c_str(), "Q") == 0
|
||||
|| strcasecmp(message.c_str(), "QUIT") == 0
|
||||
|| strcasecmp(message.c_str(), "STOP") == 0) {
|
||||
exit(EXIT_SUCCESS);
|
||||
return "";
|
||||
}
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return ostream.str();
|
||||
return ostream.str();
|
||||
}
|
||||
|
||||
void connect(const char* host, uint16_t port, char* const *args, int argCount) {
|
||||
TCPClient* client = new TCPClient();
|
||||
TCPSocket* socket = client->connect(host, port);
|
||||
TCPClient* client = new TCPClient();
|
||||
TCPSocket* socket = client->connect(host, port);
|
||||
|
||||
bool once = args != NULL && argCount > 0;
|
||||
if (socket != NULL) {
|
||||
string message, sendmessage;
|
||||
do {
|
||||
bool listening = false;
|
||||
bool once = args != NULL && argCount > 0;
|
||||
if (socket != NULL) {
|
||||
string message, sendmessage;
|
||||
do {
|
||||
bool listening = false;
|
||||
|
||||
if (!once) {
|
||||
cout << host << ": ";
|
||||
getline(cin, message);
|
||||
} else {
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
if (i > 0) {
|
||||
message += " ";
|
||||
}
|
||||
bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL;
|
||||
if (quote) {
|
||||
message += "\"";
|
||||
}
|
||||
message += args[i];
|
||||
if (quote) {
|
||||
message += "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!once) {
|
||||
cout << host << ": ";
|
||||
getline(cin, message);
|
||||
} else {
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
if (i > 0) {
|
||||
message += " ";
|
||||
}
|
||||
bool quote = strchr(args[i], ' ') != NULL && strchr(args[i], '"') == NULL;
|
||||
if (quote) {
|
||||
message += "\"";
|
||||
}
|
||||
message += args[i];
|
||||
if (quote) {
|
||||
message += "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendmessage = message+'\n';
|
||||
socket->send(sendmessage.c_str(), sendmessage.size());
|
||||
sendmessage = message+'\n';
|
||||
socket->send(sendmessage.c_str(), sendmessage.size());
|
||||
|
||||
if (strcasecmp(message.c_str(), "Q") == 0
|
||||
|| strcasecmp(message.c_str(), "QUIT") == 0
|
||||
|| strcasecmp(message.c_str(), "STOP") == 0)
|
||||
break;
|
||||
if (strcasecmp(message.c_str(), "Q") == 0
|
||||
|| strcasecmp(message.c_str(), "QUIT") == 0
|
||||
|| strcasecmp(message.c_str(), "STOP") == 0)
|
||||
break;
|
||||
|
||||
if (message.length() > 0) {
|
||||
if (strcasecmp(message.c_str(), "L") == 0
|
||||
|| strcasecmp(message.c_str(), "LISTEN") == 0) {
|
||||
listening = true;
|
||||
while (listening && !cin.eof()) {
|
||||
string result(fetchData(socket, listening));
|
||||
cout << result;
|
||||
if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cout << fetchData(socket, listening);
|
||||
}
|
||||
}
|
||||
} while (!once && !cin.eof());
|
||||
delete socket;
|
||||
} else {
|
||||
cout << "error connecting to " << host << ":" << port << endl;
|
||||
}
|
||||
delete client;
|
||||
if (message.length() > 0) {
|
||||
if (strcasecmp(message.c_str(), "L") == 0
|
||||
|| strcasecmp(message.c_str(), "LISTEN") == 0) {
|
||||
listening = true;
|
||||
while (listening && !cin.eof()) {
|
||||
string result(fetchData(socket, listening));
|
||||
cout << result;
|
||||
if (strcasecmp(result.c_str(), "LISTEN STOPPED") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cout << fetchData(socket, listening);
|
||||
}
|
||||
}
|
||||
} while (!once && !cin.eof());
|
||||
delete socket;
|
||||
} else {
|
||||
cout << "error connecting to " << host << ":" << port << endl;
|
||||
}
|
||||
delete client;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
|
||||
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
|
||||
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
connect(opt.server, opt.port, opt.args, opt.argCount);
|
||||
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
|
||||
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
|
||||
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
connect(opt.server, opt.port, opt.args, opt.argCount);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
+91
-91
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <argp.h>
|
||||
@@ -42,18 +42,18 @@ using ebusd::Device;
|
||||
|
||||
/** A structure holding all program options. */
|
||||
struct options {
|
||||
const char* device; //!< device to write to [/dev/ttyUSB60]
|
||||
unsigned int time; //!< delay between bytes in us [10000]
|
||||
const char* device; //!< device to write to [/dev/ttyUSB60]
|
||||
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. */
|
||||
static struct options opt = {
|
||||
"/dev/ttyUSB60", // device
|
||||
10000, // time
|
||||
"/dev/ttyUSB60", // device
|
||||
10000, // time
|
||||
|
||||
"/tmp/ebus_dump.bin", // dumpFile
|
||||
"/tmp/ebus_dump.bin", // dumpFile
|
||||
};
|
||||
|
||||
/** the version string of the program. */
|
||||
@@ -64,27 +64,27 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT"";
|
||||
|
||||
/** the documentation of the program. */
|
||||
static const char argpdoc[] =
|
||||
"Feed data from an "PACKAGE" DUMPFILE to a serial device.\n"
|
||||
"\v"
|
||||
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
|
||||
"\n"
|
||||
"Example for setting up two pseudo terminals with 'socat':\n"
|
||||
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
|
||||
" 2. create symbol links to appropriate devices, e.g.\n"
|
||||
" 'ln -s /dev/pts/2 /dev/ttyUSB60'\n"
|
||||
" 'ln -s /dev/pts/3 /dev/ttyUSB20'\n"
|
||||
" 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n"
|
||||
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n";
|
||||
"Feed data from an "PACKAGE" DUMPFILE to a serial device.\n"
|
||||
"\v"
|
||||
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
|
||||
"\n"
|
||||
"Example for setting up two pseudo terminals with 'socat':\n"
|
||||
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
|
||||
" 2. create symbol links to appropriate devices, e.g.\n"
|
||||
" 'ln -s /dev/pts/2 /dev/ttyUSB60'\n"
|
||||
" 'ln -s /dev/pts/3 /dev/ttyUSB20'\n"
|
||||
" 3. start "PACKAGE": '"PACKAGE" -f -d /dev/ttyUSB20 --nodevicecheck'\n"
|
||||
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n";
|
||||
|
||||
/** the description of the accepted arguments. */
|
||||
static char argpargsdoc[] = "[DUMPFILE]";
|
||||
|
||||
/** the definition of the known program arguments. */
|
||||
static const struct argp_option argpoptions[] = {
|
||||
{"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 },
|
||||
{"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 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 },
|
||||
|
||||
{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.
|
||||
*/
|
||||
error_t parse_opt(int key, char *arg, struct argp_state *state) {
|
||||
struct options *opt = (struct options*)state->input;
|
||||
char* strEnd = NULL;
|
||||
switch (key) {
|
||||
// Device settings:
|
||||
case 'd': // --device=/dev/ttyUSB60
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid device");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->device = arg;
|
||||
break;
|
||||
case 't': // --time=10000
|
||||
opt->time = (unsigned int)strtoul(arg, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) {
|
||||
argp_error(state, "invalid time");
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (state->arg_num == 0) {
|
||||
if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) {
|
||||
argp_error(state, "invalid dumpfile");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->dumpFile = arg;
|
||||
} else {
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
struct options *opt = (struct options*)state->input;
|
||||
char* strEnd = NULL;
|
||||
switch (key) {
|
||||
// Device settings:
|
||||
case 'd': // --device=/dev/ttyUSB60
|
||||
if (arg == NULL || arg[0] == 0) {
|
||||
argp_error(state, "invalid device");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->device = arg;
|
||||
break;
|
||||
case 't': // --time=10000
|
||||
opt->time = (unsigned int)strtoul(arg, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) {
|
||||
argp_error(state, "invalid time");
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (state->arg_num == 0) {
|
||||
if (arg == NULL || arg[0] == 0 || strcmp("/", arg) == 0) {
|
||||
argp_error(state, "invalid dumpfile");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->dumpFile = arg;
|
||||
} else {
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
|
||||
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
|
||||
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
Device* device = Device::create(opt.device, false, false, NULL);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device " << opt.device << endl;
|
||||
return EINVAL;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != ebusd::RESULT_OK) {
|
||||
cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl;
|
||||
}
|
||||
if (!device->isValid()) {
|
||||
cout << "device " << opt.device << " not available" << endl;
|
||||
} else {
|
||||
cout << "device opened" << endl;
|
||||
fstream file(opt.dumpFile, ios::in | ios::binary);
|
||||
if (file.is_open()) {
|
||||
while (true) {
|
||||
unsigned char byte = (unsigned char)file.get();
|
||||
if (file.eof()) {
|
||||
break;
|
||||
}
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
device->send(byte);
|
||||
usleep(opt.time);
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
cout << "error opening file " << opt.dumpFile << endl;
|
||||
}
|
||||
}
|
||||
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, NULL, NULL, NULL };
|
||||
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
|
||||
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
Device* device = Device::create(opt.device, false, false, NULL);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device " << opt.device << endl;
|
||||
return EINVAL;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != ebusd::RESULT_OK) {
|
||||
cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl;
|
||||
}
|
||||
if (!device->isValid()) {
|
||||
cout << "device " << opt.device << " not available" << endl;
|
||||
} else {
|
||||
cout << "device opened" << endl;
|
||||
fstream file(opt.dumpFile, ios::in | ios::binary);
|
||||
if (file.is_open()) {
|
||||
while (true) {
|
||||
unsigned char byte = (unsigned char)file.get();
|
||||
if (file.eof()) {
|
||||
break;
|
||||
}
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
device->send(byte);
|
||||
usleep(opt.time);
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
cout << "error opening file " << opt.dumpFile << endl;
|
||||
}
|
||||
}
|
||||
|
||||
delete device;
|
||||
exit(EXIT_SUCCESS);
|
||||
delete device;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user