abstract bus protocol handling

This commit is contained in:
John
2023-10-13 08:08:58 +02:00
parent 6c66fd553b
commit 730d92d06c
14 changed files with 1695 additions and 1284 deletions
+47 -859
View File
File diff suppressed because it is too large Load Diff
+29 -380
View File
@@ -19,7 +19,6 @@
#ifndef EBUSD_BUSHANDLER_H_ #ifndef EBUSD_BUSHANDLER_H_
#define EBUSD_BUSHANDLER_H_ #define EBUSD_BUSHANDLER_H_
#include <pthread.h>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -31,59 +30,16 @@
#include "lib/ebus/symbol.h" #include "lib/ebus/symbol.h"
#include "lib/ebus/result.h" #include "lib/ebus/result.h"
#include "lib/ebus/device.h" #include "lib/ebus/device.h"
#include "lib/utils/queue.h" #include "lib/ebus/protocol.h"
#include "lib/utils/thread.h"
namespace ebusd { namespace ebusd {
/** @file ebusd/bushandler.h /** @file ebusd/bushandler.h
* Classes, functions, and constants related to handling of symbols on the eBUS. * Classes, functions, and constants related to handling messages on the eBUS.
*
* The following table shows the possible states, symbols, and state transition
* depending on the kind of message to send/receive:
* @image html states.png "ebusd BusHandler states"
*/ */
using std::string; using std::string;
/** the default time [ms] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15
/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 51
/** the time [ms] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250
/** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION_MICROS 4700
/** the maximum duration [ms] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION 5
/** the maximum allowed time [ms] for retrieving back a sent symbol (2x symbol duration). */
#define SEND_TIMEOUT ((int)((2*SYMBOL_DURATION_MICROS+999)/1000))
/** the possible bus states. */
enum BusState {
bs_noSignal, //!< no signal on the bus
bs_skip, //!< skip all symbols until next @a SYN
bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ)
bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set]
bs_recvCmdCrc, //!< receive command CRC [passive set]
bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get]
bs_recvRes, //!< receive response (slave data) [passive set + active get]
bs_recvResCrc, //!< receive response CRC [passive set + active get]
bs_recvResAck, //!< receive response ACK/NACK [passive set]
bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get]
bs_sendCmdCrc, //!< send command CRC [active set+get]
bs_sendResAck, //!< send response ACK/NACK [active get]
bs_sendCmdAck, //!< send command ACK/NACK [passive get]
bs_sendRes, //!< send response (slave data) [passive get]
bs_sendResCrc, //!< send response CRC [passive get]
bs_sendSyn, //!< send SYN for completed transfer [active set+get]
};
/** bit for the seen state: seen. */ /** bit for the seen state: seen. */
#define SEEN 0x01 #define SEEN 0x01
@@ -101,47 +57,6 @@ enum BusState {
class BusHandler; class BusHandler;
/**
* Generic request for sending to and receiving from the bus.
*/
class BusRequest {
friend class BusHandler;
public:
/**
* Constructor.
* @param master the master data @a MasterSymbolString to send.
* @param deleteOnFinish whether to automatically delete this @a BusRequest when finished.
*/
BusRequest(const MasterSymbolString& master, bool deleteOnFinish)
: m_master(master), m_busLostRetries(0),
m_deleteOnFinish(deleteOnFinish) {}
/**
* Destructor.
*/
virtual ~BusRequest() {}
/**
* Notify the request of the specified result.
* @param result the result of the request.
* @param slave the @a SlaveSymbolString received.
* @return true if the request needs to be restarted.
*/
virtual bool notify(result_t result, const SlaveSymbolString& slave) = 0;
protected:
/** the master data @a MasterSymbolString to send. */
const MasterSymbolString& m_master;
/** the number of times a send is repeated due to lost arbitration. */
unsigned int m_busLostRetries;
/** whether to automatically delete this @a BusRequest when finished. */
const bool m_deleteOnFinish;
};
/** /**
* A poll @a BusRequest handled by @a BusHandler itself. * A poll @a BusRequest handled by @a BusHandler itself.
@@ -259,39 +174,6 @@ class ScanRequest : public BusRequest {
}; };
/**
* An active @a BusRequest that can be waited for.
*/
class ActiveBusRequest : public BusRequest {
friend class BusHandler;
public:
/**
* Constructor.
* @param master the master data @a MasterSymbolString to send.
* @param slave reference to @a SlaveSymbolString for filling in the received slave data.
*/
ActiveBusRequest(const MasterSymbolString& master, SlaveSymbolString* slave)
: BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {}
/**
* Destructor.
*/
virtual ~ActiveBusRequest() {}
// @copydoc
bool notify(result_t result, const SlaveSymbolString& slave) override;
private:
/** the result of handling the request. */
result_t m_result;
/** reference to @a SlaveSymbolString for filling in the received slave data. */
SlaveSymbolString* m_slave;
};
/** /**
* Helper class for keeping track of grabbed messages. * Helper class for keeping track of grabbed messages.
*/ */
@@ -362,7 +244,7 @@ class GrabbedMessage {
/** /**
* Handles input from and output to the bus with respect to the eBUS protocol. * Handles input from and output to the bus with respect to the eBUS protocol.
*/ */
class BusHandler : public WaitThread { class BusHandler : public ProtocolListener {
public: public:
/** /**
* Construct a new instance. * Construct a new instance.
@@ -380,82 +262,39 @@ class BusHandler : public WaitThread {
* @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.
*/ */
BusHandler(Device* device, MessageMap* messages, ScanHelper* scanHelper, BusHandler(Device* device, MessageMap* messages, ScanHelper* scanHelper,
symbol_t ownAddress, bool answer, const ebus_protocol_config_t config, unsigned int pollInterval)
unsigned int busLostRetries, unsigned int failedSendRetries, : m_messages(messages), m_scanHelper(scanHelper),
unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout, m_pollInterval(pollInterval), m_lastPoll(0), m_runningScans(0),
unsigned int lockCount, bool generateSyn,
unsigned int pollInterval)
: WaitThread(), m_device(device), m_reconnect(false), m_messages(messages), m_scanHelper(scanHelper),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)),
m_answer(answer), m_addressConflict(false),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0),
m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount ? 1 : 0),
m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0),
m_pollInterval(pollInterval), m_symbolLatencyMin(-1), m_symbolLatencyMax(-1), m_arbitrationDelayMin(-1),
m_arbitrationDelayMax(-1), m_lastReceive(0), m_lastPoll(0),
m_currentRequest(nullptr), m_currentAnswering(false), m_runningScans(0), m_nextSendPos(0),
m_symPerSec(0), m_maxSymPerSec(0),
m_state(bs_noSignal), m_escape(0), m_crc(0), m_crcValid(false), m_repeat(false),
m_grabMessages(true) { m_grabMessages(true) {
m_protocol = ProtocolHandler::create(config, device, this);
memset(m_seenAddresses, 0, sizeof(m_seenAddresses)); memset(m_seenAddresses, 0, sizeof(m_seenAddresses));
m_lastSynReceiveTime.tv_sec = 0;
m_lastSynReceiveTime.tv_nsec = 0;
} }
/** /**
* Destructor. * Destructor.
*/ */
virtual ~BusHandler() { virtual ~BusHandler() {
stop(); if (m_protocol) {
join(); delete m_protocol;
BusRequest* req; m_protocol = nullptr;
while ((req = m_finishedRequests.pop()) != nullptr) {
delete req;
}
while ((req = m_nextRequests.pop()) != nullptr) {
if (req->m_deleteOnFinish) {
delete req;
}
}
if (m_currentRequest != nullptr) {
delete m_currentRequest;
m_currentRequest = nullptr;
} }
} }
/**
* @return the @a ProtocolHandler instance for accessing the bus.
*/
ProtocolHandler* getProtocol() const { return m_protocol; }
/** /**
* @return the @a Device instance for accessing the bus. * @return the @a Device instance for accessing the bus.
*/ */
const Device* getDevice() const { return m_device; } const Device* getDevice() const { return m_protocol->getDevice(); }
/** /**
* Clear stored values (e.g. scan results). * Clear stored values (e.g. scan results).
*/ */
void clear(); void clear();
/**
* Inject a message from outside and treat it as regularly retrieved from the bus.
* @param master the @a MasterSymbolString with the master data.
* @param slave the @a SlaveSymbolString with the slave data.
*/
void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) {
m_command = master;
m_response = slave;
m_addressConflict = true; // avoid conflict messages
messageCompleted();
m_addressConflict = false;
}
/**
* Send a message on the bus and wait for the answer.
* @param master the @a MasterSymbolString with the master data to send.
* @param slave the @a SlaveSymbolString that will be filled with retrieved slave data.
* @return the result code.
*/
result_t sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave);
/** /**
* Prepare the master part for the @a Message, send it to the bus and wait for the answer. * Prepare the master part for the @a Message, send it to the bus and wait for the answer.
* @param message the @a Message instance. * @param message the @a Message instance.
@@ -467,11 +306,6 @@ class BusHandler : public WaitThread {
result_t readFromBus(Message* message, const string& inputStr, symbol_t dstAddress = SYN, result_t readFromBus(Message* message, const string& inputStr, symbol_t dstAddress = SYN,
symbol_t srcAddress = SYN); symbol_t srcAddress = SYN);
/**
* Main thread entry.
*/
virtual void run();
/** /**
* Initiate a scan of the slave addresses. * Initiate a scan of the slave addresses.
* @param full true for a full scan (all slaves), false for scanning only already seen slaves. * @param full true for a full scan (all slaves), false for scanning only already seen slaves.
@@ -560,59 +394,6 @@ class BusHandler : public WaitThread {
void formatGrabResult(bool unknown, OutputFormat outputFormat, ostringstream* output, bool isDirectMode = false, void formatGrabResult(bool unknown, OutputFormat outputFormat, ostringstream* output, bool isDirectMode = false,
time_t since = 0, time_t until = 0) const; time_t since = 0, time_t until = 0) const;
/**
* Return true when a signal on the bus is available.
* @return true when a signal on the bus is available.
*/
bool hasSignal() const { return m_state != bs_noSignal; }
/**
* Reconnect the device.
*/
void reconnect() { m_reconnect = true; }
/**
* Return the current symbol rate.
* @return the number of received symbols in the last second.
*/
unsigned int getSymbolRate() const { return m_symPerSec; }
/**
* Return the maximum seen symbol rate.
* @return the maximum number of received symbols per second ever seen.
*/
unsigned int getMaxSymbolRate() const { return m_maxSymPerSec; }
/**
* Return the minimal measured latency between send and receive of a symbol.
* @return the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known.
*/
int getMinSymbolLatency() const { return m_symbolLatencyMin; }
/**
* Return the maximal measured latency between send and receive of a symbol.
* @return the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known.
*/
int getMaxSymbolLatency() const { return m_symbolLatencyMax; }
/**
* Return the minimal measured delay between received SYN and sent own master address in microseconds.
* @return the minimal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known.
*/
int getMinArbitrationDelay() const { return m_arbitrationDelayMin; }
/**
* Return the maximal measured delay between received SYN and sent own master address in microseconds.
* @return the maximal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known.
*/
int getMaxArbitrationDelay() const { return m_arbitrationDelayMax; }
/**
* Return the number of masters already seen.
* @return the number of masters already seen (including ebusd itself).
*/
unsigned int getMasterCount() const { return m_masterCount; }
/** /**
* Get the next slave address that still needs to be scanned or loaded. * Get the next slave address that still needs to be scanned or loaded.
* @param lastAddress the last returned slave address, or 0 for returning the first one. * @param lastAddress the last returned slave address, or 0 for returning the first one.
@@ -628,42 +409,19 @@ class BusHandler : public WaitThread {
*/ */
void setScanConfigLoaded(symbol_t address, const string& file); void setScanConfigLoaded(symbol_t address, const string& file);
// @copydoc
void notifyProtocolStatus(bool signal) override;
// @copydoc
result_t notifyProtocolAnswer(const MasterSymbolString& master, SlaveSymbolString* slave) override;
// @copydoc
void notifyProtocolSeenAddress(symbol_t address) override;
// @copydoc
void notifyProtocolMessage(bool sent, const MasterSymbolString& master, const SlaveSymbolString& slave) override;
private: private:
/**
* Handle the next symbol on the bus.
* @return RESULT_OK on success, or an error code.
*/
result_t handleSymbol();
/**
* Set a new @a BusState and add a log message if necessary.
* @param state the new @a BusState.
* @param result the result code.
* @param firstRepetition true if the first repetition of a message part is being started.
* @return the result code.
*/
result_t setState(BusState state, result_t result, bool firstRepetition = false);
/**
* Add a seen bus address.
* @param address the seen bus address.
* @return true if a conflict with the own addresses was detected, false otherwise.
*/
bool addSeenAddress(symbol_t address);
/**
* Called to measure the latency between send and receive of a symbol.
* @param sentTime the time the symbol was sent.
* @param recvTime the time the symbol was received.
*/
void measureLatency(struct timespec* sentTime, struct timespec* recvTime);
/**
* Called when a message sending or reception was successfully completed.
*/
void messageCompleted();
/** /**
* Prepare a @a ScanRequest. * Prepare a @a ScanRequest.
* @param slave the single slave address to scan, or @a SYN for multiple. * @param slave the single slave address to scan, or @a SYN for multiple.
@@ -675,11 +433,8 @@ class BusHandler : public WaitThread {
*/ */
result_t prepareScan(symbol_t slave, bool full, const string& levels, bool* reload, ScanRequest** request); result_t prepareScan(symbol_t slave, bool full, const string& levels, bool* reload, ScanRequest** request);
/** the @a Device instance for accessing the bus. */ /** the @a ProtocolHandler instance for accessing the bus. */
Device* m_device; ProtocolHandler* m_protocol;
/** set to @p true when the device shall be reconnected. */
bool m_reconnect;
/** the @a MessageMap instance with all known @a Message instances. */ /** the @a MessageMap instance with all known @a Message instances. */
MessageMap* m_messages; MessageMap* m_messages;
@@ -687,121 +442,15 @@ class BusHandler : public WaitThread {
/** the @a ScanHelper instance. */ /** the @a ScanHelper instance. */
ScanHelper* m_scanHelper; ScanHelper* m_scanHelper;
/** the own master address. */
const symbol_t m_ownMasterAddress;
/** the own slave address. */
const symbol_t m_ownSlaveAddress;
/** whether to answer queries for the own master/slave address. */
const bool m_answer;
/** set to @p true once an address conflict with the own addresses was detected. */
bool m_addressConflict;
/** the number of times a send is repeated due to lost arbitration. */
const unsigned int m_busLostRetries;
/** the number of times a failed send is repeated (other than lost arbitration). */
const unsigned int m_failedSendRetries;
/** the maximum time in milliseconds for bus acquisition. */
const unsigned int m_busAcquireTimeout;
/** the maximum time in milliseconds an addressed slave is expected to acknowledge. */
const unsigned int m_slaveRecvTimeout;
/** the number of masters already seen. */
unsigned int m_masterCount;
/** whether m_lockCount shall be detected automatically. */
const bool m_autoLockCount;
/** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */
unsigned int m_lockCount;
/** the remaining number of AUTO-SYN symbols before sending is allowed again. */
unsigned int m_remainLockCount;
/** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
unsigned int m_generateSynInterval;
/** the interval in seconds in which poll messages are cycled, or 0 if disabled. */ /** the interval in seconds in which poll messages are cycled, or 0 if disabled. */
const unsigned int m_pollInterval; const unsigned int m_pollInterval;
/** the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */
int m_symbolLatencyMin;
/** the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */
int m_symbolLatencyMax;
/**
* the minimal measured delay between received SYN and sent own master address in microseconds,
* -1 if not yet known.
*/
int m_arbitrationDelayMin;
/**
* the maximal measured delay between received SYN and sent own master address in microseconds,
* -1 if not yet known.
*/
int m_arbitrationDelayMax;
/** the time of the last received SYN symbol, or 0 for never. */
struct timespec m_lastSynReceiveTime;
/** the time of the last received symbol, or 0 for never. */
time_t m_lastReceive;
/** the time of the last poll, or 0 for never. */ /** the time of the last poll, or 0 for never. */
time_t m_lastPoll; time_t m_lastPoll;
/** the queue of @a BusRequests that shall be handled. */
Queue<BusRequest*> m_nextRequests;
/** the currently handled BusRequest, or nullptr. */
BusRequest* m_currentRequest;
/** whether currently answering a request from another participant. */
bool m_currentAnswering;
/** the queue of @a BusRequests that are already finished. */
Queue<BusRequest*> m_finishedRequests;
/** the number of scan requests currently running. */ /** the number of scan requests currently running. */
unsigned int m_runningScans; unsigned int m_runningScans;
/** the offset of the next symbol that needs to be sent from the command or response,
* (only relevant if m_request is set and state is @a bs_command or @a bs_response). */
size_t m_nextSendPos;
/** the number of received symbols in the last second. */
unsigned int m_symPerSec;
/** the maximum number of received symbols per second ever seen. */
unsigned int m_maxSymPerSec;
/** the current @a BusState. */
BusState m_state;
/** 0 when not escaping/unescaping, or @a ESC when receiving, or the original value when sending. */
symbol_t m_escape;
/** the calculated CRC. */
symbol_t m_crc;
/** whether the CRC matched. */
bool m_crcValid;
/** whether the current message part is being repeated. */
bool m_repeat;
/** the received command @a MasterSymbolString. */
MasterSymbolString m_command;
/** the received response @a SlaveSymbolString or response to send. */
SlaveSymbolString m_response;
/** the participating bus addresses seen so far (0 if not seen yet, or combination of @a SEEN bits). */ /** the participating bus addresses seen so far (0 if not seen yet, or combination of @a SEEN bits). */
symbol_t m_seenAddresses[256]; symbol_t m_seenAddresses[256];
+2 -2
View File
@@ -609,7 +609,7 @@ void KnxHandler::handleGroupTelegram(knx_addr_t src, knx_addr_t dest, int len, c
sendGlobalValue(GLOBAL_UPTIME, static_cast<unsigned>(time(nullptr) - m_start), true); sendGlobalValue(GLOBAL_UPTIME, static_cast<unsigned>(time(nullptr) - m_start), true);
break; break;
case GLOBAL_SIGNAL: case GLOBAL_SIGNAL:
sendGlobalValue(GLOBAL_SIGNAL, m_busHandler->hasSignal() ? 1 : 0, true); sendGlobalValue(GLOBAL_SIGNAL, m_busHandler->getProtocol()->hasSignal() ? 1 : 0, true);
break; break;
case GLOBAL_SCAN: case GLOBAL_SCAN:
sendGlobalValue(GLOBAL_SCAN, m_lastScanStatus == SCAN_STATUS_RUNNING ? 1 : 0, true); sendGlobalValue(GLOBAL_SCAN, m_lastScanStatus == SCAN_STATUS_RUNNING ? 1 : 0, true);
@@ -893,7 +893,7 @@ void KnxHandler::run() {
time(&lastTaskRun); time(&lastTaskRun);
} }
if (sendSignal) { if (sendSignal) {
if (m_busHandler->hasSignal()) { if (m_busHandler->getProtocol()->hasSignal()) {
lastSignal = now; lastSignal = now;
if (!signal || reconnected) { if (!signal || reconnected) {
signal = true; signal = true;
+1 -1
View File
@@ -412,7 +412,7 @@ int main(int argc, char* argv[], char* envp[]) {
if (!s_scanHelper->parseMessage(argv[arg_index++], false, &master, &slave)) { if (!s_scanHelper->parseMessage(argv[arg_index++], false, &master, &slave)) {
continue; continue;
} }
busHandler->injectMessage(master, slave); busHandler->getProtocol()->injectMessage(master, slave);
if (s_opt.scanConfig && master.size() >= 5 && master[4] == 0 && master[2] == 0x07 && master[3] == 0x04 if (s_opt.scanConfig && master.size() >= 5 && master[4] == 0 && master[2] == 0x07 && master[3] == 0x04
&& isValidAddress(master[1], false) && !isMaster(master[1]) && !scanAddresses[master[1]]) { && isValidAddress(master[1], false) && !isMaster(master[1]) && !scanAddresses[master[1]]) {
// scan message, simulate scanning // scan message, simulate scanning
+49 -41
View File
@@ -151,13 +151,20 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag
} }
} }
// create BusHandler // create BusHandler
ebus_protocol_config_t config = {
.ownAddress = m_address,
.answer = opt.answer,
.busLostRetries = opt.acquireRetries,
.failedSendRetries = opt.sendRetries,
.busAcquireTimeout = opt.acquireTimeout,
.slaveRecvTimeout = opt.receiveTimeout,
.lockCount = opt.masterCount,
.generateSyn = opt.generateSyn,
};
m_busHandler = new BusHandler(m_device, m_messages, scanHelper, m_busHandler = new BusHandler(m_device, m_messages, scanHelper,
m_address, opt.answer, config, opt.pollInterval);
opt.acquireRetries, opt.sendRetries, m_protocol = m_busHandler->getProtocol();
opt.acquireTimeout, opt.receiveTimeout, m_protocol->start("bushandler");
opt.masterCount, opt.generateSyn,
opt.pollInterval);
m_busHandler->start("bushandler");
// create network // create network
m_htmlPath = opt.htmlPath; m_htmlPath = opt.htmlPath;
@@ -194,6 +201,7 @@ MainLoop::~MainLoop() {
if (m_busHandler != nullptr) { if (m_busHandler != nullptr) {
delete m_busHandler; delete m_busHandler;
m_busHandler = nullptr; m_busHandler = nullptr;
m_protocol = nullptr; // ProtocolHandler is freed by BusHandler
} }
if (m_device != nullptr) { if (m_device != nullptr) {
delete m_device; delete m_device;
@@ -255,16 +263,16 @@ void MainLoop::run() {
lastTaskRun = now; lastTaskRun = now;
} else if (!m_shutdown && now > lastTaskRun+taskDelay) { } else if (!m_shutdown && now > lastTaskRun+taskDelay) {
logDebug(lf_main, "performing regular tasks"); logDebug(lf_main, "performing regular tasks");
if (m_busHandler->hasSignal()) { if (m_protocol->hasSignal()) {
lastSignal = now; lastSignal = now;
} else if (lastSignal && now > lastSignal+RECONNECT_MISSING_SIGNAL) { } else if (lastSignal && now > lastSignal+RECONNECT_MISSING_SIGNAL) {
lastSignal = 0; lastSignal = 0;
m_busHandler->reconnect(); m_protocol->reconnect();
m_reconnectCount++; m_reconnectCount++;
} }
if (m_scanConfig && scanRetry <= m_scanRetries) { if (m_scanConfig && scanRetry <= m_scanRetries) {
bool loadDelay = false; bool loadDelay = false;
if (m_initialScan != ESC && reload && m_busHandler->hasSignal()) { if (m_initialScan != ESC && reload && m_protocol->hasSignal()) {
loadDelay = true; loadDelay = true;
result_t result; result_t result;
if (m_initialScan == SYN) { if (m_initialScan == SYN) {
@@ -282,7 +290,7 @@ void MainLoop::run() {
istringstream input; istringstream input;
result = message->prepareMaster(0, m_address, SYN, UI_FIELD_SEPARATOR, &input, &master); result = message->prepareMaster(0, m_address, SYN, UI_FIELD_SEPARATOR, &input, &master);
if (result == RESULT_OK) { if (result == RESULT_OK) {
result = m_busHandler->sendAndWait(master, &slave); result = m_protocol->sendAndWait(master, &slave);
} }
} else { } else {
result = RESULT_ERR_NOTFOUND; result = RESULT_ERR_NOTFOUND;
@@ -304,7 +312,7 @@ void MainLoop::run() {
reload = false; reload = false;
} }
} }
if (!loadDelay && m_busHandler->hasSignal()) { if (!loadDelay && m_protocol->hasSignal()) {
lastScanAddress = m_busHandler->getNextScanAddress(lastScanAddress, scanCompleted >= SCAN_REPEAT_COUNT); lastScanAddress = m_busHandler->getNextScanAddress(lastScanAddress, scanCompleted >= SCAN_REPEAT_COUNT);
if (lastScanAddress == SYN) { if (lastScanAddress == SYN) {
taskDelay = 5; taskDelay = 5;
@@ -335,7 +343,7 @@ void MainLoop::run() {
dataSink->notifyScanStatus(lastScanStatus); dataSink->notifyScanStatus(lastScanStatus);
} }
} }
} else if (reload && m_busHandler->hasSignal()) { } else if (reload && m_protocol->hasSignal()) {
reload = false; reload = false;
// execute initial instructions // execute initial instructions
m_scanHelper->executeInstructions(m_busHandler); m_scanHelper->executeInstructions(m_busHandler);
@@ -873,7 +881,7 @@ result_t MainLoop::executeRead(const vector<string>& args, const string& levels,
// send message // send message
SlaveSymbolString slave; SlaveSymbolString slave;
ret = m_busHandler->sendAndWait(master, &slave); ret = m_protocol->sendAndWait(master, &slave);
if (ret == RESULT_OK) { if (ret == RESULT_OK) {
ret = message->storeLastData(master, slave); ret = message->storeLastData(master, slave);
@@ -1095,7 +1103,7 @@ result_t MainLoop::executeWrite(const vector<string>& args, const string levels,
} }
// send message // send message
SlaveSymbolString slave; SlaveSymbolString slave;
ret = m_busHandler->sendAndWait(master, &slave); ret = m_protocol->sendAndWait(master, &slave);
if (ret == RESULT_OK) { if (ret == RESULT_OK) {
// also update read messages // also update read messages
@@ -1227,7 +1235,7 @@ result_t MainLoop::parseHexAndSend(const vector<string>& args, size_t& argPos, b
// send message // send message
SlaveSymbolString slave; SlaveSymbolString slave;
ret = m_busHandler->sendAndWait(master, &slave); ret = m_protocol->sendAndWait(master, &slave);
if (ret == RESULT_OK) { if (ret == RESULT_OK) {
if (master[1] == BROADCAST) { if (master[1] == BROADCAST) {
@@ -1601,11 +1609,11 @@ result_t MainLoop::executeState(const vector<string>& args, ostringstream* ostre
" Report bus state."; " Report bus state.";
return RESULT_OK; return RESULT_OK;
} }
if (m_busHandler->hasSignal()) { if (m_protocol->hasSignal()) {
*ostream << "signal acquired, " *ostream << "signal acquired, "
<< m_busHandler->getSymbolRate() << " symbols/sec (" << m_protocol->getSymbolRate() << " symbols/sec ("
<< m_busHandler->getMaxSymbolRate() << " max), " << m_protocol->getMaxSymbolRate() << " max), "
<< m_busHandler->getMasterCount() << " masters"; << m_protocol->getMasterCount() << " masters";
return RESULT_OK; return RESULT_OK;
} }
return RESULT_ERR_NO_SIGNAL; return RESULT_ERR_NO_SIGNAL;
@@ -1818,7 +1826,7 @@ result_t MainLoop::executeScan(const vector<string>& args, const string& levels,
return RESULT_OK; return RESULT_OK;
} }
if (!m_busHandler->hasSignal()) { if (!m_protocol->hasSignal()) {
return RESULT_ERR_NO_SIGNAL; return RESULT_ERR_NO_SIGNAL;
} }
result_t result; result_t result;
@@ -1941,17 +1949,17 @@ result_t MainLoop::executeInfo(const vector<string>& args, const string& user, o
if (!user.empty() || !levels.empty()) { if (!user.empty() || !levels.empty()) {
*ostream << "access: " << levels << "\n"; *ostream << "access: " << levels << "\n";
} }
if (m_busHandler->hasSignal()) { if (m_protocol->hasSignal()) {
*ostream << "signal: acquired\n" *ostream << "signal: acquired\n"
<< "symbol rate: " << m_busHandler->getSymbolRate() << "\n" << "symbol rate: " << m_protocol->getSymbolRate() << "\n"
<< "max symbol rate: " << m_busHandler->getMaxSymbolRate() << "\n"; << "max symbol rate: " << m_protocol->getMaxSymbolRate() << "\n";
if (m_busHandler->getMinArbitrationDelay() >= 0) { if (m_protocol->getMinArbitrationDelay() >= 0) {
*ostream << "min arbitration micros: " << m_busHandler->getMinArbitrationDelay() << "\n" *ostream << "min arbitration micros: " << m_protocol->getMinArbitrationDelay() << "\n"
<< "max arbitration micros: " << m_busHandler->getMaxArbitrationDelay() << "\n"; << "max arbitration micros: " << m_protocol->getMaxArbitrationDelay() << "\n";
} }
if (m_busHandler->getMinSymbolLatency() >= 0) { if (m_protocol->getMinSymbolLatency() >= 0) {
*ostream << "min symbol latency: " << m_busHandler->getMinSymbolLatency() << "\n" *ostream << "min symbol latency: " << m_protocol->getMinSymbolLatency() << "\n"
<< "max symbol latency: " << m_busHandler->getMaxSymbolLatency() << "\n"; << "max symbol latency: " << m_protocol->getMaxSymbolLatency() << "\n";
} }
if (m_scanStatus != SCAN_STATUS_NONE) { if (m_scanStatus != SCAN_STATUS_NONE) {
*ostream << "scan: " << (m_scanStatus == SCAN_STATUS_FINISHED ? "finished" : "running"); *ostream << "scan: " << (m_scanStatus == SCAN_STATUS_FINISHED ? "finished" : "running");
@@ -1965,7 +1973,7 @@ result_t MainLoop::executeInfo(const vector<string>& args, const string& user, o
*ostream << "signal: no signal\n"; *ostream << "signal: no signal\n";
} }
*ostream << "reconnects: " << m_reconnectCount << "\n" *ostream << "reconnects: " << m_reconnectCount << "\n"
<< "masters: " << m_busHandler->getMasterCount() << "\n" << "masters: " << m_protocol->getMasterCount() << "\n"
<< "messages: " << m_messages->size() << "\n" << "messages: " << m_messages->size() << "\n"
<< "conditional: " << m_messages->sizeConditional() << "\n" << "conditional: " << m_messages->sizeConditional() << "\n"
<< "poll: " << m_messages->sizePoll() << "\n" << "poll: " << m_messages->sizePoll() << "\n"
@@ -2210,24 +2218,24 @@ result_t MainLoop::executeGet(const vector<string>& args, bool* connected, ostri
if (!user.empty() || !levels.empty()) { if (!user.empty() || !levels.empty()) {
*ostream << ",\n \"access\": \"" << levels << "\""; *ostream << ",\n \"access\": \"" << levels << "\"";
} }
*ostream << ",\n \"signal\": " << (m_busHandler->hasSignal() ? "true" : "false"); *ostream << ",\n \"signal\": " << (m_protocol->hasSignal() ? "true" : "false");
if (m_busHandler->hasSignal()) { if (m_protocol->hasSignal()) {
*ostream << ",\n \"symbolrate\": " << m_busHandler->getSymbolRate() *ostream << ",\n \"symbolrate\": " << m_protocol->getSymbolRate()
<< ",\n \"maxsymbolrate\": " << m_busHandler->getMaxSymbolRate(); << ",\n \"maxsymbolrate\": " << m_protocol->getMaxSymbolRate();
if (m_busHandler->getMinArbitrationDelay() >= 0) { if (m_protocol->getMinArbitrationDelay() >= 0) {
*ostream << ",\n \"minarbitrationmicros\": " << m_busHandler->getMinArbitrationDelay() *ostream << ",\n \"minarbitrationmicros\": " << m_protocol->getMinArbitrationDelay()
<< ",\n \"maxarbitrationmicros\": " << m_busHandler->getMaxArbitrationDelay(); << ",\n \"maxarbitrationmicros\": " << m_protocol->getMaxArbitrationDelay();
} }
if (m_busHandler->getMinSymbolLatency() >= 0) { if (m_protocol->getMinSymbolLatency() >= 0) {
*ostream << ",\n \"minsymbollatency\": " << m_busHandler->getMinSymbolLatency() *ostream << ",\n \"minsymbollatency\": " << m_protocol->getMinSymbolLatency()
<< ",\n \"maxsymbollatency\": " << m_busHandler->getMaxSymbolLatency(); << ",\n \"maxsymbollatency\": " << m_protocol->getMaxSymbolLatency();
} }
} }
if (!m_device->isReadOnly()) { if (!m_device->isReadOnly()) {
*ostream << ",\n \"qq\": " << static_cast<unsigned>(m_address); *ostream << ",\n \"qq\": " << static_cast<unsigned>(m_address);
} }
*ostream << ",\n \"reconnects\": " << m_reconnectCount *ostream << ",\n \"reconnects\": " << m_reconnectCount
<< ",\n \"masters\": " << m_busHandler->getMasterCount() << ",\n \"masters\": " << m_protocol->getMasterCount()
<< ",\n \"messages\": " << m_messages->size() << ",\n \"messages\": " << m_messages->size()
<< ",\n \"lastup\": " << static_cast<unsigned>(maxLastUp) << ",\n \"lastup\": " << static_cast<unsigned>(maxLastUp)
<< "\n }" << "\n }"
+3
View File
@@ -443,6 +443,9 @@ class MainLoop : public Thread, DeviceListener {
/** the created @a BusHandler instance. */ /** the created @a BusHandler instance. */
BusHandler* m_busHandler; BusHandler* m_busHandler;
/** the created @a ProtocolHandler instance. */
ProtocolHandler* m_protocol;
/** the reference to the @a Request @a Queue. */ /** the reference to the @a Request @a Queue. */
Queue<Request*>* m_requestQueue; Queue<Request*>* m_requestQueue;
+1 -1
View File
@@ -1041,7 +1041,7 @@ void MqttHandler::run() {
time(&lastTaskRun); time(&lastTaskRun);
} }
if (sendSignal) { if (sendSignal) {
if (m_busHandler->hasSignal()) { if (m_busHandler->getProtocol()->hasSignal()) {
lastSignal = now; lastSignal = now;
if (!signal || reconnected) { if (!signal || reconnected) {
signal = true; signal = true;
+2
View File
@@ -7,6 +7,8 @@ set(libebus_a_SOURCES
datatype.h datatype.cpp datatype.h datatype.cpp
data.h data.cpp data.h data.cpp
device.h device.cpp device.h device.cpp
protocol.h protocol.cpp
protocol_direct.h protocol_direct.cpp
message.h message.cpp message.h message.cpp
stringhelper.h stringhelper.cpp stringhelper.h stringhelper.cpp
) )
+2
View File
@@ -11,6 +11,8 @@ libebus_a_SOURCES = \
datatype.h datatype.cpp \ datatype.h datatype.cpp \
data.h data.cpp \ data.h data.cpp \
device.h device.cpp \ device.h device.cpp \
protocol.h protocol.cpp \
protocol_direct.h protocol_direct.cpp \
message.h message.cpp \ message.h message.cpp \
stringhelper.h stringhelper.cpp stringhelper.h stringhelper.cpp
+140
View File
@@ -0,0 +1,140 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2023 John Baier <ebusd@ebusd.eu>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "lib/ebus/protocol.h"
#include "lib/ebus/protocol_direct.h"
#include <string>
#include "lib/utils/log.h"
namespace ebusd {
bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) {
if (result == RESULT_OK) {
string str = slave.getStr();
logDebug(lf_bus, "read res: %s", str.c_str());
}
m_result = result;
*m_slave = slave;
return false;
}
ProtocolHandler* ProtocolHandler::create(const ebus_protocol_config_t config,
Device* device, ProtocolListener* listener) {
return new DirectProtocolHandler(config, device, listener);
}
void ProtocolHandler::clear() {
memset(m_seenAddresses, 0, sizeof(m_seenAddresses));
m_masterCount = 1;
}
bool ProtocolHandler::addRequest(BusRequest* request, bool wait) {
m_nextRequests.push(request);
return !wait || m_finishedRequests.remove(request, true);
}
result_t ProtocolHandler::sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave) {
if (!hasSignal()) {
return RESULT_ERR_NO_SIGNAL; // don't wait when there is no signal
}
result_t result = RESULT_ERR_NO_SIGNAL;
slave->clear();
ActiveBusRequest request(master, slave);
logInfo(lf_bus, "send message: %s", master.getStr().c_str());
for (int sendRetries = m_config.failedSendRetries + 1; sendRetries > 0; sendRetries--) {
bool success = addRequest(&request, true);
result = success ? request.m_result : RESULT_ERR_TIMEOUT;
if (result == RESULT_OK) {
break;
}
if (!success || result == RESULT_ERR_NO_SIGNAL || result == RESULT_ERR_SEND || result == RESULT_ERR_DEVICE) {
logError(lf_bus, "send to %2.2x: %s, give up", master[1], getResultCode(result));
break;
}
logError(lf_bus, "send to %2.2x: %s%s", master[1], getResultCode(result), sendRetries > 1 ? ", retry" : "");
request.m_busLostRetries = 0;
}
return result;
}
void ProtocolHandler::measureLatency(struct timespec* sentTime, struct timespec* recvTime) {
int64_t latencyLong = (recvTime->tv_sec*1000000000 + recvTime->tv_nsec
- sentTime->tv_sec*1000000000 - sentTime->tv_nsec)/1000000;
if (latencyLong < 0 || latencyLong > 1000) {
return; // clock skew or out of reasonable range
}
auto latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "send/receive symbol latency %d ms", latency);
if (m_symbolLatencyMin >= 0 && (latency >= m_symbolLatencyMin && latency <= m_symbolLatencyMax)) {
return;
}
if (m_symbolLatencyMin == -1 || latency < m_symbolLatencyMin) {
m_symbolLatencyMin = latency;
}
if (m_symbolLatencyMax == -1 || latency > m_symbolLatencyMax) {
m_symbolLatencyMax = latency;
}
logInfo(lf_bus, "send/receive symbol latency %d - %d ms", m_symbolLatencyMin, m_symbolLatencyMax);
}
bool ProtocolHandler::addSeenAddress(symbol_t address) {
if (!isValidAddress(address, false)) {
return false;
}
if (!isMaster(address)) {
if (!m_device->isReadOnly() && address == m_ownSlaveAddress) {
if (!m_addressConflict) {
m_addressConflict = true;
logError(lf_bus, "own slave address %2.2x is used by another participant", address);
}
}
if (!m_seenAddresses[address]) {
m_listener->notifyProtocolSeenAddress(address);
}
m_seenAddresses[address] = true;
address = getMasterAddress(address);
if (address == SYN) {
return false;
}
}
if (m_seenAddresses[address]) {
return false;
}
bool ret = false;
if (!m_device->isReadOnly() && address == m_ownMasterAddress) {
if (!m_addressConflict) {
m_addressConflict = true;
logError(lf_bus, "own master address %2.2x is used by another participant", address);
}
} else {
m_masterCount++;
ret = true;
logNotice(lf_bus, "new master %2.2x, master count %d", address, m_masterCount);
}
m_listener->notifyProtocolSeenAddress(address);
m_seenAddresses[address] = true;
return ret;
}
} // namespace ebusd
+477
View File
@@ -0,0 +1,477 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2023 John Baier <ebusd@ebusd.eu>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_EBUS_PROTOCOL_H_
#define LIB_EBUS_PROTOCOL_H_
#include "lib/ebus/symbol.h"
#include "lib/ebus/result.h"
#include "lib/ebus/device.h"
#include "lib/utils/queue.h"
#include "lib/utils/thread.h"
namespace ebusd {
/** @file lib/ebus/protocol.h
* Classes, functions, and constants related to handling the eBUS protocol.
*/
/** the default time [ms] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15
/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 51
/** the time [ms] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250
/** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION_MICROS 4700
/** the maximum duration [ms] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION 5
/** the maximum allowed time [ms] for retrieving back a sent symbol (2x symbol duration). */
#define SEND_TIMEOUT ((int)((2*SYMBOL_DURATION_MICROS+999)/1000))
/** settings for the eBUS protocol handler. */
typedef struct ebus_protocol_config {
/** the own master address. */
symbol_t ownAddress;
/** whether to answer queries for the own master/slave address. */
bool answer;
/** the number of times a send is repeated due to lost arbitration. */
unsigned int busLostRetries;
/** the number of times a failed send is repeated (other than lost arbitration). */
unsigned int failedSendRetries;
/** the maximum time in milliseconds for bus acquisition. */
unsigned int busAcquireTimeout;
/** the maximum time in milliseconds an addressed slave is expected to acknowledge. */
unsigned int slaveRecvTimeout;
/** the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. */
unsigned int lockCount;
/** whether to enable AUTO-SYN symbol generation. */
bool generateSyn;
} ebus_protocol_config_t;
class ProtocolHandler;
/**
* Generic request for sending to and receiving from the bus.
*/
class BusRequest {
friend class ProtocolHandler;
public:
/**
* Constructor.
* @param master the master data @a MasterSymbolString to send.
* @param deleteOnFinish whether to automatically delete this @a BusRequest when finished.
*/
BusRequest(const MasterSymbolString& master, bool deleteOnFinish)
: m_master(master), m_busLostRetries(0),
m_deleteOnFinish(deleteOnFinish) {}
/**
* Destructor.
*/
virtual ~BusRequest() {}
/**
* @return the master data @a MasterSymbolString to send.
*/
const MasterSymbolString& getMaster() const { return m_master; }
/**
* @return the number of times a send was repeated due to lost arbitration.
*/
unsigned int getBusLostRetries() const { return m_busLostRetries; }
/**
* Increment the number of times a send was repeated due to lost arbitration.
*/
void incrementBusLostRetries() { m_busLostRetries++; }
/**
* Reset the number of times a send was repeated due to lost arbitration.
*/
void resetBusLostRetries() { m_busLostRetries = 0; }
/**
* @return whether to automatically delete this @a BusRequest when finished.
*/
bool deleteOnFinish() const { return m_deleteOnFinish; }
/**
* Notify the request of the specified result.
* @param result the result of the request.
* @param slave the @a SlaveSymbolString received.
* @return true if the request needs to be restarted.
*/
virtual bool notify(result_t result, const SlaveSymbolString& slave) = 0; // abstract
protected:
/** the master data @a MasterSymbolString to send. */
const MasterSymbolString& m_master;
/** the number of times a send was repeated due to lost arbitration. */
unsigned int m_busLostRetries;
/** whether to automatically delete this @a BusRequest when finished. */
const bool m_deleteOnFinish;
};
/**
* An active @a BusRequest that can be waited for.
*/
class ActiveBusRequest : public BusRequest {
friend class ProtocolHandler;
public:
/**
* Constructor.
* @param master the master data @a MasterSymbolString to send.
* @param slave reference to @a SlaveSymbolString for filling in the received slave data.
*/
ActiveBusRequest(const MasterSymbolString& master, SlaveSymbolString* slave)
: BusRequest(master, false), m_result(RESULT_ERR_NO_SIGNAL), m_slave(slave) {}
/**
* Destructor.
*/
virtual ~ActiveBusRequest() {}
// @copydoc
bool notify(result_t result, const SlaveSymbolString& slave) override;
private:
/** the result of handling the request. */
result_t m_result;
/** reference to @a SlaveSymbolString for filling in the received slave data. */
SlaveSymbolString* m_slave;
};
/**
* Interface for listening to eBUS protocol data.
*/
class ProtocolListener {
public:
/**
* Destructor.
*/
virtual ~ProtocolListener() {}
/**
* Called to notify a status update from the protocol.
* @param signal true when signal is acquired, false otherwise.
*/
virtual void notifyProtocolStatus(bool signal) = 0; // abstract
/**
* Called to notify a new valid seen address on the bus.
* @param address the seen address.
*/
virtual void notifyProtocolSeenAddress(symbol_t address) = 0; // abstract
/**
* Listener method that is called when a message was sent or received.
* @param sent true when the master part was actively sent, false if the whole message
* was received only.
* @param master the @a MasterSymbolString received.
* @param slave the @a SlaveSymbolString received.
*/
virtual void notifyProtocolMessage(bool sent, const MasterSymbolString& master,
const SlaveSymbolString& slave) = 0; // abstract
/**
* Listener method that is called when in answer mode and a message targeting ourself was received.
* @param master the @a MasterSymbolString received.
* @param slave the @a SlaveSymbolString for writing the response to.
* @return @a RESULT_OK on success, or an error code.
*/
virtual result_t notifyProtocolAnswer(const MasterSymbolString& master,
SlaveSymbolString* slave) = 0; // abstract
};
/**
* Handles input from and output to eBUS with respect to the eBUS protocol.
*/
class ProtocolHandler : public WaitThread {
public:
/**
* Construct a new instance.
* @param config the configuration to use.
* @param device the @a Device instance for accessing the bus.
* @param listener the @a ProtocolListener.
*/
ProtocolHandler(const ebus_protocol_config_t config,
Device* device, ProtocolListener* listener)
: WaitThread(), m_config(config), m_device(device), m_listener(listener),
m_reconnect(false),
m_ownMasterAddress(config.ownAddress), m_ownSlaveAddress(getSlaveAddress(config.ownAddress)),
m_addressConflict(false),
m_masterCount(device->isReadOnly()?0:1),
m_symbolLatencyMin(-1), m_symbolLatencyMax(-1), m_arbitrationDelayMin(-1),
m_arbitrationDelayMax(-1), m_lastReceive(0),
m_symPerSec(0), m_maxSymPerSec(0) {
memset(m_seenAddresses, 0, sizeof(m_seenAddresses));
}
/**
* Destructor.
*/
virtual ~ProtocolHandler() {
stop();
join();
BusRequest* req;
while ((req = m_finishedRequests.pop()) != nullptr) {
delete req;
}
while ((req = m_nextRequests.pop()) != nullptr) {
if (req->m_deleteOnFinish) {
delete req;
}
}
}
/**
* Create a new instance.
* @param config the configuration to use.
* @param device the @a Device instance for accessing the bus.
* @param listener the @a ProtocolListener.
* @return the new ProtocolHandler, or @a nullptr on error.
*/
static ProtocolHandler* create(const ebus_protocol_config_t config, Device* device, ProtocolListener* listener);
/**
* @return the own master address.
*/
symbol_t getOwnMasterAddress() const { return m_ownMasterAddress; }
/**
* @return the own slave address.
*/
symbol_t getOwnSlaveAddress() const { return m_ownSlaveAddress; }
/**
* @return @p true if answering queries for the own master/slave address (if not readonly).
*/
bool isAnswering() const { return m_config.answer; }
/**
* @param address the address to check.
* @return @p true when the address is the own master or slave address (if not readonly).
*/
bool isOwnAddress(symbol_t address) const {
return !m_device->isReadOnly() && (address == m_ownMasterAddress || address == m_ownSlaveAddress);
}
/**
* @param address the own address to check for conflict or @a SYN for any.
* @return @p true when an address conflict with any of the own addresses or the specified own address was detected.
*/
bool isAddressConflict(symbol_t address) const {
return m_addressConflict && (address == SYN || m_seenAddresses[address]);
}
/**
* @return the maximum number of received symbols per second ever seen.
*/
unsigned int getMaxSymPerSec() const { return m_maxSymPerSec; }
/**
* @return the @a Device instance for accessing the bus.
*/
const Device* getDevice() const { return m_device; }
/**
* Clear stored values (e.g. scan results).
*/
virtual void clear();
/**
* Inject a message from outside and treat it as regularly retrieved from the bus.
* This may only be called before bus handling was actually started.
* @param master the @a MasterSymbolString with the master data.
* @param slave the @a SlaveSymbolString with the slave data.
*/
virtual void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) = 0; // abstract
/**
* Add a @a BusRequest to the internal queue and optionally wait for it to complete.
* @param request the @a BusRequest to add.
* @param wait true to wait for it to complete, false to return immediately.
* @return true when it was not waited for or when it was completed.
*/
virtual bool addRequest(BusRequest* request, bool wait);
/**
* Send a message on the bus and wait for the answer.
* @param master the @a MasterSymbolString with the master data to send.
* @param slave the @a SlaveSymbolString that will be filled with retrieved slave data.
* @return the result code.
*/
virtual result_t sendAndWait(const MasterSymbolString& master, SlaveSymbolString* slave);
/**
* Main thread entry.
*/
virtual void run() = 0; // abstract
/**
* Return true when a signal on the bus is available.
* @return true when a signal on the bus is available.
*/
virtual bool hasSignal() const = 0; // abstract
/**
* Reconnect the device.
*/
virtual void reconnect() { m_reconnect = true; }
/**
* Return the current symbol rate.
* @return the number of received symbols in the last second.
*/
unsigned int getSymbolRate() const { return m_symPerSec; }
/**
* Return the maximum seen symbol rate.
* @return the maximum number of received symbols per second ever seen.
*/
unsigned int getMaxSymbolRate() const { return m_maxSymPerSec; }
/**
* Return the minimal measured latency between send and receive of a symbol.
* @return the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known.
*/
int getMinSymbolLatency() const { return m_symbolLatencyMin; }
/**
* Return the maximal measured latency between send and receive of a symbol.
* @return the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known.
*/
int getMaxSymbolLatency() const { return m_symbolLatencyMax; }
/**
* Return the minimal measured delay between received SYN and sent own master address in microseconds.
* @return the minimal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known.
*/
int getMinArbitrationDelay() const { return m_arbitrationDelayMin; }
/**
* Return the maximal measured delay between received SYN and sent own master address in microseconds.
* @return the maximal measured delay between received SYN and sent own master address in microseconds, -1 if not yet known.
*/
int getMaxArbitrationDelay() const { return m_arbitrationDelayMax; }
/**
* Return the number of masters already seen.
* @return the number of masters already seen (including ebusd itself).
*/
unsigned int getMasterCount() const { return m_masterCount; }
protected:
/**
* Called to measure the latency between send and receive of a symbol.
* @param sentTime the time the symbol was sent.
* @param recvTime the time the symbol was received.
*/
virtual void measureLatency(struct timespec* sentTime, struct timespec* recvTime);
/**
* Add a seen bus address.
* @param address the seen bus address.
* @return true if a conflict with the own addresses was detected, false otherwise.
*/
virtual bool addSeenAddress(symbol_t address);
/** the client configuration to use. */
const ebus_protocol_config_t m_config;
/** the @a Device instance for accessing the bus. */
Device* m_device;
/** the @a ProtocolListener. */
ProtocolListener *m_listener;
/** set to @p true when the device shall be reconnected. */
bool m_reconnect;
/** the own master address. */
const symbol_t m_ownMasterAddress;
/** the own slave address. */
const symbol_t m_ownSlaveAddress;
/** set to @p true once an address conflict with the own addresses was detected. */
bool m_addressConflict;
/** the number of masters already seen. */
unsigned int m_masterCount;
/** the minimal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */
int m_symbolLatencyMin;
/** the maximal measured latency between send and receive of a symbol in milliseconds, -1 if not yet known. */
int m_symbolLatencyMax;
/**
* the minimal measured delay between received SYN and sent own master address in microseconds,
* -1 if not yet known.
*/
int m_arbitrationDelayMin;
/**
* the maximal measured delay between received SYN and sent own master address in microseconds,
* -1 if not yet known.
*/
int m_arbitrationDelayMax;
/** the time of the last received symbol, or 0 for never. */
time_t m_lastReceive;
/** the queue of @a BusRequests that shall be handled. */
Queue<BusRequest*> m_nextRequests;
/** the queue of @a BusRequests that are already finished. */
Queue<BusRequest*> m_finishedRequests;
/** the number of received symbols in the last second. */
unsigned int m_symPerSec;
/** the maximum number of received symbols per second ever seen. */
unsigned int m_maxSymPerSec;
/** the participating bus addresses seen so far. */
bool m_seenAddresses[256];
};
} // namespace ebusd
#endif // LIB_EBUS_PROTOCOL_H_
+762
View File
@@ -0,0 +1,762 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2023 John Baier <ebusd@ebusd.eu>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "lib/ebus/protocol_direct.h"
#include "lib/utils/log.h"
namespace ebusd {
/**
* Return the string corresponding to the @a BusState.
* @param state the @a BusState.
* @return the string corresponding to the @a BusState.
*/
const char* getStateCode(BusState state) {
switch (state) {
case bs_noSignal: return "no signal";
case bs_skip: return "skip";
case bs_ready: return "ready";
case bs_sendCmd: return "send command";
case bs_recvCmdCrc: return "receive command CRC";
case bs_recvCmdAck: return "receive command ACK";
case bs_recvRes: return "receive response";
case bs_recvResCrc: return "receive response CRC";
case bs_sendResAck: return "send response ACK";
case bs_recvCmd: return "receive command";
case bs_recvResAck: return "receive response ACK";
case bs_sendCmdCrc: return "send command CRC";
case bs_sendCmdAck: return "send command ACK";
case bs_sendRes: return "send response";
case bs_sendResCrc: return "send response CRC";
case bs_sendSyn: return "send SYN";
default: return "unknown";
}
}
void DirectProtocolHandler::run() {
unsigned int symCount = 0;
time_t now, lastTime;
time(&lastTime);
lastTime += 2;
logNotice(lf_bus, "bus started with own address %2.2x/%2.2x%s", m_ownMasterAddress, m_ownSlaveAddress,
m_config.answer?" in answer mode":"");
do {
if (m_device->isValid() && !m_reconnect) {
result_t result = handleSymbol();
time(&now);
if (result != RESULT_ERR_TIMEOUT && now >= lastTime) {
symCount++;
}
if (now > lastTime) {
m_symPerSec = symCount / (unsigned int)(now-lastTime);
if (m_symPerSec > m_maxSymPerSec) {
m_maxSymPerSec = m_symPerSec;
if (m_maxSymPerSec > 100) {
logNotice(lf_bus, "max. symbols per second: %d", m_maxSymPerSec);
}
}
lastTime = now;
symCount = 0;
}
} else {
if (!m_device->isValid()) {
logNotice(lf_bus, "device invalid");
setState(bs_noSignal, RESULT_ERR_DEVICE);
}
if (!Wait(5)) {
break;
}
m_reconnect = false;
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));
setState(bs_noSignal, result);
}
symCount = 0;
m_symbolLatencyMin = m_symbolLatencyMax = m_arbitrationDelayMin = m_arbitrationDelayMax = -1;
time(&lastTime);
lastTime += 2;
}
} while (isRunning());
}
#ifndef FALLTHROUGH
#if defined(__GNUC__) && __GNUC__ >= 7
#define FALLTHROUGH [[fallthrough]];
#else
#define FALLTHROUGH
#endif
#endif
result_t DirectProtocolHandler::handleSymbol() {
unsigned int timeout = SYN_TIMEOUT;
symbol_t sendSymbol = ESC;
bool sending = false;
// check if another symbol has to be sent and determine timeout for receive
switch (m_state) {
case bs_noSignal:
timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT;
break;
case bs_skip:
timeout = SYN_TIMEOUT;
FALLTHROUGH
case bs_ready:
if (m_currentRequest != nullptr) {
setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up
}
if (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) {
BusRequest* startRequest = m_nextRequests.peek();
if (startRequest != nullptr) { // initiate arbitration
symbol_t master = startRequest->getMaster()[0];
logDebug(lf_bus, "start request %2.2x", master);
result_t ret = m_device->startArbitration(master);
if (ret == RESULT_OK) {
logDebug(lf_bus, "arbitration start with %2.2x", master);
} else {
logError(lf_bus, "arbitration start: %s", getResultCode(ret));
m_nextRequests.remove(startRequest);
m_currentRequest = startRequest;
setState(bs_ready, ret); // force the failed request to be notified
}
}
}
break;
case bs_recvCmd:
case bs_recvCmdCrc:
timeout = m_config.slaveRecvTimeout;
break;
case bs_recvCmdAck:
timeout = m_config.slaveRecvTimeout;
break;
case bs_recvRes:
case bs_recvResCrc:
if (m_response.size() > 0 || m_config.slaveRecvTimeout > SYN_TIMEOUT) {
timeout = m_config.slaveRecvTimeout;
} else {
timeout = SYN_TIMEOUT;
}
break;
case bs_recvResAck:
timeout = m_config.slaveRecvTimeout;
break;
case bs_sendCmd:
if (m_currentRequest != nullptr) {
sendSymbol = m_currentRequest->getMaster()[m_nextSendPos]; // unescaped command
sending = true;
}
break;
case bs_sendCmdCrc:
if (m_currentRequest != nullptr) {
sendSymbol = m_crc;
sending = true;
}
break;
case bs_sendResAck:
if (m_currentRequest != nullptr) {
sendSymbol = m_crcValid ? ACK : NAK;
sending = true;
}
break;
case bs_sendCmdAck:
if (m_config.answer) {
sendSymbol = m_crcValid ? ACK : NAK;
sending = true;
}
break;
case bs_sendRes:
if (m_config.answer) {
sendSymbol = m_response[m_nextSendPos]; // unescaped response
sending = true;
}
break;
case bs_sendResCrc:
if (m_config.answer) {
sendSymbol = m_crc;
sending = true;
}
break;
case bs_sendSyn:
sendSymbol = SYN;
sending = true;
break;
}
// send symbol if necessary
result_t result;
struct timespec sentTime, recvTime;
if (sending) {
if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) {
if (m_escape) {
sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01);
} else {
m_escape = sendSymbol;
sendSymbol = ESC;
}
}
result = m_device->send(sendSymbol);
clockGettime(&sentTime);
if (result == RESULT_OK) {
if (m_state == bs_ready) {
timeout = m_config.busAcquireTimeout;
} else {
timeout = SEND_TIMEOUT;
}
} else {
sending = false;
timeout = SYN_TIMEOUT;
setState(bs_skip, result);
}
} else {
clockGettime(&sentTime); // for measuring arbitration delay in enhanced protocol
}
// receive next symbol (optionally check reception of sent symbol)
symbol_t recvSymbol;
ArbitrationState arbitrationState = as_none;
result = m_device->recv(timeout, &recvSymbol, &arbitrationState);
if (sending) {
clockGettime(&recvTime);
}
bool sentAutoSyn = false;
if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
// check if acting as AUTO-SYN generator is required
result = m_device->send(SYN);
if (result != RESULT_OK) {
return setState(bs_skip, result);
}
clockGettime(&sentTime);
recvSymbol = ESC;
result = m_device->recv(SEND_TIMEOUT, &recvSymbol, &arbitrationState);
clockGettime(&recvTime);
if (result != RESULT_OK) {
logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result));
return setState(bs_noSignal, result);
}
if (recvSymbol != SYN) {
logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol);
return setState(bs_noSignal, result);
}
measureLatency(&sentTime, &recvTime);
if (m_generateSynInterval != SYN_TIMEOUT) {
// received own AUTO-SYN symbol back again: act as AUTO-SYN generator now
m_generateSynInterval = SYN_TIMEOUT;
logNotice(lf_bus, "acting as AUTO-SYN generator");
}
m_remainLockCount = 0;
m_lastSynReceiveTime = recvTime;
sentAutoSyn = true;
setState(bs_ready, RESULT_OK);
}
switch (arbitrationState) {
case as_lost:
case as_timeout:
logDebug(lf_bus, arbitrationState == as_lost ? "arbitration lost" : "arbitration lost (timed out)");
if (m_currentRequest == nullptr) {
BusRequest *startRequest = m_nextRequests.peek();
if (startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
}
setState(m_state, RESULT_ERR_BUS_LOST);
break;
case as_won: // implies RESULT_OK
if (m_currentRequest != nullptr) {
logNotice(lf_bus, "arbitration won while handling another request");
setState(bs_ready, RESULT_OK); // force the current request to be notified
} else {
BusRequest *startRequest = m_nextRequests.peek();
if (m_state != bs_ready || startRequest == nullptr || !m_nextRequests.remove(startRequest)) {
logNotice(lf_bus, "arbitration won in invalid state %s", getStateCode(m_state));
setState(bs_ready, RESULT_ERR_TIMEOUT);
} else {
logDebug(lf_bus, "arbitration won");
m_currentRequest = startRequest;
sendSymbol = m_currentRequest->getMaster()[0];
sending = true;
}
}
break;
case as_running:
break;
case as_error:
logError(lf_bus, "arbitration start error");
// cancel request
if (!m_currentRequest) {
BusRequest *startRequest = m_nextRequests.peek();
if (startRequest && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest;
}
}
if (m_currentRequest) {
setState(m_state, RESULT_ERR_BUS_LOST);
}
break;
default: // only as_none
break;
}
if (sentAutoSyn && !sending) {
return RESULT_OK;
}
time_t now;
time(&now);
if (result != RESULT_OK) {
if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1)
// at least one full second has passed since last received symbol
|| m_state == bs_noSignal) {
return setState(bs_noSignal, result);
}
return setState(bs_skip, result);
}
m_lastReceive = now;
if ((recvSymbol == SYN) && (m_state != bs_sendSyn)) {
if (!sending && m_remainLockCount > 0 && m_command.size() != 1) {
m_remainLockCount--;
} else if (!sending && m_remainLockCount == 0 && m_command.size() == 1) {
m_remainLockCount = 1; // wait for next AUTO-SYN after SYN / address / SYN (bus locked for own priority)
}
clockGettime(&m_lastSynReceiveTime);
return setState(bs_ready, m_state == bs_skip ? RESULT_OK : RESULT_ERR_SYN);
}
if (sending && m_state != bs_ready) { // check received symbol for equality if not in arbitration
if (recvSymbol != sendSymbol) {
return setState(bs_skip, RESULT_ERR_SYMBOL);
}
measureLatency(&sentTime, &recvTime);
}
switch (m_state) {
case bs_ready:
case bs_recvCmd:
case bs_recvRes:
case bs_sendCmd:
case bs_sendRes:
SymbolString::updateCrc(recvSymbol, &m_crc);
break;
default:
break;
}
if (m_escape) {
// check escape/unescape state
if (sending) {
if (sendSymbol == ESC) {
return RESULT_OK;
}
sendSymbol = recvSymbol = m_escape;
} else {
if (recvSymbol > 0x01) {
return setState(bs_skip, RESULT_ERR_ESC);
}
recvSymbol = recvSymbol == 0x00 ? ESC : SYN;
}
m_escape = 0;
} else if (!sending && recvSymbol == ESC) {
m_escape = ESC;
return RESULT_OK;
}
switch (m_state) {
case bs_noSignal:
return setState(bs_skip, RESULT_OK);
case bs_skip:
return RESULT_OK;
case bs_ready:
if (m_currentRequest != nullptr && sending) {
// check arbitration
if (recvSymbol == sendSymbol) { // arbitration successful
// measure arbitration delay
int64_t latencyLong = (sentTime.tv_sec*1000000000 + sentTime.tv_nsec
- m_lastSynReceiveTime.tv_sec*1000000000 - m_lastSynReceiveTime.tv_nsec)/1000;
if (latencyLong >= 0 && latencyLong <= 10000) { // skip clock skew or out of reasonable range
auto latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "arbitration delay %d micros", latency);
if (m_arbitrationDelayMin < 0 || (latency < m_arbitrationDelayMin || latency > m_arbitrationDelayMax)) {
if (m_arbitrationDelayMin == -1 || latency < m_arbitrationDelayMin) {
m_arbitrationDelayMin = latency;
}
if (m_arbitrationDelayMax == -1 || latency > m_arbitrationDelayMax) {
m_arbitrationDelayMax = latency;
}
logInfo(lf_bus, "arbitration delay %d - %d micros", m_arbitrationDelayMin, m_arbitrationDelayMax);
}
}
m_nextSendPos = 1;
m_repeat = false;
return setState(bs_sendCmd, RESULT_OK);
}
// arbitration lost. if same priority class found, try again after next AUTO-SYN
m_remainLockCount = isMaster(recvSymbol) ? 2 : 1; // number of SYN to wait for before next send try
if ((recvSymbol & 0x0f) != (sendSymbol & 0x0f) && m_lockCount > m_remainLockCount) {
// if different priority class found, try again after N AUTO-SYN symbols (at least next AUTO-SYN)
m_remainLockCount = m_lockCount;
}
setState(m_state, RESULT_ERR_BUS_LOST); // try again later
}
m_command.push_back(recvSymbol);
m_repeat = false;
return setState(bs_recvCmd, RESULT_OK);
case bs_recvCmd:
m_command.push_back(recvSymbol);
if (m_command.isComplete()) { // all data received
return setState(bs_recvCmdCrc, RESULT_OK);
}
return RESULT_OK;
case bs_recvCmdCrc:
m_crcValid = recvSymbol == m_crc;
if (m_command[1] == BROADCAST) {
if (m_crcValid) {
addSeenAddress(m_command[0]);
messageCompleted();
return setState(bs_skip, RESULT_OK);
}
return setState(bs_skip, RESULT_ERR_CRC);
}
if (m_config.answer) {
symbol_t dstAddress = m_command[1];
if (dstAddress == m_ownMasterAddress || dstAddress == m_ownSlaveAddress) {
if (m_crcValid) {
addSeenAddress(m_command[0]);
m_currentAnswering = true;
return setState(bs_sendCmdAck, RESULT_OK);
}
return setState(bs_sendCmdAck, RESULT_ERR_CRC);
}
}
if (m_crcValid) {
addSeenAddress(m_command[0]);
return setState(bs_recvCmdAck, RESULT_OK);
}
if (m_repeat) {
return setState(bs_skip, RESULT_ERR_CRC);
}
return setState(bs_recvCmdAck, RESULT_ERR_CRC);
case bs_recvCmdAck:
if (recvSymbol == ACK) {
if (!m_crcValid) {
return setState(bs_skip, RESULT_ERR_ACK);
}
if (m_currentRequest != nullptr) {
if (isMaster(m_currentRequest->getMaster()[1])) {
messageCompleted();
return setState(bs_sendSyn, RESULT_OK);
}
} else if (isMaster(m_command[1])) {
messageCompleted();
return setState(bs_skip, RESULT_OK);
}
m_repeat = false;
return setState(bs_recvRes, RESULT_OK);
}
if (recvSymbol == NAK) {
if (!m_repeat) {
m_repeat = true;
m_crc = 0;
m_nextSendPos = 0;
m_command.clear();
if (m_currentRequest != nullptr) {
return setState(bs_sendCmd, RESULT_ERR_NAK, true);
}
return setState(bs_recvCmd, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_ACK);
case bs_recvRes:
m_response.push_back(recvSymbol);
if (m_response.isComplete()) { // all data received
return setState(bs_recvResCrc, RESULT_OK);
}
return RESULT_OK;
case bs_recvResCrc:
m_crcValid = recvSymbol == m_crc;
if (m_crcValid) {
if (m_currentRequest != nullptr) {
return setState(bs_sendResAck, RESULT_OK);
}
return setState(bs_recvResAck, RESULT_OK);
}
if (m_repeat) {
if (m_currentRequest != nullptr) {
return setState(bs_sendSyn, RESULT_ERR_CRC);
}
return setState(bs_skip, RESULT_ERR_CRC);
}
if (m_currentRequest != nullptr) {
return setState(bs_sendResAck, RESULT_ERR_CRC);
}
return setState(bs_recvResAck, RESULT_ERR_CRC);
case bs_recvResAck:
if (recvSymbol == ACK) {
if (!m_crcValid) {
return setState(bs_skip, RESULT_ERR_ACK);
}
messageCompleted();
return setState(bs_skip, RESULT_OK);
}
if (recvSymbol == NAK) {
if (!m_repeat) {
m_repeat = true;
if (m_currentAnswering) {
m_nextSendPos = 0;
return setState(bs_sendRes, RESULT_ERR_NAK, true);
}
m_response.clear();
return setState(bs_recvRes, RESULT_ERR_NAK, true);
}
return setState(bs_skip, RESULT_ERR_NAK);
}
return setState(bs_skip, RESULT_ERR_ACK);
case bs_sendCmd:
if (!sending || m_currentRequest == nullptr) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG);
}
m_nextSendPos++;
if (m_nextSendPos >= m_currentRequest->getMaster().size()) {
return setState(bs_sendCmdCrc, RESULT_OK);
}
return RESULT_OK;
case bs_sendCmdCrc:
if (m_currentRequest->getMaster()[1] == BROADCAST) {
messageCompleted();
return setState(bs_sendSyn, RESULT_OK);
}
m_crcValid = true;
return setState(bs_recvCmdAck, RESULT_OK);
case bs_sendResAck:
if (!sending || m_currentRequest == nullptr) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG);
}
if (!m_crcValid) {
if (!m_repeat) {
m_repeat = true;
m_response.clear();
return setState(bs_recvRes, RESULT_ERR_NAK, true);
}
return setState(bs_sendSyn, RESULT_ERR_ACK);
}
messageCompleted();
return setState(bs_sendSyn, RESULT_OK);
case bs_sendCmdAck:
if (!sending || !m_config.answer) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG);
}
if (!m_crcValid) {
if (!m_repeat) {
m_repeat = true;
m_crc = 0;
m_command.clear();
return setState(bs_recvCmd, RESULT_ERR_NAK, true);
}
return setState(bs_skip, RESULT_ERR_ACK);
}
if (isMaster(m_command[1])) {
messageCompleted(); // TODO decode command and store value into database of internal variables
return setState(bs_skip, RESULT_OK);
}
m_nextSendPos = 0;
m_repeat = false;
// build response and store in m_response for sending back to requesting master
m_response.clear();
result = m_listener->notifyProtocolAnswer(m_command, &m_response);
if (result != RESULT_OK) {
return setState(bs_skip, result);
}
return setState(bs_sendRes, RESULT_OK);
case bs_sendRes:
if (!sending || !m_config.answer) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG);
}
m_nextSendPos++;
if (m_nextSendPos >= m_response.size()) {
// slave data completely sent
return setState(bs_sendResCrc, RESULT_OK);
}
return RESULT_OK;
case bs_sendResCrc:
if (!sending || !m_config.answer) {
return setState(bs_skip, RESULT_ERR_INVALID_ARG);
}
return setState(bs_recvResAck, RESULT_OK);
case bs_sendSyn:
if (!sending) {
return setState(bs_ready, RESULT_ERR_INVALID_ARG);
}
return setState(bs_ready, RESULT_OK);
}
return RESULT_OK;
}
result_t DirectProtocolHandler::setState(BusState state, result_t result, bool firstRepetition) {
if (m_currentRequest != nullptr) {
if (result == RESULT_ERR_BUS_LOST && m_currentRequest->getBusLostRetries() < m_config.busLostRetries) {
logDebug(lf_bus, "%s during %s, retry", getResultCode(result), getStateCode(m_state));
m_currentRequest->incrementBusLostRetries();
m_nextRequests.push(m_currentRequest); // repeat
m_currentRequest = nullptr;
} else if (state == bs_sendSyn || (result != RESULT_OK && !firstRepetition)) {
logDebug(lf_bus, "notify request: %s", getResultCode(result));
bool restart = m_currentRequest->notify(
result == RESULT_ERR_SYN && (m_state == bs_recvCmdAck || m_state == bs_recvRes)
? RESULT_ERR_TIMEOUT : result, m_response);
if (restart) {
m_currentRequest->resetBusLostRetries();
m_nextRequests.push(m_currentRequest);
} else if (m_currentRequest->deleteOnFinish()) {
delete m_currentRequest;
} else {
m_finishedRequests.push(m_currentRequest);
}
m_currentRequest = nullptr;
}
if (state == bs_skip) {
m_device->startArbitration(SYN); // reset arbitration state
}
}
if (state == bs_noSignal) { // notify all requests
if (m_state != bs_noSignal) {
m_listener->notifyProtocolStatus(false);
}
m_response.clear(); // notify with empty response
while ((m_currentRequest = m_nextRequests.pop()) != nullptr) {
bool restart = m_currentRequest->notify(RESULT_ERR_NO_SIGNAL, m_response);
if (restart) { // should not occur with no signal
m_currentRequest->resetBusLostRetries();
m_nextRequests.push(m_currentRequest);
} else if (m_currentRequest->deleteOnFinish()) {
delete m_currentRequest;
} else {
m_finishedRequests.push(m_currentRequest);
}
}
} else if (m_state == bs_noSignal) {
m_listener->notifyProtocolStatus(true);
}
m_escape = 0;
if (state == m_state) {
return result;
}
if ((result < RESULT_OK && !(result == RESULT_ERR_TIMEOUT && state == bs_skip && m_state == bs_ready))
|| (result != RESULT_OK && state == bs_skip && m_state != bs_ready)) {
logDebug(lf_bus, "%s during %s, switching to %s", getResultCode(result), getStateCode(m_state),
getStateCode(state));
} else if (m_currentRequest != nullptr || state == bs_sendCmd || state == bs_sendCmdCrc || state == bs_sendCmdAck
|| state == bs_sendRes || state == bs_sendResCrc || state == bs_sendResAck || state == bs_sendSyn
|| m_state == bs_sendSyn) {
logDebug(lf_bus, "switching from %s to %s", getStateCode(m_state), getStateCode(state));
}
if (state == bs_noSignal) {
if (m_generateSynInterval == 0 || m_state != bs_skip) {
logError(lf_bus, "signal lost");
}
} else if (m_state == bs_noSignal) {
if (m_generateSynInterval == 0 || state != bs_skip) {
logNotice(lf_bus, "signal acquired");
}
}
m_state = state;
if (state == bs_ready || state == bs_skip) {
m_command.clear();
m_crc = 0;
m_crcValid = false;
m_response.clear();
m_nextSendPos = 0;
m_currentAnswering = false;
} else if (state == bs_recvRes || state == bs_sendRes) {
m_crc = 0;
}
return result;
}
bool DirectProtocolHandler::addSeenAddress(symbol_t address) {
if (!ProtocolHandler::addSeenAddress(address)) {
return false;
}
if (m_config.lockCount == 0 && m_masterCount > m_lockCount) {
m_lockCount = m_masterCount;
}
return true;
}
void DirectProtocolHandler::messageCompleted() {
const char* prefix = m_currentRequest ? "sent" : "received";
// do an explicit copy here in case being called by another thread
const MasterSymbolString command(m_currentRequest ? m_currentRequest->getMaster() : m_command);
const SlaveSymbolString response(m_response);
symbol_t srcAddress = command[0], dstAddress = command[1];
if (srcAddress == dstAddress) {
logError(lf_bus, "invalid self-addressed message from %2.2x", srcAddress);
return;
}
if (!m_currentAnswering) {
addSeenAddress(dstAddress);
}
bool master = isMaster(dstAddress);
if (dstAddress == BROADCAST || master) {
logInfo(lf_update, "%s %s cmd: %s", prefix, master ? "MM" : "BC", command.getStr().c_str());
} else {
logInfo(lf_update, "%s MS cmd: %s / %s", prefix, command.getStr().c_str(), response.getStr().c_str());
}
m_listener->notifyProtocolMessage(m_currentRequest != nullptr, command, response);
}
} // namespace ebusd
+180
View File
@@ -0,0 +1,180 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2014-2023 John Baier <ebusd@ebusd.eu>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_EBUS_PROTOCOL_DIRECT_H_
#define LIB_EBUS_PROTOCOL_DIRECT_H_
#include "lib/ebus/protocol.h"
namespace ebusd {
/** @file lib/ebus/protocol_direct.h
* Implementation of directly handled eBUS protocol.
*
* The following table shows the possible states, symbols, and state transition
* depending on the kind of message to send/receive:
* @image html states.png "ebusd direct ProtocolHandler states"
*/
/** the possible bus states. */
enum BusState {
bs_noSignal, //!< no signal on the bus
bs_skip, //!< skip all symbols until next @a SYN
bs_ready, //!< ready for next master (after @a SYN symbol, send/receive QQ)
bs_recvCmd, //!< receive command (ZZ, PBSB, master data) [passive set]
bs_recvCmdCrc, //!< receive command CRC [passive set]
bs_recvCmdAck, //!< receive command ACK/NACK [passive set + active set+get]
bs_recvRes, //!< receive response (slave data) [passive set + active get]
bs_recvResCrc, //!< receive response CRC [passive set + active get]
bs_recvResAck, //!< receive response ACK/NACK [passive set]
bs_sendCmd, //!< send command (ZZ, PBSB, master data) [active set+get]
bs_sendCmdCrc, //!< send command CRC [active set+get]
bs_sendResAck, //!< send response ACK/NACK [active get]
bs_sendCmdAck, //!< send command ACK/NACK [passive get]
bs_sendRes, //!< send response (slave data) [passive get]
bs_sendResCrc, //!< send response CRC [passive get]
bs_sendSyn, //!< send SYN for completed transfer [active set+get]
};
/**
* Directly handles input from and output to eBUS with respect to the eBUS protocol.
*/
class DirectProtocolHandler : public ProtocolHandler {
public:
/**
* Construct a new instance.
* @param config the configuration to use.
* @param device the @a Device instance for accessing the bus.
* @param listener the @a ProtocolListener.
*/
DirectProtocolHandler(const ebus_protocol_config_t config,
Device* device, ProtocolListener* listener)
: ProtocolHandler(config, device, listener),
m_lockCount(config.lockCount <= 3 ? 3 : config.lockCount),
m_remainLockCount(config.lockCount == 0 ? 1 : 0),
m_generateSynInterval(config.generateSyn ? SYN_TIMEOUT*getMasterNumber(config.ownAddress)+SYMBOL_DURATION : 0),
m_currentRequest(nullptr), m_currentAnswering(false), m_nextSendPos(0),
m_state(bs_noSignal), m_escape(0), m_crc(0), m_crcValid(false), m_repeat(false) {
m_lastSynReceiveTime.tv_sec = 0;
m_lastSynReceiveTime.tv_nsec = 0;
}
/**
* Destructor.
*/
virtual ~DirectProtocolHandler() {
if (m_currentRequest != nullptr) {
delete m_currentRequest;
m_currentRequest = nullptr;
}
}
// @copydoc
void injectMessage(const MasterSymbolString& master, const SlaveSymbolString& slave) override {
if (isRunning()) {
return;
}
m_command = master;
m_response = slave;
m_addressConflict = true; // avoid conflict messages
messageCompleted();
m_addressConflict = false;
}
/**
* Main thread entry.
*/
virtual void run();
// @copydoc
bool hasSignal() const override { return m_state != bs_noSignal; }
private:
/**
* Handle the next symbol on the bus.
* @return RESULT_OK on success, or an error code.
*/
result_t handleSymbol();
/**
* Set a new @a BusState and add a log message if necessary.
* @param state the new @a BusState.
* @param result the result code.
* @param firstRepetition true if the first repetition of a message part is being started.
* @return the result code.
*/
result_t setState(BusState state, result_t result, bool firstRepetition = false);
// @copydoc
bool addSeenAddress(symbol_t address) override;
/**
* Called when a message sending or reception was successfully completed.
*/
void messageCompleted();
/** the number of AUTO-SYN symbols before sending is allowed after lost arbitration. */
unsigned int m_lockCount;
/** the remaining number of AUTO-SYN symbols before sending is allowed again. */
unsigned int m_remainLockCount;
/** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
unsigned int m_generateSynInterval;
/** the time of the last received SYN symbol, or 0 for never. */
struct timespec m_lastSynReceiveTime;
/** the currently handled BusRequest, or nullptr. */
BusRequest* m_currentRequest;
/** whether currently answering a request from another participant. */
bool m_currentAnswering;
/** the offset of the next symbol that needs to be sent from the command or response,
* (only relevant if m_request is set and state is @a bs_command or @a bs_response). */
size_t m_nextSendPos;
/** the current @a BusState. */
BusState m_state;
/** 0 when not escaping/unescaping, or @a ESC when receiving, or the original value when sending. */
symbol_t m_escape;
/** the calculated CRC. */
symbol_t m_crc;
/** whether the CRC matched. */
bool m_crcValid;
/** whether the current message part is being repeated. */
bool m_repeat;
/** the received command @a MasterSymbolString. */
MasterSymbolString m_command;
/** the received response @a SlaveSymbolString or response to send. */
SlaveSymbolString m_response;
};
} // namespace ebusd
#endif // LIB_EBUS_PROTOCOL_DIRECT_H_

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB