reworked and optimized device handling and check network device address, log failed device open, avoid blocking threads with sleep(), optimized shutdown

This commit is contained in:
john30
2015-02-08 10:06:03 +01:00
parent 4404202609
commit aa306d9131
17 changed files with 664 additions and 735 deletions
+11 -10
View File
@@ -23,6 +23,7 @@
#include "result.h"
#include "symbol.h"
#include "log.h"
#include <unistd.h>
#include <string>
#include <vector>
#include <deque>
@@ -171,18 +172,18 @@ result_t BusHandler::sendAndWait(SymbolString& master, SymbolString& slave)
void BusHandler::run()
{
do {
if (m_port->isOpen() == true)
if (m_device->isValid() == true)
handleSymbol();
else {
// TODO define max reopen
sleep(10);
result_t result = m_port->open();
if (result != RESULT_OK)
logError(lf_bus, "can't open %s", m_port->getDeviceName());
if (Wait(10) == false)
break;
result_t result = m_device->open();
if (result == RESULT_OK)
logNotice(lf_bus, "re-opened %s", m_device->getName());
else
logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result));
}
} while (isRunning() == true);
}
@@ -289,7 +290,7 @@ result_t BusHandler::handleSymbol()
// send symbol if necessary
result_t result;
if (sending == true) {
result = m_port->send(sendSymbol);
result = m_device->send(sendSymbol);
if (result == RESULT_OK)
if (m_state == bs_ready)
timeout = m_busAcquireTimeout;
@@ -304,7 +305,7 @@ result_t BusHandler::handleSymbol()
// receive next symbol (optionally check reception of sent symbol)
unsigned char recvSymbol;
result = m_port->recv(timeout, recvSymbol);
result = m_device->recv(timeout, recvSymbol);
time_t now;
time(&now);
+8 -7
View File
@@ -24,7 +24,7 @@
#include "data.h"
#include "symbol.h"
#include "result.h"
#include "port.h"
#include "device.h"
#include "wqueue.h"
#include "thread.h"
#include <string>
@@ -244,13 +244,13 @@ private:
/**
* Handles input from and output to the bus with respect to the eBUS protocol.
*/
class BusHandler : public Thread
class BusHandler : public WaitThread
{
public:
/**
* Construct a new instance.
* @param port the @a Port instance for accessing the bus.
* @param device the @a Device instance for accessing the bus.
* @param messages the @a MessageMap instance with all known @a Message instances.
* @param ownAddress the own master address.
* @param answer whether to answer queries for the own master/slave address.
@@ -261,12 +261,12 @@ public:
* @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration.
* @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled.
*/
BusHandler(Port* port, MessageMap* messages,
BusHandler(Device* device, MessageMap* messages,
const unsigned char ownAddress, const bool answer,
const unsigned int busLostRetries, const unsigned int failedSendRetries,
const unsigned int busAcquireTimeout, const unsigned int slaveRecvTimeout,
const unsigned int lockCount, const unsigned int pollInterval)
: m_port(port), m_messages(messages),
: m_device(device), m_messages(messages),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress((ownAddress+5)&0xff), m_answer(answer),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
@@ -283,6 +283,7 @@ public:
* Destructor.
*/
virtual ~BusHandler() {
stop();
if (m_scanMessage != NULL)
delete m_scanMessage;
}
@@ -348,8 +349,8 @@ private:
*/
void receiveCompleted();
/** the @a Port instance for accessing the bus. */
Port* m_port;
/** the @a Device instance for accessing the bus. */
Device* m_device;
/** the @a MessageMap instance with all known @a Message instances. */
MessageMap* m_messages;
+21 -1
View File
@@ -481,6 +481,19 @@ result_t loadConfigFiles(DataFieldTemplates* templates, MessageMap* messages, bo
}
/**
* Create a log message for a received/sent raw data byte.
* @param byte the raw data byte.
* @param received true if the byte was received, false if it was sent.
*/
static void logRawData(const unsigned char byte, bool received)
{
if (received == true)
logNotice(lf_bus, "<%02x", byte);
else
logNotice(lf_bus, ">%02x", byte);
}
/**
* Main method.
*
@@ -507,6 +520,13 @@ int main(int argc, char* argv[])
return 0;
}
// open the device
Device *device = Device::create(opt.device, opt.noDeviceCheck==false, &logRawData);
if (device == NULL) {
logError(lf_main, "unable to create device %s", opt.device);
return EINVAL;
}
if (opt.foreground == false) {
setLogFile(opt.logFile);
daemonize(); // make me daemon
@@ -523,7 +543,7 @@ int main(int argc, char* argv[])
loadConfigFiles(&templates, &messages);
// create the MainLoop and run it
mainLoop = new MainLoop(opt, &templates, &messages);
mainLoop = new MainLoop(opt, device, &templates, &messages);
mainLoop->run();
// shutdown
+21 -26
View File
@@ -26,18 +26,24 @@
using namespace std;
MainLoop::MainLoop(const struct options opt, DataFieldTemplates* templates, MessageMap* messages)
: m_templates(templates), m_messages(messages), m_address(opt.address)
MainLoop::MainLoop(const struct options opt, Device *device, DataFieldTemplates* templates, MessageMap* messages)
: m_device(device), m_templates(templates), m_messages(messages), m_address(opt.address)
{
// create Port
m_port = new Port(opt.device, opt.noDeviceCheck, opt.logRaw, &logRaw, opt.dump, opt.dumpFile, opt.dumpSize);
m_port->open();
// setup Device
m_device->setLogRaw(opt.logRaw);
m_device->setDumpRawFile(opt.dumpFile);
m_device->setDumpRawMaxSize(opt.dumpSize);
m_device->setDumpRaw(opt.dump);
if (m_port->isOpen() == false)
logError(lf_bus, "can't open %s", m_port->getDeviceName());
// open Device
result_t result = m_device->open();
if (result != RESULT_OK)
logError(lf_bus, "unable to open %s: %s", m_device->getName(), getResultCode(result));
else if (m_device->isValid() == false)
logError(lf_bus, "device %s not available", m_device->getName());
// create BusHandler
m_busHandler = new BusHandler(m_port, m_messages,
m_busHandler = new BusHandler(m_device, m_messages,
m_address, opt.answer,
opt.acquireRetries, opt.sendRetries,
opt.acquireTimeout, opt.receiveTimeout,
@@ -55,17 +61,13 @@ MainLoop::~MainLoop()
delete m_network;
m_network = NULL;
}
if (m_busHandler != NULL) {
m_busHandler->stop();
m_busHandler->join();
delete m_busHandler;
m_busHandler = NULL;
}
if (m_port != NULL) {
delete m_port;
m_port = NULL;
if (m_device != NULL) {
delete m_device;
m_device = NULL;
}
m_messages->clear();
@@ -109,13 +111,6 @@ void MainLoop::run()
}
}
void MainLoop::logRaw(const unsigned char byte, bool received) {
if (received == true)
logNotice(lf_bus, "<%02x", byte);
else
logNotice(lf_bus, ">%02x", byte);
}
string MainLoop::decodeMessage(const string& data, bool& connected, bool& listening, bool& running)
{
ostringstream result;
@@ -570,8 +565,8 @@ string MainLoop::executeRaw(vector<string> &args)
return "usage: 'raw'\n"
" Toggle log raw data.";
bool enabled = !m_port->getLogRaw();
m_port->setLogRaw(enabled);
bool enabled = !m_device->getLogRaw();
m_device->setLogRaw(enabled);
return enabled ? "raw output enabled" : "raw output disabled";
}
@@ -582,8 +577,8 @@ string MainLoop::executeDump(vector<string> &args)
return "usage: 'dump'\n"
" Toggle raw dump.";
bool enabled = !m_port->getDumpRaw();
m_port->setDumpRaw(enabled);
bool enabled = !m_device->getDumpRaw();
m_device->setDumpRaw(enabled);
return enabled ? "dump enabled" : "dump disabled";
}
+5 -11
View File
@@ -38,10 +38,11 @@ public:
/**
* Construct the main loop and create network and bus handling components.
* @param opt the program options.
* @param device the @a Device instance.
* @param templates the @a DataFieldTemplates instance.
* @param messages the @a MessageMap instance.
*/
MainLoop(const struct options opt, DataFieldTemplates* templates, MessageMap* messages);
MainLoop(const struct options opt, Device *device, DataFieldTemplates* templates, MessageMap* messages);
/**
* Destructor.
@@ -59,15 +60,11 @@ public:
*/
void addMessage(NetMessage* message) { m_netQueue.add(message); }
/**
* Create a log message for a received/sent raw data byte.
* @param byte the raw data byte.
* @param received true if the byte was received, false if it was sent.
*/
static void logRaw(const unsigned char byte, bool received);
private:
/** the @a Device instance. */
Device* m_device;
/** the @a DataFieldTemplates instance. */
DataFieldTemplates* m_templates;
@@ -77,9 +74,6 @@ private:
/** the own master address for sending on the bus. */
unsigned char m_address;
/** the created @a Port instance. */
Port* m_port;
/** the created @a BusHandler instance. */
BusHandler* m_busHandler;
+4 -3
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>,
* John Baier 2014-2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
@@ -165,6 +166,8 @@ Network::Network(const bool local, const int port, WQueue<NetMessage*>* netQueue
Network::~Network()
{
stop();
while (m_connections.empty() == false) {
Connection* connection = m_connections.back();
m_connections.pop_back();
@@ -172,8 +175,6 @@ Network::~Network()
connection->join();
delete connection;
}
stop();
join();
if (m_tcpServer != NULL)
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>,
* John Baier 2014-2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
+2 -2
View File
@@ -11,8 +11,8 @@ libebus_a_SOURCES = result.cpp \
symbol.h \
data.cpp \
data.h \
port.cpp \
port.h \
device.cpp \
device.h \
message.cpp \
message.h
+295
View File
@@ -0,0 +1,295 @@
/*
* Copyright (C) John Baier 2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
* ebusd is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ebusd is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ebusd. If not, see http://www.gnu.org/licenses/.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "device.h"
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <sys/ioctl.h>
#include <errno.h>
#ifdef HAVE_PPOLL
#include <poll.h>
#endif
using namespace std;
Device::~Device()
{
close();
m_dumpRawStream.close();
}
Device* Device::create(const char* name, const bool checkDevice,
void (*logRawFunc)(const unsigned char byte, bool received))
{
if (strchr(name, '/') == NULL) {
char* pos = strchr((char*)name, ':');
if (pos != NULL) {
char* end = NULL;
unsigned int port = strtoul(pos+1, &end, 10);
if (end == NULL || *end != 0 || port < 1 || port > 65535) {
return NULL; // invalid port
}
struct sockaddr_in address;
memset((char*)&address, 0, sizeof(address));
char* host = strndup(name, pos-name);
if (inet_aton(host, &address.sin_addr) == 0) {
struct hostent* h = gethostbyname(host);
if (h == NULL) {
free(host);
return NULL; // invalid host
}
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
}
free(host);
address.sin_family = AF_INET;
address.sin_port = htons(port);
return new NetworkDevice(name, address, logRawFunc);
}
}
return new SerialDevice(name, checkDevice, logRawFunc);
}
void Device::close()
{
if (m_fd != -1) {
::close(m_fd);
m_fd = -1;
}
}
bool Device::isValid()
{
if (m_fd == -1)
return false;
if (m_checkDevice == true)
checkDevice();
return m_fd != -1;
}
result_t Device::send(const unsigned char value)
{
if (isValid() == false)
return RESULT_ERR_DEVICE;
if (write(m_fd, &value, 1) != 1)
return RESULT_ERR_SEND;
if (m_logRaw == true && m_logRawFunc != NULL)
(*m_logRawFunc)(value, false);
return RESULT_OK;
}
result_t Device::recv(const long timeout, unsigned char& value)
{
if (isValid() == false)
return RESULT_ERR_DEVICE;
if (timeout > 0) {
int ret;
struct timespec tdiff;
// set select timeout
tdiff.tv_sec = 0;
tdiff.tv_nsec = timeout*1000;
#ifdef HAVE_PPOLL
int nfds = 1;
struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd;
fds[0].events = POLLIN;
ret = ppoll(fds, nfds, &tdiff, NULL);
#else
#ifdef HAVE_PSELECT
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL);
#else
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;
}
// directly read byte from device
ssize_t nbytes = read(m_fd, &value, 1);
if (nbytes == 0)
return RESULT_ERR_EOF;
if (nbytes < 0)
return RESULT_ERR_DEVICE;
if (m_logRaw == true && m_logRawFunc != NULL)
(*m_logRawFunc)(value, true);
if (m_dumpRaw == true && m_dumpRawStream.is_open() == true) {
m_dumpRawStream.write((char*)&value, 1);
m_dumpRawFileSize++;
if ((m_dumpRawFileSize%1024) == 0)
m_dumpRawStream.flush();
if (m_dumpRawFileSize >= m_dumpRawMaxSize * 1024) {
string oldfile = string(m_dumpRawFile) + ".old";
if (rename(m_dumpRawFile, oldfile.c_str()) == 0) {
m_dumpRawStream.close();
m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
}
return RESULT_OK;
}
void Device::setDumpRaw(bool dumpRaw)
{
if (dumpRaw == m_dumpRaw)
return;
m_dumpRaw = dumpRaw;
if (dumpRaw == false || m_dumpRawFile == NULL)
m_dumpRawStream.close();
else {
m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
void Device::setDumpRawFile(const char* dumpFile) {
if ((dumpFile == NULL) ? (m_dumpRawFile == NULL) : (m_dumpRawFile != NULL && (m_dumpRawFile == dumpFile || strcmp(dumpFile, m_dumpRawFile) == 0)))
return;
m_dumpRawStream.close();
m_dumpRawFile = dumpFile;
if (m_dumpRaw == true && m_dumpRawFile != NULL) {
m_dumpRawStream.open(m_dumpRawFile, ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
result_t SerialDevice::open()
{
if (m_fd != -1)
close();
struct termios newSettings;
// open file descriptor
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
if (m_fd < 0 || isatty(m_fd) == 0)
return RESULT_ERR_NOTFOUND;
// save current settings
tcgetattr(m_fd, &m_oldSettings);
// 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;
// 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);
// 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);
return RESULT_OK;
}
void SerialDevice::close()
{
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();
}
void SerialDevice::checkDevice()
{
int port;
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
close();
}
}
result_t NetworkDevice::open()
{
if (m_fd != -1)
close();
int ret;
m_fd = socket(AF_INET, SOCK_STREAM, 0);
if (m_fd < 0)
return RESULT_ERR_GENERIC_IO;
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
if (ret < 0) {
close();
return RESULT_ERR_GENERIC_IO;
}
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)) {
close();
}
}
+244
View File
@@ -0,0 +1,244 @@
/*
* Copyright (C) John Baier 2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
* ebusd is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ebusd is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ebusd. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LIBEBUS_DEVICE_H_
#define LIBEBUS_DEVICE_H_
#include <termios.h>
#include <iostream>
#include <fstream>
#include <arpa/inet.h>
#include <netdb.h>
#include "result.h"
/** \file device.h */
using namespace std;
/**
* 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 logRawFunc the function to call for logging raw data, or NULL.
*/
Device(const char* name, const bool checkDevice,
void (*logRawFunc)(const unsigned char byte, bool received))
: m_name(name), m_checkDevice(checkDevice), m_fd(-1),
m_logRaw(false), m_logRawFunc(logRawFunc),
m_dumpRaw(false), m_dumpRawFile(NULL), m_dumpRawMaxSize(0), m_dumpRawStream(NULL), m_dumpRawFileSize(0) {}
/**
* 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 logRawFunc the function to call for logging raw data, or NULL.
* @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,
void (*logRawFunc)(const unsigned char byte, bool received)=NULL);
/**
* 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();
/**
* 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 long timeout, unsigned char& value);
/**
* Get whether logging of raw data is enabled.
* @return whether logging of raw data is enabled.
*/
bool getLogRaw() { return m_logRaw; }
/**
* Enable or disable logging of raw data.
* @param logRaw true to enable logging of raw data, false to disable it.
*/
void setLogRaw(bool logRaw=true) { m_logRaw = logRaw; }
/**
* Get whether dumping of raw data to a file is enabled.
* @return whether dumping of raw data to a file is enabled.
*/
bool getDumpRaw() { return m_dumpRaw; }
/**
* Enable or disable dumping of raw data to a file.
* @param dumpRaw true to enable dumping of raw data to a file, false to disable it.
*/
void setDumpRaw(bool dumpRaw=true);
/**
* Set the name of the file to dump raw data to.
* @param dumpFile the name of the file to dump raw data to.
*/
void setDumpRawFile(const char* dumpFile);
/**
* Set the maximum size of a file to dump raw data to.
* @param maxSize the maximum size of a file to dump raw data to.
*/
void setDumpRawMaxSize(const long maxSize) { m_dumpRawMaxSize = maxSize; }
/**
* 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();
protected:
/**
* Check if the device is still available and close it if not.
*/
virtual void checkDevice() = 0; // abstract
protected:
/** 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;
/** the opened file descriptor, or -1. */
int m_fd;
private:
/** whether logging of raw data is enabled. */
bool m_logRaw;
/** the function to call for logging raw data, or NULL. */
void (*m_logRawFunc)(const unsigned char byte, bool received);
/** whether dumping of raw data to a file is enabled. */
bool m_dumpRaw;
/** the name of the file to dump raw data to. */
const char* m_dumpRawFile;
/** the maximum size of @a m_dumpFile, or 0 for infinite. */
long m_dumpRawMaxSize;
/** the @a ofstream for dumping raw data to. */
ofstream m_dumpRawStream;
/** the number of bytes already written to the @a m_dumpFile. */
long m_dumpRawFileSize;
};
/**
* 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 logRawFunc the function to call for logging raw data, or NULL.
*/
SerialDevice(const char* name, const bool checkDevice,
void (*logRawFunc)(const unsigned char byte, bool received))
: Device(name, checkDevice, logRawFunc) {}
// @copydoc
virtual result_t open();
// @copydoc
void close();
protected:
// @copydoc
virtual void checkDevice();
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 logRawFunc the function to call for logging raw data, or NULL.
*/
NetworkDevice(const char* name, const struct sockaddr_in address,
void (*logRawFunc)(const unsigned char byte, bool received))
: Device(name, true, logRawFunc), m_address(address) {}
// @copydoc
virtual result_t open();
protected:
// @copydoc
virtual void checkDevice();
private:
/** the socket address of the device. */
const struct sockaddr_in m_address;
};
#endif // LIBEBUS_DEVICE_H_
-338
View File
@@ -1,338 +0,0 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
*
* This file is part of ebusd.
*
* ebusd is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ebusd is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ebusd. If not, see http://www.gnu.org/licenses/.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "port.h"
#include "result.h"
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_PPOLL
#include <poll.h>
#endif
using namespace std;
bool Device::isOpen()
{
if (isValid() == false)
m_open = false;
return m_open;
}
bool Device::isValid()
{
if (m_noDeviceCheck == false) {
int port;
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
closeDevice();
m_open = false;
return false;
}
}
return true;
}
result_t Device::send(const unsigned char value)
{
if (isValid() == false)
return RESULT_ERR_DEVICE;
// write bytes to device
return write(m_fd, &value, 1) == 1 ? RESULT_OK : RESULT_ERR_SEND;
}
result_t Device::recv(const long timeout, unsigned char& value)
{
if (isValid() == false)
return RESULT_ERR_DEVICE;
if (timeout > 0) {
int ret;
struct timespec tdiff;
// set select timeout
tdiff.tv_sec = 0;
tdiff.tv_nsec = timeout*1000;
#ifdef HAVE_PPOLL
int nfds = 1;
struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd;
fds[0].events = POLLIN;
ret = ppoll(fds, nfds, &tdiff, NULL);
#else
#ifdef HAVE_PSELECT
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL);
#else
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;
}
// directly read byte from device
ssize_t nbytes = read(m_fd, &value, 1);
if (nbytes == 0)
return RESULT_ERR_EOF;
return nbytes < 0 ? RESULT_ERR_DEVICE : RESULT_OK;
}
result_t DeviceSerial::openDevice(const string deviceName, const bool noDeviceCheck)
{
m_noDeviceCheck = noDeviceCheck;
struct termios newSettings;
m_open = false;
// open file descriptor
m_fd = open(deviceName.c_str(), O_RDWR | O_NOCTTY);
if (m_fd < 0 || isatty(m_fd) == 0)
return RESULT_ERR_NOTFOUND;
// save current settings
tcgetattr(m_fd, &m_oldSettings);
// 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;
// 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);
// 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);
m_open = true;
return RESULT_OK;
}
void DeviceSerial::closeDevice()
{
if (m_open == true) {
// empty device buffer
tcflush(m_fd, TCIOFLUSH);
// activate old settings of serial device
tcsetattr(m_fd, TCSANOW, &m_oldSettings);
// close file descriptor from serial device
close(m_fd);
m_fd = -1;
m_open = false;
}
}
result_t DeviceNetwork::openDevice(const string deviceName, const bool noDeviceCheck)
{
m_noDeviceCheck = noDeviceCheck;
struct sockaddr_in sock;
char* hostport;
int ret;
m_open = false;
memset((char*) &sock, 0, sizeof(sock));
hostport = strdup(deviceName.c_str());
char* host = strtok(hostport, ":");
char* port = strtok(NULL, ":");
if (inet_addr(host) == INADDR_NONE) {
struct hostent* he;
he = gethostbyname(host);
if (he == NULL)
return RESULT_ERR_NOTFOUND;
memcpy(&sock.sin_addr, he->h_addr_list[0], he->h_length);
}
else {
ret = inet_aton(host, &sock.sin_addr);
if (ret == 0)
return RESULT_ERR_NOTFOUND;
}
sock.sin_family = AF_INET;
sock.sin_port = htons(strtol(port, NULL, 10));
m_fd = socket(AF_INET, SOCK_STREAM, 0);
if (m_fd < 0)
return RESULT_ERR_GENERIC_IO;
ret = connect(m_fd, (struct sockaddr*) &sock, sizeof(sock));
if (ret < 0)
return RESULT_ERR_GENERIC_IO;
free(hostport);
m_open = true;
return RESULT_OK;
}
void DeviceNetwork::closeDevice()
{
if (m_open == true) {
// close file descriptor from network device
close(m_fd);
m_fd = -1;
m_open = false;
}
}
Port::Port(const string deviceName, const bool noDeviceCheck,
const bool logRaw, void (*logRawFunc)(const unsigned char byte, bool received),
const bool dumpRaw, const char* dumpRawFile, const long dumpRawMaxSize)
: m_deviceName(deviceName), m_noDeviceCheck(noDeviceCheck),
m_logRaw(logRaw), m_logRawFunc(logRawFunc),
m_dumpRawFile(dumpRawFile), m_dumpRawMaxSize(dumpRawMaxSize),
m_dumpRawFileSize(0)
{
m_device = NULL;
if (strchr(deviceName.c_str(), '/') == NULL && strchr(deviceName.c_str(), ':') != NULL) {
setType(dt_network);
m_noDeviceCheck = true;
}
else
setType(dt_serial);
m_dumpRaw = false;
setDumpRaw(dumpRaw); // open fstream if necessary
}
result_t Port::send(const unsigned char value)
{
result_t ret = m_device->send(value);
if (ret == RESULT_OK && m_logRaw == true && m_logRawFunc != NULL)
(*m_logRawFunc)(value, false);
return ret;
}
result_t Port::recv(const long timeout, unsigned char& value)
{
result_t ret = m_device->recv(timeout, value);
if (ret == RESULT_OK) {
if (m_logRaw == true && m_logRawFunc != NULL) {
(*m_logRawFunc)(value, true);
}
if (m_dumpRaw == true && m_dumpRawStream.is_open() == true) {
m_dumpRawStream.write((char*)&value, 1);
m_dumpRawFileSize++;
if ((m_dumpRawFileSize%1024) == 0)
m_dumpRawStream.flush();
if (m_dumpRawFileSize >= m_dumpRawMaxSize * 1024) {
string oldfile = m_dumpRawFile + ".old";
if (rename(m_dumpRawFile.c_str(), oldfile.c_str()) == 0) {
m_dumpRawStream.close();
m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
}
}
return ret;
}
void Port::setDumpRaw(bool dumpRaw)
{
if (dumpRaw == m_dumpRaw)
return;
m_dumpRaw = dumpRaw;
if (dumpRaw == false)
m_dumpRawStream.close();
else {
m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
void Port::setDumpRawFile(const string& dumpFile) {
if (dumpFile == m_dumpRawFile)
return;
m_dumpRawStream.close();
m_dumpRawFile = dumpFile;
if (m_dumpRaw == true) {
m_dumpRawStream.open(m_dumpRawFile.c_str(), ios::out | ios::binary | ios::app);
m_dumpRawFileSize = 0;
}
}
void Port::setType(const DeviceType type)
{
if (m_device != NULL)
delete m_device;
switch (type) {
case dt_serial:
m_device = new DeviceSerial();
break;
case dt_network:
m_device = new DeviceNetwork();
break;
};
};
-296
View File
@@ -1,296 +0,0 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
*
* This file is part of ebusd.
*
* ebusd is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ebusd is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ebusd. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LIBEBUS_PORT_H_
#define LIBEBUS_PORT_H_
#include <string>
#include <queue>
#include <termios.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include "result.h"
/** \file port.h */
using namespace std;
/** available device types. */
enum DeviceType {
dt_serial, /*!< serial device */
dt_network /*!< network device */
};
/**
* base class for input devices.
*/
class Device
{
public:
/**
* constructs a new instance.
*/
Device() : m_fd(-1), m_open(false), m_noDeviceCheck(false) {}
/**
* destructor.
*/
virtual ~Device() {}
/**
* virtual open function for opening file descriptor
* @param deviceName to determine device type.
* @param noDeviceCheck en-/disable device check.
* @return the @a result_t code.
*/
virtual result_t openDevice(const string deviceName, const bool noDeviceCheck) = 0;
/**
* virtual close function for closing opened file descriptor
*/
virtual void closeDevice() = 0;
/**
* connection state of device.
* @return true if device is open
*/
bool isOpen();
/**
* Write a single byte to opened file descriptor.
* @param value the value to send.
* @return the result_t code.
*/
result_t send(const unsigned char value);
/**
* Read a single byte from opened file descriptor.
* @param timeout max time out for new input data [usec], or 0 for infinite.
* @param value the reference in which the value is stored.
* @return the result_t code.
*/
result_t recv(const long timeout, unsigned char& value);
protected:
/** file descriptor from input device */
int m_fd;
/** true if device is opened */
bool m_open;
/** true if device check is disabled */
bool m_noDeviceCheck;
private:
/**
* system check if opened file descriptor is valid
* @return true if file descriptor is valid
*/
bool isValid();
};
/**
* class for serial input device.
*/
class DeviceSerial : public Device
{
public:
/**
* destructor.
*/
~DeviceSerial() { closeDevice(); }
// @copydoc
virtual result_t openDevice(const string deviceName, const bool noDeviceCheck);
// @copydoc
void closeDevice();
private:
/** save settings from serial device */
termios m_oldSettings;
};
/**
* class for network input device.
*/
class DeviceNetwork : public Device
{
public:
/**
* destructor.
*/
~DeviceNetwork() { closeDevice(); }
// @copydoc
virtual result_t openDevice(const string deviceName, const bool noDeviceCheck);
// @copydoc
void closeDevice();
private:
};
/**
* wrapper class for class device.
*/
class Port
{
public:
/**
* constructs a new instance and determine device type.
* @param deviceName to determine device type.
* @param noDeviceCheck en-/disable device check.
* @param logRaw whether logging of raw data is enabled.
* @param logRawFunc a function to call for logging raw data, or NULL.
* @param dumpRaw whether dumping of raw data to a file is enabled.
* @param dumpRawFile the name of the file to dump raw data to.
* @param dumpRawMaxSize the maximum size of @a m_dumpFile.
*/
Port(const string deviceName, const bool noDeviceCheck,
const bool logRaw, void (*logRawFunc)(const unsigned char byte, bool received),
const bool dumpRaw, const char* dumpRawFile, const long dumpRawMaxSize);
/**
* destructor.
*/
~Port() { delete m_device; m_dumpRawStream.close(); }
/**
* open device
*/
result_t open() { return m_device->openDevice(m_deviceName, m_noDeviceCheck); }
/**
* close device
*/
void close() { m_device->closeDevice(); }
/**
* connection state of device.
* @return true if device is open
*/
bool isOpen() { return m_device->isOpen(); }
/**
* Write a single byte to opened file descriptor.
* @param value the value to send.
* @return the result_t code.
*/
result_t send(const unsigned char value);
/**
* Read a single byte from opened file descriptor.
* @param timeout max time out for new input data [usec], or 0 for infinite.
* @param value the reference in which the value is stored.
* @return the result_t code.
*/
result_t recv(const long timeout, unsigned char& value);
/**
* Get whether logging of raw data is enabled.
* @return whether logging of raw data is enabled.
*/
bool getLogRaw() { return m_logRaw; }
/**
* Enable or disable logging of raw data.
* @param logRaw true to enable logging of raw data, false to disable it.
*/
void setLogRaw(bool logRaw=true) { m_logRaw = logRaw; }
/**
* Get whether dumping of raw data to a file is enabled.
* @return whether dumping of raw data to a file is enabled.
*/
bool getDumpRaw() { return m_dumpRaw; }
/**
* Enable or disable dumping of raw data to a file.
* @param dumpRaw true to enable dumping of raw data to a file, false to disable it.
*/
void setDumpRaw(bool dumpRaw=true);
/**
* Set the name of the file to dump raw data to.
* @param dumpFile the name of the file to dump raw data to.
*/
void setDumpRawFile(const string& dumpFile);
/**
* Set the maximum size of a file to dump raw data to.
* @param maxSize the maximum size of a file to dump raw data to.
*/
void setDumpRawMaxSize(const long maxSize) { m_dumpRawMaxSize = maxSize; }
/**
* Return the device name.
* @return the device name.
*/
const char* getDeviceName() { return m_deviceName.c_str(); }
private:
/** the device name */
const string m_deviceName;
/** the device instance */
Device* m_device;
/** true if device check is disabled */
bool m_noDeviceCheck;
/** whether logging of raw data is enabled. */
bool m_logRaw;
/** a function to call for logging raw data, or NULL. */
void (*m_logRawFunc)(const unsigned char byte, bool received);
/** whether dumping of raw data to a file is enabled. */
bool m_dumpRaw;
/** the name of the file to dump raw data to. */
string m_dumpRawFile;
/** the maximum size of @a m_dumpFile. */
long m_dumpRawMaxSize;
/** the @a ofstream for dumping raw data to. */
ofstream m_dumpRawStream;
/** the number of bytes already written to the @a m_dumpFile. */
long m_dumpRawFileSize;
/**
* internal setter for device type.
* @param type of device
*/
void setType(const DeviceType type);
};
#endif // LIBEBUS_PORT_H_
+3 -3
View File
@@ -3,13 +3,13 @@ AM_CXXFLAGS = -fpic \
-Wextra \
-isystem$(top_srcdir)/src/lib/ebus
noinst_PROGRAMS = test_port \
noinst_PROGRAMS = test_device \
test_symbol \
test_data \
test_message
test_port_SOURCES = test_port.cpp
test_port_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a
test_device_SOURCES = test_device.cpp
test_device_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a
test_symbol_SOURCES = test_symbol.cpp
test_symbol_LDADD = $(top_srcdir)/src/lib/ebus/libebus.a
@@ -1,5 +1,5 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) John Baier 2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
@@ -17,7 +17,7 @@
* along with ebusd. If not, see http://www.gnu.org/licenses/.
*/
#include "port.h"
#include "device.h"
#include <iostream>
#include <iomanip>
@@ -25,33 +25,38 @@ using namespace std;
int main ()
{
string dev("/dev/ttyUSB20");
Port port(dev, true, false, NULL, false, "", 1);
Device* device = Device::create("/dev/ttyUSB20", true, NULL);
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() == false)
cout << "device not available." << endl;
port.open();
int count = 0;
if(port.isOpen() == true)
cout << "openPort successful." << endl;
while (1) {
unsigned char byte = 0;
result = device->recv(0, byte);
int count = 0;
if (result == RESULT_OK)
cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(byte) << endl;
while (1) {
result_t result;
unsigned char byte = 0;
result = port.recv(0, byte);
count++;
}
if (result == RESULT_OK)
cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(byte) << endl;
device->close();
count++;
if(device->isValid() == false)
cout << "close successful." << endl;
}
port.close();
if(port.isOpen() == false)
cout << "closePort successful." << endl;
delete device;
return 0;
}
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>,
* John Baier 2014-2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>,
* John Baier 2014-2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
+18 -14
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>
* Copyright (C) Roland Jax 2012-2014 <ebusd@liwest.at>,
* John Baier 2014-2015 <ebusd@johnm.de>
*
* This file is part of ebusd.
*
@@ -22,7 +23,8 @@
#endif
#include <argp.h>
#include "port.h"
#include "device.h"
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <cstdlib>
@@ -129,12 +131,19 @@ int main(int argc, char* argv[])
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opt) != 0)
return EINVAL;
string dev(opt.device);
Port port(dev, true, false, NULL, false, "", 1);
Device* device = Device::create(opt.device, true, NULL);
if (device == NULL) {
cout << "unable to create device " << opt.device << endl;
return EINVAL;
}
result_t result = device->open();
if (result != RESULT_OK)
cout << "unable to open " << opt.device << ": " << getResultCode(result) << endl;
port.open();
if(port.isOpen() == true) {
cout << "openPort successful." << endl;
if (device->isValid() == false)
cout << "device " << opt.device << " not available" << endl;
else {
cout << "device opened" << endl;
fstream file(opt.dumpFile, ios::in | ios::binary);
@@ -147,7 +156,7 @@ int main(int argc, char* argv[])
cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(byte) << endl;
port.send(byte);
device->send(byte);
usleep(opt.time);
}
@@ -155,14 +164,9 @@ int main(int argc, char* argv[])
}
else
cout << "error opening file " << opt.dumpFile << endl;
port.close();
if(port.isOpen() == false)
cout << "closePort successful." << endl;
}
else
cout << "error opening device " << opt.device << endl;
delete device;
exit(EXIT_SUCCESS);
}