added support for UDP devices, added buffer for network devices, added option for sending an initial symbol after opening a network device, added option for setting transfer latency and use default 10 ms for network device, set TCP_NODELAY for TCP device socket, flush potentially buffered input on network device when connecting

This commit is contained in:
john30
2016-05-05 12:39:52 +02:00
parent 516bbe0edf
commit d5b07f5783
9 changed files with 220 additions and 56 deletions
+6 -6
View File
@@ -313,7 +313,7 @@ result_t BusHandler::handleSymbol()
break; break;
case bs_recvResAck: case bs_recvResAck:
timeout = m_slaveRecvTimeout; timeout = m_slaveRecvTimeout+m_transferLatency;
break; break;
case bs_sendCmd: case bs_sendCmd:
@@ -354,12 +354,12 @@ result_t BusHandler::handleSymbol()
result_t result; result_t result;
if (sending) { if (sending) {
result = m_device->send(sendSymbol); result = m_device->send(sendSymbol);
if (result == RESULT_OK) if (result == RESULT_OK) {
if (m_state == bs_ready) if (m_state == bs_ready)
timeout = m_busAcquireTimeout; timeout = m_transferLatency+m_busAcquireTimeout;
else else
timeout = SEND_TIMEOUT; timeout = m_transferLatency+SEND_TIMEOUT;
else { } else {
sending = false; sending = false;
timeout = SYN_TIMEOUT; timeout = SYN_TIMEOUT;
if (startRequest != NULL && m_nextRequests.remove(startRequest)) { if (startRequest != NULL && m_nextRequests.remove(startRequest)) {
@@ -371,7 +371,7 @@ result_t BusHandler::handleSymbol()
// receive next symbol (optionally check reception of sent symbol) // receive next symbol (optionally check reception of sent symbol)
unsigned char recvSymbol; unsigned char recvSymbol;
result = m_device->recv(timeout, recvSymbol); result = m_device->recv(timeout+m_transferLatency, recvSymbol);
if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
+7 -3
View File
@@ -303,8 +303,9 @@ public:
* @param answer whether to answer queries for the own master/slave address. * @param answer whether to answer queries for the own master/slave address.
* @param busLostRetries the number of times a send is repeated due to lost arbitration. * @param busLostRetries the number of times a send is repeated due to lost arbitration.
* @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration). * @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration).
* @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge. * @param transferLatency the bus transfer latency in microseconds.
* @param busAcquireTimeout the maximum time in microseconds for bus acquisition. * @param busAcquireTimeout the maximum time in microseconds for bus acquisition.
* @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge.
* @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection.
* @param generateSyn whether to enable AUTO-SYN symbol generation. * @param generateSyn whether to enable AUTO-SYN symbol generation.
* @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled. * @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled.
@@ -312,13 +313,13 @@ public:
BusHandler(Device* device, MessageMap* messages, BusHandler(Device* device, MessageMap* messages,
const unsigned char ownAddress, const bool answer, const unsigned char ownAddress, const bool answer,
const unsigned int busLostRetries, const unsigned int failedSendRetries, const unsigned int busLostRetries, const unsigned int failedSendRetries,
const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout, const unsigned int transferLatency, const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout,
const unsigned int lockCount, const bool generateSyn, const unsigned int lockCount, const bool generateSyn,
const unsigned int pollInterval) const unsigned int pollInterval)
: m_device(device), m_messages(messages), : m_device(device), m_messages(messages),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress((unsigned char)(ownAddress+5)), m_answer(answer), m_ownMasterAddress(ownAddress), m_ownSlaveAddress((unsigned char)(ownAddress+5)), m_answer(answer),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
m_masterCount(1), m_autoLockCount(lockCount==0), m_lockCount(lockCount<=3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount), m_masterCount(1), m_autoLockCount(lockCount==0), m_lockCount(lockCount<=3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount),
m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0), m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0),
m_pollInterval(pollInterval), m_lastReceive(0), m_lastPoll(0), m_pollInterval(pollInterval), m_lastReceive(0), m_lastPoll(0),
@@ -496,6 +497,9 @@ private:
/** the number of times a failed send is repeated (other than lost arbitration). */ /** the number of times a failed send is repeated (other than lost arbitration). */
const unsigned int m_failedSendRetries; const unsigned int m_failedSendRetries;
/** the bus transfer latency in microseconds. */
const unsigned int m_transferLatency;
/** the maximum time in microseconds for bus acquisition. */ /** the maximum time in microseconds for bus acquisition. */
const unsigned int m_busAcquireTimeout; const unsigned int m_busAcquireTimeout;
+24 -8
View File
@@ -65,7 +65,9 @@ static bool isDaemon = false;
static struct options opt = { static struct options opt = {
"/dev/ttyUSB0", // device "/dev/ttyUSB0", // device
false, // noDeviceCheck false, // noDeviceCheck
false, // readonly false, // readOnly
false, // initialSend
-1, // latency
CONFIG_PATH, // configPath CONFIG_PATH, // configPath
false, // scanConfig false, // scanConfig
0, // checkConfig 0, // checkConfig
@@ -108,7 +110,9 @@ const char *argp_program_bug_address = ""PACKAGE_BUGREPORT"";
static const char argpdoc[] = static const char argpdoc[] =
"A daemon for communication with eBUS heating systems."; "A daemon for communication with eBUS heating systems.";
#define O_CHKCFG 1 #define O_INISND 1
#define O_DEVLAT (O_INISND+1)
#define O_CHKCFG (O_DEVLAT+1)
#define O_DMPCFG (O_CHKCFG+1) #define O_DMPCFG (O_CHKCFG+1)
#define O_POLINT (O_DMPCFG+1) #define O_POLINT (O_DMPCFG+1)
#define O_ANSWER (O_POLINT+1) #define O_ANSWER (O_POLINT+1)
@@ -132,9 +136,11 @@ static const char argpdoc[] =
/** the definition of the known program arguments. */ /** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = { static const struct argp_option argpoptions[] = {
{NULL, 0, NULL, 0, "Device options:", 1 }, {NULL, 0, NULL, 0, "Device options:", 1 },
{"device", 'd', "DEV", 0, "Use DEV as eBUS device (serial or ip:port) [/dev/ttyUSB0]", 0 }, {"device", 'd', "DEV", 0, "Use DEV as eBUS device (serial or [udp:]ip:port) [/dev/ttyUSB0]", 0 },
{"nodevicecheck", 'n', NULL, 0, "Skip serial eBUS device test", 0 }, {"nodevicecheck", 'n', NULL, 0, "Skip serial eBUS device test", 0 },
{"readonly", 'r', NULL, 0, "Only read from device, never write to it", 0 }, {"readonly", 'r', NULL, 0, "Only read from device, never write to it", 0 },
{"initsend", O_INISND, NULL, 0, "Send an initial escape symbol after connecting device", 0 },
{"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 },
{NULL, 0, NULL, 0, "Message configuration options:", 2 }, {NULL, 0, NULL, 0, "Message configuration options:", 2 },
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH [" CONFIG_PATH "]", 0 }, {"configpath", 'c', "PATH", 0, "Read CSV config files from PATH [" CONFIG_PATH "]", 0 },
@@ -209,12 +215,22 @@ error_t parse_opt(int key, char *arg, struct argp_state *state)
opt->noDeviceCheck = true; opt->noDeviceCheck = true;
break; break;
case 'r': // --readonly case 'r': // --readonly
opt->readonly = true; opt->readOnly = true;
if (opt->scanConfig || opt->answer || opt->generateSyn) { if (opt->scanConfig || opt->answer || opt->generateSyn) {
argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn");
return EINVAL; return EINVAL;
} }
break; break;
case O_INISND: // --initsend
opt->initialSend = true;
break;
case O_DEVLAT: // --latency
opt->latency = parseInt(arg, 10, 0, 200000, result);
if (result != RESULT_OK) {
argp_error(state, "invalid latency");
return EINVAL;
}
break;
// Message configuration options: // Message configuration options:
case 'c': // --configpath=/etc/ebusd case 'c': // --configpath=/etc/ebusd
@@ -226,7 +242,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state)
break; break;
case 's': // --scanconfig case 's': // --scanconfig
opt->scanConfig = true; opt->scanConfig = true;
if (opt->readonly) { if (opt->readOnly) {
argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn");
return EINVAL; return EINVAL;
} }
@@ -256,7 +272,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state)
break; break;
case O_ANSWER: // --answer case O_ANSWER: // --answer
opt->answer = true; opt->answer = true;
if (opt->readonly) { if (opt->readOnly) {
argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn");
return EINVAL; return EINVAL;
} }
@@ -298,7 +314,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state)
break; break;
case O_GENSYN: // --generatesyn case O_GENSYN: // --generatesyn
opt->generateSyn = true; opt->generateSyn = true;
if (opt->readonly) { if (opt->readOnly) {
argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn"); argp_error(state, "cannot combine readonly with scanconfig/answer/generatesyn");
return EINVAL; return EINVAL;
} }
@@ -929,7 +945,7 @@ int main(int argc, char* argv[])
return EINVAL; return EINVAL;
// open the device // open the device
Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readonly, &logRawData); Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend, &logRawData);
if (device == NULL) { if (device == NULL) {
logError(lf_main, "unable to create device %s", opt.device); logError(lf_main, "unable to create device %s", opt.device);
return EINVAL; return EINVAL;
+4 -2
View File
@@ -29,9 +29,11 @@
/** A structure holding all program options. */ /** A structure holding all program options. */
struct options struct options
{ {
const char* device; //!< eBUS device (serial device or ip:port) [/dev/ttyUSB0] const char* device; //!< eBUS device (serial device or [udp:]ip:port) [/dev/ttyUSB0]
bool noDeviceCheck; //!< skip serial eBUS device test bool noDeviceCheck; //!< skip serial eBUS device test
bool readonly; //!< read-only access to the device bool readOnly; //!< read-only access to the device
bool initialSend; //!< send an initial escape symbol after connecting device
int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
const char* configPath; //!< path to CSV configuration files [/etc/ebusd] const char* configPath; //!< path to CSV configuration files [/etc/ebusd]
bool scanConfig; //!< pick configuration files matching initial scan bool scanConfig; //!< pick configuration files matching initial scan
+7 -1
View File
@@ -58,10 +58,16 @@ MainLoop::MainLoop(const struct options opt, Device *device, MessageMap* message
logError(lf_bus, "device %s not available", m_device->getName()); logError(lf_bus, "device %s not available", m_device->getName());
// create BusHandler // create BusHandler
unsigned int latency;
if (opt.latency<0) {
latency = device->getLatency();
} else {
latency = (unsigned int)opt.latency;
}
m_busHandler = new BusHandler(m_device, m_messages, m_busHandler = new BusHandler(m_device, m_messages,
m_address, opt.answer, m_address, opt.answer,
opt.acquireRetries, opt.sendRetries, opt.acquireRetries, opt.sendRetries,
opt.acquireTimeout, opt.receiveTimeout, latency, opt.acquireTimeout, opt.receiveTimeout,
opt.masterCount, opt.generateSyn, opt.masterCount, opt.generateSyn,
opt.pollInterval); opt.pollInterval);
m_busHandler->start("bushandler"); m_busHandler->start("bushandler");
+93 -20
View File
@@ -22,13 +22,15 @@
#include "device.h" #include "device.h"
#include "data.h" #include "data.h"
#include <unistd.h>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fcntl.h> #include <fcntl.h>
#include <fstream> #include <fstream>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h> #include <errno.h>
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
@@ -43,36 +45,45 @@ Device::~Device()
m_dumpRawStream.close(); m_dumpRawStream.close();
} }
Device* Device::create(const char* name, const bool checkDevice, const bool readonly, Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend,
void (*logRawFunc)(const unsigned char byte, bool received)) void (*logRawFunc)(const unsigned char byte, bool received))
{ {
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) { if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
char* dup = strdup(name); char* in = strdup(name);
char* pos = strchr(dup, ':'); 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; result_t result = RESULT_OK;
unsigned int port = parseInt(pos+1, 10, 1, 65535, result); unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
if (result!=RESULT_OK) { if (result!=RESULT_OK) {
free(dup); free(in);
return NULL; // invalid port return NULL; // invalid port
} }
struct sockaddr_in address; struct sockaddr_in address;
memset((char*)&address, 0, sizeof(address)); memset((char*)&address, 0, sizeof(address));
*portpos = 0;
*pos = 0; if (inet_aton(addrpos, &address.sin_addr) == 0) {
if (inet_aton(dup, &address.sin_addr) == 0) { struct hostent* h = gethostbyname(addrpos);
struct hostent* h = gethostbyname(dup);
if (h == NULL) { if (h == NULL) {
free(dup); free(in);
return NULL; // invalid host return NULL; // invalid host
} }
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length); memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
} }
free(dup); free(in);
address.sin_family = AF_INET; address.sin_family = AF_INET;
address.sin_port = (in_port_t)htons((uint16_t)port); address.sin_port = (in_port_t)htons((uint16_t)port);
return new NetworkDevice(name, address, readonly, logRawFunc); return new NetworkDevice(name, address, readOnly, initialSend, logRawFunc, udp);
} }
return new SerialDevice(name, checkDevice, readonly, logRawFunc); return new SerialDevice(name, checkDevice, readOnly, initialSend, logRawFunc);
} }
void Device::close() void Device::close()
@@ -99,7 +110,7 @@ result_t Device::send(const unsigned char value)
if (!isValid()) if (!isValid())
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
if (m_readonly || write(m_fd, &value, 1) != 1) if (m_readOnly || write(value) != 1)
return RESULT_ERR_SEND; return RESULT_ERR_SEND;
if (m_logRaw && m_logRawFunc != NULL) if (m_logRaw && m_logRawFunc != NULL)
@@ -113,7 +124,7 @@ result_t Device::recv(const long timeout, unsigned char& value)
if (!isValid()) if (!isValid())
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
if (timeout > 0) { if (!available() && timeout > 0) {
int ret; int ret;
struct timespec tdiff; struct timespec tdiff;
@@ -148,7 +159,7 @@ result_t Device::recv(const long timeout, unsigned char& value)
} }
// directly read byte from device // directly read byte from device
ssize_t nbytes = read(m_fd, &value, 1); ssize_t nbytes = read(value);
if (nbytes == 0) if (nbytes == 0)
return RESULT_ERR_EOF; return RESULT_ERR_EOF;
if (nbytes < 0) if (nbytes < 0)
@@ -251,6 +262,9 @@ result_t SerialDevice::open()
// set serial device into blocking mode // set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK; return RESULT_OK;
} }
@@ -281,17 +295,43 @@ result_t NetworkDevice::open()
if (m_fd != -1) if (m_fd != -1)
close(); close();
int ret; m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
m_fd = socket(AF_INET, SOCK_STREAM, 0);
if (m_fd < 0) if (m_fd < 0)
return RESULT_ERR_GENERIC_IO; 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, (void*)&value, sizeof(value));
}
if (ret==0) {
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address)); ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
}
if (ret < 0) { if (ret < 0) {
close(); close();
return RESULT_ERR_GENERIC_IO; return RESULT_ERR_GENERIC_IO;
} }
int cnt;
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; return RESULT_OK;
} }
@@ -300,6 +340,39 @@ void NetworkDevice::checkDevice()
unsigned char value; unsigned char value;
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT); ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
if (c == 0 || (c < 0 && errno != EAGAIN)) { if (c == 0 || (c < 0 && errno != EAGAIN)) {
m_bufLen = 0; // flush read buffer
close(); close();
} }
} }
bool NetworkDevice::available()
{
return m_buffer && m_bufLen>0;
}
ssize_t NetworkDevice::write(const unsigned char 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);
}
+76 -13
View File
@@ -19,6 +19,7 @@
#ifndef LIBEBUS_DEVICE_H_ #ifndef LIBEBUS_DEVICE_H_
#define LIBEBUS_DEVICE_H_ #define LIBEBUS_DEVICE_H_
#include <unistd.h>
#include <termios.h> #include <termios.h>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
@@ -48,12 +49,13 @@ public:
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readonly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param logRawFunc the function to call for logging raw data, or NULL. * @param logRawFunc the function to call for logging raw data, or NULL.
*/ */
Device(const char* name, const bool checkDevice, const bool readonly, Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend,
void (*logRawFunc)(const unsigned char byte, bool received)) void (*logRawFunc)(const unsigned char byte, bool received))
: m_name(name), m_checkDevice(checkDevice), m_readonly(readonly), m_fd(-1), : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
m_logRaw(false), m_logRawFunc(logRawFunc), m_logRaw(false), m_logRawFunc(logRawFunc),
m_dumpRaw(false), m_dumpRawFile(NULL), m_dumpRawMaxSize(0), m_dumpRawStream(), m_dumpRawFileSize(0) {} m_dumpRaw(false), m_dumpRawFile(NULL), m_dumpRawMaxSize(0), m_dumpRawStream(), m_dumpRawFileSize(0) {}
@@ -66,14 +68,21 @@ public:
* Factory method for creating a new instance. * Factory method for creating a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readonly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param logRawFunc the function to call for logging raw data, or NULL. * @param logRawFunc the function to call for logging raw data, or NULL.
* @return the new @a Device, or NULL on error. * @return the new @a Device, or NULL on error.
* Note: the caller needs to free the created instance. * Note: the caller needs to free the created instance.
*/ */
static Device* create(const char* name, const bool checkDevice=true, const bool readonly=false, static Device* create(const char* name, const bool checkDevice=true, const bool readOnly=false, const bool initialSend=false,
void (*logRawFunc)(const unsigned char byte, bool received)=NULL); void (*logRawFunc)(const unsigned char byte, bool received)=NULL);
/**
* Get the transfer latency of this device.
* @return the transfer latency in microseconds.
*/
virtual unsigned int getLatency() const { return 0; }
/** /**
* Open the file descriptor. * Open the file descriptor.
* @return the @a result_t code. * @return the @a result_t code.
@@ -154,6 +163,26 @@ protected:
*/ */
virtual void checkDevice() = 0; // abstract 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; }
/**
* 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); }
protected: protected:
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
const char* m_name; const char* m_name;
@@ -162,7 +191,10 @@ protected:
const bool m_checkDevice; const bool m_checkDevice;
/** whether to allow read access to the device only. */ /** whether to allow read access to the device only. */
const bool m_readonly; const bool m_readOnly;
/** whether to send an initial @a ESC symbol in @a open(). */
const bool m_initialSend;
/** the opened file descriptor, or -1. */ /** the opened file descriptor, or -1. */
int m_fd; int m_fd;
@@ -201,12 +233,13 @@ public:
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readonly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param logRawFunc the function to call for logging raw data, or NULL. * @param logRawFunc the function to call for logging raw data, or NULL.
*/ */
SerialDevice(const char* name, const bool checkDevice, const bool readonly, SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend,
void (*logRawFunc)(const unsigned char byte, bool received)) void (*logRawFunc)(const unsigned char byte, bool received))
: Device(name, checkDevice, readonly, logRawFunc) {} : Device(name, checkDevice, readOnly, initialSend, logRawFunc) {}
// @copydoc // @copydoc
virtual result_t open(); virtual result_t open();
@@ -234,12 +267,18 @@ public:
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param address the socket address of the device. * @param address the socket address of the device.
* @param readonly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param logRawFunc the function to call for logging raw data, or NULL. * @param logRawFunc the function to call for logging raw data, or NULL.
* @param udp true for UDP, false to TCP.
*/ */
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readonly, NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
void (*logRawFunc)(const unsigned char byte, bool received)) void (*logRawFunc)(const unsigned char byte, bool received), const bool udp)
: Device(name, true, readonly, logRawFunc), m_address(address) {} : Device(name, true, readOnly, initialSend, logRawFunc), 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 // @copydoc
virtual result_t open(); virtual result_t open();
@@ -248,10 +287,34 @@ protected:
// @copydoc // @copydoc
virtual void checkDevice(); virtual void checkDevice();
// @copydoc
virtual bool available();
// @copydoc
virtual ssize_t write(const unsigned char value);
// @copydoc
virtual ssize_t read(unsigned char& value);
private: private:
/** the socket address of the device. */ /** the socket address of the device. */
const struct sockaddr_in m_address; const struct sockaddr_in m_address;
/** true for UDP, false to TCP. */
const bool m_udp;
/** the buffer memory, or NULL. */
unsigned char* m_buffer;
/** the buffer size. */
unsigned char m_bufSize;
/** the buffer fill length. */
unsigned char m_bufLen;
/** the buffer read position. */
unsigned char m_bufPos;
}; };
#endif // LIBEBUS_DEVICE_H_ #endif // LIBEBUS_DEVICE_H_
+1 -1
View File
@@ -24,7 +24,7 @@ using namespace std;
int main () int main ()
{ {
Device* device = Device::create("/dev/ttyUSB20", true, NULL); Device* device = Device::create("/dev/ttyUSB20", true, false, NULL);
if (device == NULL) { if (device == NULL) {
cout << "unable to create device" << endl; cout << "unable to create device" << endl;
return -1; return -1;
+1 -1
View File
@@ -129,7 +129,7 @@ int main(int argc, char* argv[])
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0) if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0)
return EINVAL; return EINVAL;
Device* device = Device::create(opt.device, false, NULL); Device* device = Device::create(opt.device, false, false, NULL);
if (device == NULL) { if (device == NULL) {
cout << "unable to create device " << opt.device << endl; cout << "unable to create device " << opt.device << endl;
return EINVAL; return EINVAL;