abstract bus protocol handling
This commit is contained in:
+47
-859
File diff suppressed because it is too large
Load Diff
+29
-380
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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 }"
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
Executable
+477
@@ -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_
|
||||||
@@ -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
|
||||||
Executable
+180
@@ -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 |
Reference in New Issue
Block a user