rework ebus protocol engine (consume buffered data in a row, avoid starting arbitration when data is buffered, separate transport to/from device)
This commit is contained in:
+118
-298
@@ -19,15 +19,9 @@
|
||||
#ifndef LIB_EBUS_DEVICE_H_
|
||||
#define LIB_EBUS_DEVICE_H_
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "lib/ebus/result.h"
|
||||
#include "lib/ebus/transport.h"
|
||||
#include "lib/ebus/symbol.h"
|
||||
|
||||
namespace ebusd {
|
||||
@@ -35,25 +29,12 @@ namespace ebusd {
|
||||
/** @file lib/ebus/device.h
|
||||
* Classes providing access to the eBUS.
|
||||
*
|
||||
* A @a Device is either a @a SerialDevice directly connected to a local tty
|
||||
* port or a remote @a NetworkDevice handled via a TCP socket. It allows to
|
||||
* send and receive bytes to/from the eBUS while optionally dumping the data
|
||||
* to a file and/or forwarding it to a logging function.
|
||||
* A @a Device allows to send and receive data to/from a local or remote eBUS
|
||||
* device while optionally dumping the data to a file and/or forwarding it to
|
||||
* a logging function.
|
||||
* The data transport itself is handled by a @a Transport instance.
|
||||
*/
|
||||
|
||||
/** the transfer latency of the network device [ms]. */
|
||||
#define NETWORK_LATENCY_MS 30
|
||||
|
||||
/** the extra transfer latency to take into account for enhanced protocol. */
|
||||
#define ENHANCED_LATENCY_MS 10
|
||||
|
||||
/** the latency of the host [ms]. */
|
||||
#if defined(__CYGWIN__) || defined(_WIN32)
|
||||
#define HOST_LATENCY_MS 20
|
||||
#else
|
||||
#define HOST_LATENCY_MS 10
|
||||
#endif
|
||||
|
||||
/** the arbitration state handled by @a Device. */
|
||||
enum ArbitrationState {
|
||||
as_none, //!< no arbitration in process
|
||||
@@ -65,11 +46,6 @@ enum ArbitrationState {
|
||||
as_won, //!< arbitration won
|
||||
};
|
||||
|
||||
/** the sequence IDs as handled by @a FileDevice. */
|
||||
enum SequenceId {
|
||||
sid_info, //!< send/receive info
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for listening to data received on/sent to a device.
|
||||
*/
|
||||
@@ -81,11 +57,12 @@ class DeviceListener {
|
||||
virtual ~DeviceListener() {}
|
||||
|
||||
/**
|
||||
* Listener method that is called when a symbol was received from/sent to eBUS.
|
||||
* @param symbol the received/sent symbol.
|
||||
* Listener method that is called when symbols were received from/sent to eBUS.
|
||||
* @param data the received/sent data.
|
||||
* @param len the length of received/sent data.
|
||||
* @param received @a true on reception, @a false on sending.
|
||||
*/
|
||||
virtual void notifyDeviceData(symbol_t symbol, bool received) = 0; // abstract
|
||||
virtual void notifyDeviceData(symbol_t* data, size_t len, bool received) = 0; // abstract
|
||||
|
||||
/**
|
||||
* Called to notify a status message from the device.
|
||||
@@ -99,25 +76,32 @@ class DeviceListener {
|
||||
/**
|
||||
* The base class for accessing an eBUS.
|
||||
*/
|
||||
class Device {
|
||||
class Device : public TransportListener {
|
||||
protected:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
* @param transport the @a Transport to use.
|
||||
*/
|
||||
explicit Device(const char* name);
|
||||
explicit Device(Transport* transport)
|
||||
: m_transport(transport), m_listener(nullptr) {
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Device() { }
|
||||
virtual ~Device() {
|
||||
if (m_transport) {
|
||||
delete m_transport;
|
||||
m_transport = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device name.
|
||||
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
*/
|
||||
const char* getName() const { return m_name; }
|
||||
const char* getName() const { return m_transport->getName(); }
|
||||
|
||||
/**
|
||||
* Set the @a DeviceListener.
|
||||
@@ -131,43 +115,75 @@ class Device {
|
||||
* @param verbose whether to add verbose infos.
|
||||
* @param prefix true for the synchronously retrievable prefix, false for the potentially asynchronous suffix.
|
||||
*/
|
||||
virtual void formatInfo(ostringstream* output, bool verbose, bool prefix) = 0;
|
||||
virtual void formatInfo(ostringstream* output, bool verbose, bool prefix) {
|
||||
if (prefix) {
|
||||
*output << m_transport->getName() << ", " << m_transport->getTransportInfo();
|
||||
} else if (!m_transport->isValid()) {
|
||||
*output << ", invalid";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format device infos in JSON format.
|
||||
* @param output the @a ostringstream to append the infos to.
|
||||
*/
|
||||
virtual void formatInfoJson(ostringstream* output) = 0;
|
||||
virtual void formatInfoJson(ostringstream* output) const {}
|
||||
|
||||
/**
|
||||
* @return whether the device supports checking for version updates.
|
||||
*/
|
||||
virtual bool supportsUpdateCheck() const { return false; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t notifyTransportStatus(bool opened) {
|
||||
m_listener->notifyDeviceStatus(!opened, opened ? "transport opened" : "transport closed");
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
virtual void notifyTransportMessage(bool error, const char* message) {
|
||||
m_listener->notifyDeviceStatus(error, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the file descriptor.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t open() = 0;
|
||||
|
||||
/**
|
||||
* Has to be called by subclasses upon successful opening the device as last action in open().
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t afterOpen() { return RESULT_OK; }
|
||||
|
||||
/**
|
||||
* Close the file descriptor if opened.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
virtual result_t open() { return m_transport->open(); }
|
||||
|
||||
/**
|
||||
* Return whether the device is opened and available.
|
||||
* @return whether the device is opened and available.
|
||||
*/
|
||||
virtual bool isValid() = 0;
|
||||
virtual bool isValid() { return m_transport->isValid(); }
|
||||
|
||||
protected:
|
||||
/** the @a Transport to use. */
|
||||
Transport* m_transport;
|
||||
|
||||
/** the @a DeviceListener, or nullptr. */
|
||||
DeviceListener* m_listener;
|
||||
};
|
||||
|
||||
|
||||
class CharDevice : public Device {
|
||||
protected:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param transport the @a Transport to use.
|
||||
*/
|
||||
explicit CharDevice(Transport* transport)
|
||||
: Device(transport), m_arbitrationMaster(SYN), m_arbitrationCheck(0) {
|
||||
transport->setListener(this);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Write a single byte to the device.
|
||||
* @param value the byte value to write.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t send(symbol_t value) = 0;
|
||||
virtual result_t send(symbol_t value) = 0; // abstract
|
||||
|
||||
/**
|
||||
* Read a single byte from the device.
|
||||
@@ -177,7 +193,7 @@ class Device {
|
||||
* @a as_won, the received byte is the master address that was successfully arbitrated with.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
virtual result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) = 0;
|
||||
virtual result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) = 0; // abstract
|
||||
|
||||
/**
|
||||
* Start the arbitration with the specified master address. A subsequent request while an arbitration is currently in
|
||||
@@ -185,74 +201,65 @@ class Device {
|
||||
* @param masterAddress the master address, or @a SYN to cancel a previous arbitration request.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
virtual result_t startArbitration(symbol_t masterAddress) = 0;
|
||||
virtual result_t startArbitration(symbol_t masterAddress);
|
||||
|
||||
/**
|
||||
* Return whether the device is currently in arbitration.
|
||||
* @return true when the device is currently in arbitration.
|
||||
*/
|
||||
virtual bool isArbitrating() const = 0;
|
||||
virtual bool isArbitrating() const { return m_arbitrationMaster != SYN; }
|
||||
|
||||
/**
|
||||
* @return whether the device supports checking for version updates.
|
||||
* Cancel a running arbitration.
|
||||
* @param arbitrationState the reference in which @a as_error is stored when cancelled.
|
||||
* @return true if it was cancelled, false if not.
|
||||
*/
|
||||
virtual bool supportsUpdateCheck() const = 0;
|
||||
virtual bool cancelRunningArbitration(ArbitrationState* arbitrationState);
|
||||
|
||||
protected:
|
||||
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
|
||||
const char* m_name;
|
||||
/** the arbitration master address to send when in arbitration, or @a SYN. */
|
||||
symbol_t m_arbitrationMaster;
|
||||
|
||||
/** the @a DeviceListener, or nullptr. */
|
||||
DeviceListener* m_listener;
|
||||
/** >0 when in arbitration and the next received symbol needs to be checked against the sent master address,
|
||||
* incremented with each received SYN when arbitration was not performed as expected and needs to be stopped. */
|
||||
size_t m_arbitrationCheck;
|
||||
};
|
||||
|
||||
|
||||
/** the possible enhanced protocol levels. */
|
||||
enum EnhancedLevel {
|
||||
el_none = 0, //!< non-enhanced
|
||||
el_basic = 1, //!< enhanced basic
|
||||
el_speed = 2, //!< enhanced high-speed
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The common base class for devices using a file descriptor.
|
||||
*/
|
||||
class FileDevice : public Device {
|
||||
protected:
|
||||
/**
|
||||
* 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.
|
||||
* @param latency the bus transfer latency in milliseconds.
|
||||
* @param enhancedLevel whether to use the ebusd enhanced protocol.
|
||||
*/
|
||||
FileDevice(const char* name, bool checkDevice, unsigned int latency,
|
||||
EnhancedLevel enhancedLevel);
|
||||
|
||||
class PlainCharDevice : public CharDevice {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
* Construct a new instance.
|
||||
* @param transport the @a Transport to use.
|
||||
*/
|
||||
virtual ~FileDevice();
|
||||
explicit PlainCharDevice(Transport* transport)
|
||||
: CharDevice(transport) {
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
result_t send(symbol_t value) override;
|
||||
|
||||
// @copydoc
|
||||
result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) override;
|
||||
};
|
||||
|
||||
|
||||
class EnhancedCharDevice : public CharDevice {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param transport the @a Transport to use.
|
||||
*/
|
||||
explicit EnhancedCharDevice(Transport* transport)
|
||||
: CharDevice(transport), m_resetRequested(false),
|
||||
m_extraFatures(0), m_infoReqTime(0), m_infoLen(0), m_infoPos(0) {
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
void formatInfo(ostringstream* output, bool verbose, bool prefix) override;
|
||||
|
||||
// @copydoc
|
||||
void formatInfoJson(ostringstream* output) override;
|
||||
|
||||
// @copydoc
|
||||
result_t open() override;
|
||||
|
||||
// @copydoc
|
||||
result_t afterOpen() override;
|
||||
|
||||
// @copydoc
|
||||
void close() override;
|
||||
|
||||
// @copydoc
|
||||
bool isValid() override;
|
||||
void formatInfoJson(ostringstream* output) const override;
|
||||
|
||||
// @copydoc
|
||||
result_t send(symbol_t value) override;
|
||||
@@ -264,50 +271,19 @@ class FileDevice : public Device {
|
||||
result_t startArbitration(symbol_t masterAddress) override;
|
||||
|
||||
// @copydoc
|
||||
bool isArbitrating() const override { return m_arbitrationMaster != SYN; }
|
||||
|
||||
/**
|
||||
* Get the transfer latency of this device.
|
||||
* @return the transfer latency in milliseconds.
|
||||
*/
|
||||
virtual unsigned int getLatency() const { return m_latency; }
|
||||
|
||||
/**
|
||||
* Return whether the device supports the ebusd enhanced protocol.
|
||||
* @return whether the device supports the ebusd enhanced protocol.
|
||||
*/
|
||||
bool isEnhancedProto() const { return m_enhancedLevel != el_none; }
|
||||
|
||||
/**
|
||||
* Get info about enhanced protocol support as string.
|
||||
* @return a @a string describing level of enhanced protocol support, or the empty string.
|
||||
*/
|
||||
virtual string getEnhancedProtoInfo() const { return m_enhancedLevel ? "enhanced" : ""; }
|
||||
virtual result_t notifyTransportStatus(bool opened);
|
||||
|
||||
// @copydoc
|
||||
bool supportsUpdateCheck() const override { return m_enhancedLevel && m_extraFatures & 0x01; }
|
||||
|
||||
/**
|
||||
* @return whether the device supports the ebusd enhanced protocol and supports querying extra infos.
|
||||
*/
|
||||
bool supportsEnhancedInfos() const { return m_enhancedLevel && m_extraFatures & 0x01; }
|
||||
bool supportsUpdateCheck() const override { return m_extraFatures & 0x01; }
|
||||
|
||||
/**
|
||||
* Check for a running extra infos request, wait for it to complete,
|
||||
* and then send a new request for extra infos to enhanced device.
|
||||
* @param infoId the ID of the info to request.
|
||||
* @param wait true to wait for a running request to complete, false to send right away.
|
||||
* @return @a RESULT_OK on success, or an error code otherwise.
|
||||
*/
|
||||
result_t requestEnhancedInfo(symbol_t infoId);
|
||||
|
||||
/**
|
||||
* Write a sequence of bytes to the device.
|
||||
* @param id the ID of the sequence.
|
||||
* @param data the buffer with the data to send.
|
||||
* @param len the length of the buffer.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t sendSequence(SequenceId id, const uint8_t* data = nullptr, size_t len = 0);
|
||||
result_t requestEnhancedInfo(symbol_t infoId, bool wait = true);
|
||||
|
||||
/**
|
||||
* Get the enhanced device version.
|
||||
@@ -321,12 +297,7 @@ class FileDevice : public Device {
|
||||
*/
|
||||
string getEnhancedInfos();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Check if the device is still available and close it if not.
|
||||
*/
|
||||
virtual void checkDevice() = 0; // abstract
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cancel a running arbitration.
|
||||
* @param arbitrationState the reference in which @a as_error is stored when cancelled.
|
||||
@@ -334,83 +305,22 @@ class FileDevice : public Device {
|
||||
*/
|
||||
bool cancelRunningArbitration(ArbitrationState* arbitrationState);
|
||||
|
||||
/**
|
||||
* Write a single byte.
|
||||
* @param value the byte value to write.
|
||||
* @param startArbitration true to start arbitration.
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual bool write(symbol_t value, bool startArbitration = false);
|
||||
|
||||
/**
|
||||
* Check whether a symbol is available for reading immediately (without waiting).
|
||||
* @return true when a symbol is available for reading immediately.
|
||||
*/
|
||||
virtual bool available();
|
||||
|
||||
/**
|
||||
* Read a single byte.
|
||||
* @param value the reference in which the read byte value is stored.
|
||||
* @param isAvailable the result of the immediately preceding call to @a available().
|
||||
* @param arbitrationState the variable in which to store the current/received arbitration state (mandatory for enhanced proto).
|
||||
* @param incomplete the variable in which to store when a partial transfer needs another poll.
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState = nullptr,
|
||||
bool* incomplete = nullptr);
|
||||
|
||||
/** whether to regularly check the device availability. */
|
||||
const bool m_checkDevice;
|
||||
|
||||
/** the bus transfer latency in milliseconds. */
|
||||
const unsigned int m_latency;
|
||||
|
||||
/** whether the device supports the ebusd enhanced protocol. */
|
||||
const EnhancedLevel m_enhancedLevel;
|
||||
|
||||
/** the opened file descriptor, or -1. */
|
||||
int m_fd;
|
||||
|
||||
/** whether the reset of an enhanced device was already requested. */
|
||||
bool m_resetRequested;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handle the already buffered enhanced data.
|
||||
* @param value the reference in which the read byte value is stored.
|
||||
* @param arbitrationState the variable in which to store the current/received arbitration state (mandatory for enhanced proto).
|
||||
* @return true if the value was set, false otherwise.
|
||||
* @param arbitrationState the variable in which to store the current/received arbitration state.
|
||||
* @return the @a result_t code, especially RESULT_CONTINE if the value was set and more data is available immediately.
|
||||
*/
|
||||
bool handleEnhancedBufferedData(symbol_t* value, ArbitrationState* arbitrationState);
|
||||
result_t handleEnhancedBufferedData(const uint8_t* data, size_t len, symbol_t* value,
|
||||
ArbitrationState* arbitrationState);
|
||||
|
||||
/**
|
||||
* Called when reception of an info ID was completed.
|
||||
*/
|
||||
void notifyInfoRetrieved();
|
||||
|
||||
/** the arbitration master address to send when in arbitration, or @a SYN. */
|
||||
symbol_t m_arbitrationMaster;
|
||||
|
||||
/** >0 when in arbitration and the next received symbol needs to be checked against the sent master address,
|
||||
* incremented with each received SYN when arbitration was not performed as expected and needs to be stopped. */
|
||||
size_t m_arbitrationCheck;
|
||||
|
||||
/** the read buffer. */
|
||||
symbol_t* m_buffer;
|
||||
|
||||
/** the read buffer size (multiple of 4). */
|
||||
size_t m_bufSize;
|
||||
|
||||
/** the read buffer fill length. */
|
||||
size_t m_bufLen;
|
||||
|
||||
/** the read buffer read position. */
|
||||
size_t m_bufPos;
|
||||
/** the send buffer. */
|
||||
uint8_t* m_sendBuf;
|
||||
|
||||
/** the send buffer size. */
|
||||
size_t m_sendBufSize;
|
||||
/** whether the reset of the device was already requested. */
|
||||
bool m_resetRequested;
|
||||
|
||||
/** the extra features supported by the device. */
|
||||
symbol_t m_extraFatures;
|
||||
@@ -440,96 +350,6 @@ class FileDevice : public Device {
|
||||
string m_enhInfoBusVoltage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The @a Device for directly connected serial interfaces (tty).
|
||||
*/
|
||||
class SerialDevice : public FileDevice {
|
||||
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.
|
||||
* @param extraLatency the extra bus transfer latency in milliseconds.
|
||||
* @param enhancedLevel whether to use the ebusd enhanced protocol.
|
||||
*/
|
||||
SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency,
|
||||
EnhancedLevel enhancedLevel)
|
||||
: FileDevice(name, checkDevice, extraLatency, enhancedLevel) {
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
string getEnhancedProtoInfo() const override {
|
||||
return m_enhancedLevel == el_speed ? "enhanced high speed" : FileDevice::getEnhancedProtoInfo();
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
result_t open() override;
|
||||
|
||||
// @copydoc
|
||||
void close() override;
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
void checkDevice() override;
|
||||
|
||||
|
||||
private:
|
||||
/** the previous settings of the device for restoring. */
|
||||
termios m_oldSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* The @a Device for remote network interfaces.
|
||||
*/
|
||||
class NetworkDevice : public FileDevice {
|
||||
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 hostOrIp the host name or IP address of the device.
|
||||
* @param port the TCP or UDP port of the device.
|
||||
* @param extraLatency the extra bus transfer latency in milliseconds.
|
||||
* @param udp true for UDP, false to TCP.
|
||||
* @param enhancedLevel whether to use the ebusd enhanced protocol.
|
||||
*/
|
||||
NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, unsigned int extraLatency,
|
||||
bool udp, EnhancedLevel enhancedLevel)
|
||||
: FileDevice(name, true, NETWORK_LATENCY_MS+extraLatency, enhancedLevel),
|
||||
m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~NetworkDevice() override {
|
||||
if (m_hostOrIp) {
|
||||
free((void*)m_hostOrIp);
|
||||
m_hostOrIp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// @copydoc
|
||||
result_t open() override;
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
void checkDevice() override;
|
||||
|
||||
|
||||
private:
|
||||
/** the host name or IP address of the device. */
|
||||
const char* m_hostOrIp;
|
||||
|
||||
/** the TCP or UDP port of the device. */
|
||||
const uint16_t m_port;
|
||||
|
||||
/** true for UDP, false to TCP. */
|
||||
const bool m_udp;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
#endif // LIB_EBUS_DEVICE_H_
|
||||
|
||||
Reference in New Issue
Block a user