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
+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.
*