Revert "Merge branch 'enhanced_device' of github.com:john30/ebusd"

This reverts commit aaccb15a9b, reversing
changes made to edfe09a383.
This commit is contained in:
john30
2020-12-06 17:57:22 +01:00
parent 4569b04266
commit 61feda2f34
21 changed files with 296 additions and 906 deletions
Executable → Regular
+1
View File
@@ -30,6 +30,7 @@ app.info
/src/lib/utils/libutils.a /src/lib/utils/libutils.a
/src/lib/ebus/libebus.a /src/lib/ebus/libebus.a
/src/lib/ebus/contrib/test/test_tem /src/lib/ebus/contrib/test/test_tem
/src/lib/ebus/test/test_device
/src/lib/ebus/test/test_symbol /src/lib/ebus/test/test_symbol
/src/lib/ebus/test/test_data /src/lib/ebus/test/test_data
/src/lib/ebus/test/test_message /src/lib/ebus/test/test_message
-3
View File
@@ -39,9 +39,6 @@
* added support for single quotes to all commands * added support for single quotes to all commands
* added "--mqttlog" and "--mqttversion" options * added "--mqttlog" and "--mqttversion" options
## Breaking Changes
* added support for enhanced network protocol mode for recent [ebusd-esp firmware](https://github.com/john30/ebusd-esp/) that allows the arbitration to be done directly by the Wemos
# 3.2 (2018-05-10) # 3.2 (2018-05-10)
Executable → Regular
+1 -1
View File
@@ -14,7 +14,7 @@ Features
The main features of the daemon are: The main features of the daemon are:
* use USB serial, TCP connected or UDP device, or enhanced ebusd protocol for recent [ebusd-esp firmware](https://github.com/john30/ebusd-esp/) (allows arbitration to be done directly by the Wemos) * use USB serial, TCP connected, or UDP device
* actively send messages to and receive answers from the eBUS * actively send messages to and receive answers from the eBUS
* passively listen to messages sent on the eBUS * passively listen to messages sent on the eBUS
* regularly poll for messages * regularly poll for messages
+1 -1
View File
@@ -41,7 +41,7 @@ Using a network device
---------------------- ----------------------
When using a network device, the "--device" argument to docker can be omitted, but the device information has to be passed on to ebusd: When using a network device, the "--device" argument to docker can be omitted, but the device information has to be passed on to ebusd:
> docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80 > docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80000
Note: the "-f" and "--scanconfig" arguments are only passed to ebusd if it is called without any additional arguments. So when passing further arguments, these two usually need to be added as well. Note: the "-f" and "--scanconfig" arguments are only passed to ebusd if it is called without any additional arguments. So when passing further arguments, these two usually need to be added as well.
-156
View File
@@ -1,156 +0,0 @@
## Transfer speed
In order to compensate potential overhead of transfer encoding, the transfer speed is set to 9600 Baud with 8 bits, no parity, and 1 stop bit.
## Protocol
Data bytes with a value below 0x80 can be transferred as is.
Data bytes with value above or equal to 0x80 are split up into two bytes, each one with the highest bit set to 1.
The second bit indicates whether it is the first or second byte of a split transfer. This way protocol errors can easily be detected.
The bits in the two bytes look like this:
```
first second
76543210 76543210
11ccccdd 10dddddd
```
4 bits in `c` are used for indicating a special purpose and is set to one of the command request/response symbols as stated below.
8 bits in `d` are the data byte to be transferred (might also be unused).
### Command request/response symbols
#### from ebusd to interface
* initialization request
`<INIT> <features>`
Requests an initialization of the interface and requests special features in the data byte (tbd).
* send data request
`<SEND> <data>`
Requests the specified data byte in `d` to be sent to the eBUS.
For data byte values <0x80, the short form without the `<SEND>` prefix is allowed as well.
* arbitration start request
`<START> <master>`
Requests the start of the arbitration process after the next received `<SYN>` symbol with the specified master address in `d`.
#### from interface to ebusd
* initialization response
`<RESETTED> <features>`
Indicates a reboot or an initial ebusd connection on the interface and is expected to be returned after an `<INIT`> request.
The data byte `d` indicates availability of certain features (like full message sending instead of arbitration only, tbd).
* receive data notification
`<RECEIVED> <data>`
Indicates that the specified data byte in `d` was received from the eBUS.
For data byte values <0x80, the short form without the `<RECEIVED>` prefix is allowed as well.
Note that this message shall not be sent when the byte received was part of an arbitration request initiated by ebusd.
* arbitration start succeeded
`<STARTED> <master>`
Indicates the the last arbitration request succeeded (arbitration was won).
The data byte in `d` contains the master address that was sent to eBUS during arbitration.
* arbitration start failed
`<FAILED> <master>`
Indicates that the last arbitration request failed (arbitration was lost or sending failed).
The data byte in `d` contains the master address that has won the arbitration.
* eBUS communication error
`<ERROR_EBUS> <error>`
Indicates an error in the eBUS UART.
The data byte in `d` contains the error message.
* host communication error
`<ERROR_HOST> <error>`
Indicates an error in the host UART.
The data byte in `d` contains the error message.
## Symbols
These are the predefined symbols as used above.
### Bus symbols
* SYN 0xAA
### Command request symbols (from ebusd to interface)
* INIT 0x0
* SEND 0x1
* START 0x2
### Command response symbols (from interface to ebusd)
* RESETTED 0x0
* RECEIVED 0x1
* STARTED 0x2
* FAILED 0xa
### Error codes (from interface to ebusd)
* ERR_FRAMING 0x00: framing error
* ERR_OVERRUN 0x00: buffer overrun error
## Examples
### Passive receive
The master-slave data sequence (without SYN, ACK, and CRC) `1008951200 / 0164` when ebusd is only listening to traffic on the bus would usually be transferred as follows (with all extra symbols seen on the bus):
|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes|
|----:|-----|-----|-----|-----|-----|
|1|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
|2|`QQ`|0x10|interface|`<0x10>`|0x10|
|3|`ZZ`|0x08|interface|`<0x08>`|0x08|
|4|`PB`|0x95|interface|`<RECEIVED> <0x95>`|0xC6 0x95|
|5|`SB`|0x12|interface|`<0x12>`|0x12|
|6|`NN`|0x00|interface|`<0x00>`|0x00|
|7|`CRC`|0xB1|interface|`<RECEIVED> <0xB1>`|0xC6 0xB1|
|8|`ACK`|0x00|interface|`<0x00>`|0x00|
|9|`NN`|0x01|interface|`<0x01>`|0x01|
|10|`DD`|0x64|interface|`<0x64>`|0x64|
|11|`CRC`|0xFF|interface|`<RECEIVED> <0xFF>`|0xC7 0xBF|
|12|`ACK`|0x00|interface|`<0x00>`|0x00|
|13|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
### Active successful send
The same data sequence `1008951200 / 0164` when initiated by ebusd as master (with address 0x10) would usually be transferred as follows (with all extra symbols seen on the bus):
|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes|
|----:|-----|-----|-----|-----|-----|
|1| | |ebusd|`<START> <0x10>`|0xC8 0x90|
|2|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
|3|`QQ`|0x10|interface|`<STARTED> <0x10>`|0xC8 0x90|
|4|`ZZ`|0x08|ebusd|`<0x08>`|0x08|
|5|`ZZ`|0x08|interface|`<0x08>`|0x08|
|6|`PB`|0x95|ebusd|`<SEND> <0x95>`|0xC6 0x95|
|7|`PB`|0x95|interface|`<RECEIVED> <0x95>`|0xC6 0x95|
|8|`SB`|0x12|ebusd|`<0x12>`|0x12|
|9|`SB`|0x12|interface|`<0x12>`|0x12|
|10|`NN`|0x00|ebusd|`<0x00>`|0x00|
|11|`NN`|0x00|interface|`<0x00>`|0x00|
|12|`CRC`|0xB1|ebusd|`<SEND> <0xB1>`|0xC6 0xB1|
|13|`CRC`|0xB1|interface|`<RECEIVED> <0xB1>`|0xC6 0xB1|
|14|`ACK`|0x00|interface|`<0x00>`|0x00|
|15|`NN`|0x01|interface|`<0x01>`|0x01|
|16|`DD`|0x64|interface|`<0x64>`|0x64|
|17|`CRC`|0xFF|interface|`<RECEIVED> <0xFF>`|0xC7 0xBF|
|18|`ACK`|0x00|ebusd|`<0x00>`|0x00|
|19|`ACK`|0x00|interface|`<0x00>`|0x00|
|20|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
### Active successful send as SYN generator
The same data sequence `1008951200 / 0164` when initiated by ebusd as master (with address 0x10) and acting as SYN generator would usually be transferred as follows (with all extra symbols seen on the bus):
|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced bytes|
|----:|-----|-----|-----|-----|-----|
|1| | |ebusd|`<START> <0x10>`|0xC8 0x90|
|2|`SYN`|0xAA|ebusd|`<SEND> <0xAA>`|0xC6 0xAA|
|3|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
|4|`QQ`|0x10|interface|`<STARTED> <0x10>`|0xC8 0x90|
|...|see above| | | | |
The rest of the communcation is the same as before (from 4.)
### Active failed traffic
A failed arbitration when initiated by ebusd as master (with address 0x10) would usually be transferred as follows (with all extra symbols seen on the bus):
|order|eBUS proto|eBUS byte|sender|enhanced proto|enhanced byte|
|----:|-----|-----|-----|-----|-----|
|1| | |ebusd|`<START> <0x10>`|0xC8 0x90|
|2|`SYN`|0xAA|interface|`<RECEIVED> <0xAA>`|0xC6 0xAA|
|3| |0x10|ebusd|`<FAILED> <0x10>`|0xE0 0x90|
|4|`QQ`|0x03|interface|`<0x03>`|0x03|
+63 -119
View File
@@ -67,8 +67,7 @@ result_t PollRequest::prepare(symbol_t ownMasterAddress) {
istringstream input; istringstream input;
result_t result = m_message->prepareMaster(m_index, ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, &m_master); result_t result = m_message->prepareMaster(m_index, ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, &m_master);
if (result == RESULT_OK) { if (result == RESULT_OK) {
string str = m_master.getStr(); logInfo(lf_bus, "poll cmd: %s", m_master.getStr().c_str());
logInfo(lf_bus, "poll cmd: %s", str.c_str());
} }
return result; return result;
} }
@@ -100,8 +99,7 @@ result_t ScanRequest::prepare(symbol_t ownMasterAddress) {
istringstream input; istringstream input;
m_result = m_message->prepareMaster(m_index, ownMasterAddress, dstAddress, UI_FIELD_SEPARATOR, &input, &m_master); m_result = m_message->prepareMaster(m_index, ownMasterAddress, dstAddress, UI_FIELD_SEPARATOR, &input, &m_master);
if (m_result >= RESULT_OK) { if (m_result >= RESULT_OK) {
string str = m_master.getStr(); logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getStr().c_str());
logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, str.c_str());
} }
return m_result; return m_result;
} }
@@ -183,8 +181,7 @@ bool ScanRequest::notify(result_t result, const SlaveSymbolString& slave) {
bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) { bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) {
if (result == RESULT_OK) { if (result == RESULT_OK) {
string str = m_master.getStr(); logDebug(lf_bus, "read res: %s", slave.getStr().c_str());
logDebug(lf_bus, "read res: %s", str.c_str());
} }
m_result = result; m_result = result;
*m_slave = slave; *m_slave = slave;
@@ -283,7 +280,7 @@ bool GrabbedMessage::dump(bool unknown, MessageMap* messages, bool first, bool d
if (remain == 0) { if (remain == 0) {
return true; return true;
} }
for (const auto& it : *types) { for (const auto it : *types) {
const DataType* baseType = it.second; const DataType* baseType = it.second;
if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types
continue; continue;
@@ -429,17 +426,23 @@ result_t BusHandler::handleSymbol() {
unsigned int timeout = SYN_TIMEOUT; unsigned int timeout = SYN_TIMEOUT;
symbol_t sendSymbol = ESC; symbol_t sendSymbol = ESC;
bool sending = false; bool sending = false;
BusRequest* startRequest = nullptr;
// check if another symbol has to be sent and determine timeout for receive // check if another symbol has to be sent and determine timeout for receive
switch (m_state) { switch (m_state) {
case bs_noSignal: case bs_noSignal:
timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT; timeout = m_generateSynInterval > 0 ? m_generateSynInterval+m_transferLatency : SIGNAL_TIMEOUT;
break; break;
case bs_skip: case bs_skip:
timeout = SYN_TIMEOUT; timeout = SYN_TIMEOUT;
if (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) { break;
BusRequest* startRequest = m_nextRequests.peek();
case bs_ready:
if (m_currentRequest != nullptr) {
setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up
} else if (m_remainLockCount == 0) {
startRequest = m_nextRequests.peek();
if (startRequest == nullptr && m_pollInterval > 0) { // check for poll/scan if (startRequest == nullptr && m_pollInterval > 0) { // check for poll/scan
time_t now; time_t now;
time(&now); time(&now);
@@ -447,7 +450,7 @@ result_t BusHandler::handleSymbol() {
Message* message = m_messages->getNextPoll(); Message* message = m_messages->getNextPoll();
if (message != nullptr) { if (message != nullptr) {
m_lastPoll = now; m_lastPoll = now;
auto request = new PollRequest(message); PollRequest* request = new PollRequest(message);
result_t ret = request->prepare(m_ownMasterAddress); result_t ret = request->prepare(m_ownMasterAddress);
if (ret != RESULT_OK) { if (ret != RESULT_OK) {
logError(lf_bus, "prepare poll message: %s", getResultCode(ret)); logError(lf_bus, "prepare poll message: %s", getResultCode(ret));
@@ -460,33 +463,19 @@ result_t BusHandler::handleSymbol() {
} }
} }
if (startRequest != nullptr) { // initiate arbitration if (startRequest != nullptr) { // initiate arbitration
logDebug(lf_bus, "start request %2.2x", startRequest->m_master[0]); sendSymbol = startRequest->m_master[0];
result_t ret = m_device->startArbitration(startRequest->m_master[0]); sending = true;
if (ret == RESULT_OK) {
logDebug(lf_bus, "arbitration start with %2.2x", startRequest->m_master[0]);
} 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; break;
case bs_ready:
if (m_currentRequest != nullptr) {
setState(bs_ready, RESULT_ERR_TIMEOUT); // just to be sure an old BusRequest is cleaned up
}
break;
case bs_recvCmd: case bs_recvCmd:
case bs_recvCmdCrc: case bs_recvCmdCrc:
timeout = m_slaveRecvTimeout; timeout = m_slaveRecvTimeout;
break; break;
case bs_recvCmdAck: case bs_recvCmdAck:
timeout = m_slaveRecvTimeout; timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency:0);
break; break;
case bs_recvRes: case bs_recvRes:
@@ -499,7 +488,7 @@ result_t BusHandler::handleSymbol() {
break; break;
case bs_recvResAck: case bs_recvResAck:
timeout = m_slaveRecvTimeout; timeout = m_slaveRecvTimeout+m_transferLatency;
break; break;
case bs_sendCmd: case bs_sendCmd:
@@ -552,11 +541,11 @@ result_t BusHandler::handleSymbol() {
// send symbol if necessary // send symbol if necessary
result_t result; result_t result;
struct timespec sentTime = {}, recvTime = {}; struct timespec sentTime, recvTime;
if (sending) { if (sending) {
if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) { if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) {
if (m_escape) { if (m_escape) {
sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01); sendSymbol = sendSymbol == ESC ? 0x00 : 0x01;
} else { } else {
m_escape = sendSymbol; m_escape = sendSymbol;
sendSymbol = ESC; sendSymbol = ESC;
@@ -566,109 +555,63 @@ result_t BusHandler::handleSymbol() {
clockGettime(&sentTime); clockGettime(&sentTime);
if (result == RESULT_OK) { if (result == RESULT_OK) {
if (m_state == bs_ready) { if (m_state == bs_ready) {
timeout = m_busAcquireTimeout; timeout = m_transferLatency+m_busAcquireTimeout;
} else { } else {
timeout = SEND_TIMEOUT; timeout = m_transferLatency+SEND_TIMEOUT;
} }
} else { } else {
sending = false; sending = false;
timeout = SYN_TIMEOUT; timeout = SYN_TIMEOUT;
if (startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
setState(bs_skip, result); setState(bs_skip, result);
} }
} else {
clockGettime(&sentTime); // for measuring arbitration delay in enhanced protocol
} }
// receive next symbol (optionally check reception of sent symbol) // receive next symbol (optionally check reception of sent symbol)
symbol_t recvSymbol; symbol_t recvSymbol;
ArbitrationState arbitrationState = as_none; bool isAutoSyn = !sending && m_generateSynInterval == SYN_TIMEOUT && (m_state == bs_noSignal || m_state == bs_skip);
result = m_device->recv(timeout, &recvSymbol, &arbitrationState); result = m_device->recv(timeout+(isAutoSyn ? 0 : m_transferLatency), &recvSymbol);
if (sending) { if (sending) {
clockGettime(&recvTime); clockGettime(&recvTime);
} }
bool sentAutoSyn = false;
if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0 if (!sending && result == RESULT_ERR_TIMEOUT && m_generateSynInterval > 0
&& timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) { && timeout >= m_generateSynInterval && (m_state == bs_noSignal || m_state == bs_skip)) {
// check if acting as AUTO-SYN generator is required // check if acting as AUTO-SYN generator is required
result = m_device->send(SYN); result = m_device->send(SYN);
if (result != RESULT_OK) { if (result == RESULT_OK) {
return setState(bs_skip, result); clockGettime(&sentTime);
} recvSymbol = ESC;
clockGettime(&sentTime); result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol);
recvSymbol = ESC; clockGettime(&recvTime);
result = m_device->recv(SEND_TIMEOUT, &recvSymbol, &arbitrationState); if (result == RESULT_ERR_TIMEOUT) {
clockGettime(&recvTime); return setState(bs_noSignal, result);
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:
logDebug(lf_bus, "arbitration lost");
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); if (result != RESULT_OK) {
break; logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result));
case as_won: // implies RESULT_OK } else if (recvSymbol != SYN) {
if (m_currentRequest != nullptr) { logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol);
logNotice(lf_bus, "arbitration won while handling another request");
setState(bs_ready, RESULT_OK); // force the current request to be notified
} else { } else {
BusRequest *startRequest = m_nextRequests.peek(); measureLatency(&sentTime, &recvTime);
if (m_state != bs_ready || startRequest == nullptr || !m_nextRequests.remove(startRequest)) { if (m_generateSynInterval != SYN_TIMEOUT) {
logNotice(lf_bus, "arbitration won in invalid state %s", getStateCode(m_state)); // received own AUTO-SYN symbol back again: act as AUTO-SYN generator now
setState(bs_ready, RESULT_ERR_TIMEOUT); m_generateSynInterval = SYN_TIMEOUT;
} else { logNotice(lf_bus, "acting as AUTO-SYN generator");
logDebug(lf_bus, "arbitration won");
m_currentRequest = startRequest;
sendSymbol = m_currentRequest->m_master[0];
sending = true;
} }
m_remainLockCount = 0;
m_lastSynReceiveTime = recvTime;
return setState(bs_ready, result);
} }
break; }
case as_running: return setState(bs_skip, result);
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_t now;
time(&now); time(&now);
if (result != RESULT_OK) { if (result != RESULT_OK) {
if (sending && startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1) if ((m_generateSynInterval != SYN_TIMEOUT && difftime(now, m_lastReceive) > 1)
// at least one full second has passed since last received symbol // at least one full second has passed since last received symbol
|| m_state == bs_noSignal) { || m_state == bs_noSignal) {
@@ -734,14 +677,19 @@ result_t BusHandler::handleSymbol() {
return RESULT_OK; return RESULT_OK;
case bs_ready: case bs_ready:
if (m_currentRequest != nullptr && sending) { if (startRequest != nullptr && sending) {
if (!m_nextRequests.remove(startRequest)) {
// request already removed (e.g. due to timeout)
return setState(bs_skip, RESULT_ERR_TIMEOUT);
}
m_currentRequest = startRequest;
// check arbitration // check arbitration
if (recvSymbol == sendSymbol) { // arbitration successful if (recvSymbol == sendSymbol) { // arbitration successful
// measure arbitration delay // measure arbitration delay
long long latencyLong = (sentTime.tv_sec*1000000000 + sentTime.tv_nsec long long latencyLong = (sentTime.tv_sec*1000000000 + sentTime.tv_nsec
- m_lastSynReceiveTime.tv_sec*1000000000 - m_lastSynReceiveTime.tv_nsec)/1000; - m_lastSynReceiveTime.tv_sec*1000000000 - m_lastSynReceiveTime.tv_nsec)/1000;
if (latencyLong >= 0 && latencyLong <= 10000) { // skip clock skew or out of reasonable range if (latencyLong >= 0 && latencyLong <= 10000) { // skip clock skew or out of reasonable range
auto latency = static_cast<int>(latencyLong); int latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "arbitration delay %d micros", latency); logDebug(lf_bus, "arbitration delay %d micros", latency);
if (m_arbitrationDelayMin < 0 || (latency < m_arbitrationDelayMin || latency > m_arbitrationDelayMax)) { if (m_arbitrationDelayMin < 0 || (latency < m_arbitrationDelayMin || latency > m_arbitrationDelayMax)) {
if (m_arbitrationDelayMin == -1 || latency < m_arbitrationDelayMin) { if (m_arbitrationDelayMin == -1 || latency < m_arbitrationDelayMin) {
@@ -1014,9 +962,6 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
} }
m_currentRequest = nullptr; m_currentRequest = nullptr;
} }
if (state == bs_skip) {
m_device->startArbitration(SYN); // reset arbitration state
}
} }
if (state == bs_noSignal) { // notify all requests if (state == bs_noSignal) { // notify all requests
@@ -1051,7 +996,6 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
} else if (m_state == bs_noSignal) { } else if (m_state == bs_noSignal) {
logNotice(lf_bus, "signal acquired"); logNotice(lf_bus, "signal acquired");
} }
// logDebug(lf_bus, "state: from %s to %s with %s", getStateCode(m_state), getStateCode(state), getResultCode(result));
m_state = state; m_state = state;
if (state == bs_ready || state == bs_skip) { if (state == bs_ready || state == bs_skip) {
@@ -1073,7 +1017,7 @@ void BusHandler::measureLatency(struct timespec* sentTime, struct timespec* recv
if (latencyLong < 0 || latencyLong > 1000) { if (latencyLong < 0 || latencyLong > 1000) {
return; // clock skew or out of reasonable range return; // clock skew or out of reasonable range
} }
auto latency = static_cast<int>(latencyLong); int latency = static_cast<int>(latencyLong);
logDebug(lf_bus, "send/receive symbol latency %d ms", latency); logDebug(lf_bus, "send/receive symbol latency %d ms", latency);
if (m_symbolLatencyMin >= 0 && (latency >= m_symbolLatencyMin && latency <= m_symbolLatencyMax)) { if (m_symbolLatencyMin >= 0 && (latency >= m_symbolLatencyMin && latency <= m_symbolLatencyMax)) {
return; return;
@@ -1355,7 +1299,7 @@ bool BusHandler::formatScanResult(symbol_t slave, bool leadingNewline, ostringst
*output << endl; *output << endl;
} }
*output << hex << setw(2) << setfill('0') << static_cast<unsigned>(slave); *output << hex << setw(2) << setfill('0') << static_cast<unsigned>(slave);
for (const auto &result : it->second) { for (const auto result : it->second) {
*output << result; *output << result;
} }
return true; return true;
@@ -1484,7 +1428,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
const auto it = m_scanResults.find(address); const auto it = m_scanResults.find(address);
if (it != m_scanResults.end()) { if (it != m_scanResults.end()) {
*output << ",\"s\":\""; *output << ",\"s\":\"";
for (const auto& result : it->second) { for (const auto result : it->second) {
*output << result; *output << result;
} }
*output << "\""; *output << "\"";
@@ -1500,7 +1444,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
if (!loadedFiles.empty()) { if (!loadedFiles.empty()) {
*output << ",\"f\":["; *output << ",\"f\":[";
bool first = true; bool first = true;
for (const auto& loadedFile : loadedFiles) { for (const auto loadedFile : loadedFiles) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
+20 -19
View File
@@ -45,23 +45,20 @@ namespace ebusd {
using std::string; using std::string;
/** the default time [ms] for retrieving a symbol from an addressed slave. */ /** the default time [us] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15 #define SLAVE_RECV_TIMEOUT 15000
/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */ /** the maximum allowed time [us] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 51 #define SYN_TIMEOUT 50800
/** the time [ms] for determining bus signal availability (AUTO-SYN timeout * 5). */ /** the time [us] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250 #define SIGNAL_TIMEOUT 250000
/** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */ /** the maximum duration [us] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */
#define SYMBOL_DURATION_MICROS 4700 #define SYMBOL_DURATION 4700
/** the maximum duration [ms] of a single symbol (Start+8Bit+Stop+Extra @ 2400Bd-2*1,2%). */ /** the maximum allowed time [us] for retrieving back a sent symbol (2x symbol duration). */
#define SYMBOL_DURATION 5 #define SEND_TIMEOUT (2*SYMBOL_DURATION)
/** 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. */ /** the possible bus states. */
enum BusState { enum BusState {
@@ -371,8 +368,9 @@ class BusHandler : public WaitThread {
* @param answer whether to answer queries for the own master/slave address. * @param answer whether to answer queries for the own master/slave address.
* @param busLostRetries the number of times a send is repeated due to lost arbitration. * @param busLostRetries the number of times a send is repeated due to lost arbitration.
* @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration). * @param failedSendRetries the number of times a failed send is repeated (other than lost arbitration).
* @param busAcquireTimeout the maximum time in milliseconds for bus acquisition. * @param transferLatency the bus transfer latency in microseconds.
* @param slaveRecvTimeout the maximum time in milliseconds an addressed slave is expected to acknowledge. * @param busAcquireTimeout the maximum time in microseconds for bus acquisition.
* @param slaveRecvTimeout the maximum time in microseconds an addressed slave is expected to acknowledge.
* @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection. * @param lockCount the number of AUTO-SYN symbols before sending is allowed after lost arbitration, or 0 for auto detection.
* @param generateSyn whether to enable AUTO-SYN symbol generation. * @param generateSyn whether to enable AUTO-SYN symbol generation.
* @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.
@@ -380,14 +378,14 @@ class BusHandler : public WaitThread {
BusHandler(Device* device, MessageMap* messages, BusHandler(Device* device, MessageMap* messages,
symbol_t ownAddress, bool answer, symbol_t ownAddress, bool answer,
unsigned int busLostRetries, unsigned int failedSendRetries, unsigned int busLostRetries, unsigned int failedSendRetries,
unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout, unsigned int transferLatency, unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout,
unsigned int lockCount, bool generateSyn, unsigned int lockCount, bool generateSyn,
unsigned int pollInterval) unsigned int pollInterval)
: WaitThread(), m_device(device), m_reconnect(false), m_messages(messages), : WaitThread(), m_device(device), m_reconnect(false), m_messages(messages),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)), m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)),
m_answer(answer), m_addressConflict(false), m_answer(answer), m_addressConflict(false),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries), m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout), m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0), m_masterCount(device->isReadOnly()?0:1), m_autoLockCount(lockCount == 0),
m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount ? 1 : 0), m_lockCount(lockCount <= 3 ? 3 : lockCount), m_remainLockCount(m_autoLockCount ? 1 : 0),
m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0), m_generateSynInterval(generateSyn ? SYN_TIMEOUT*getMasterNumber(ownAddress)+SYMBOL_DURATION : 0),
@@ -688,10 +686,13 @@ class BusHandler : public WaitThread {
/** the number of times a failed send is repeated (other than lost arbitration). */ /** the number of times a failed send is repeated (other than lost arbitration). */
const unsigned int m_failedSendRetries; const unsigned int m_failedSendRetries;
/** the maximum time in milliseconds for bus acquisition. */ /** the bus transfer latency in microseconds. */
const unsigned int m_transferLatency;
/** the maximum time in microseconds for bus acquisition. */
const unsigned int m_busAcquireTimeout; const unsigned int m_busAcquireTimeout;
/** the maximum time in milliseconds an addressed slave is expected to acknowledge. */ /** the maximum time in microseconds an addressed slave is expected to acknowledge. */
const unsigned int m_slaveRecvTimeout; const unsigned int m_slaveRecvTimeout;
/** the number of masters already seen. */ /** the number of masters already seen. */
@@ -706,7 +707,7 @@ class BusHandler : public WaitThread {
/** the remaining number of AUTO-SYN symbols before sending is allowed again. */ /** the remaining number of AUTO-SYN symbols before sending is allowed again. */
unsigned int m_remainLockCount; unsigned int m_remainLockCount;
/** the interval in milliseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */ /** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
unsigned int m_generateSynInterval; 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. */
+15 -19
View File
@@ -79,7 +79,7 @@ static struct options opt = {
false, // noDeviceCheck false, // noDeviceCheck
false, // readOnly false, // readOnly
false, // initialSend false, // initialSend
0, // extraLatency -1, // latency
CONFIG_PATH, // configPath CONFIG_PATH, // configPath
false, // scanConfig false, // scanConfig
@@ -92,7 +92,7 @@ static struct options opt = {
0x31, // address 0x31, // address
false, // answer false, // answer
10, // acquireTimeout 9400, // acquireTimeout
3, // acquireRetries 3, // acquireRetries
2, // sendRetries 2, // sendRetries
SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout SLAVE_RECV_TIMEOUT*5/3, // receiveTimeout
@@ -184,7 +184,7 @@ static const struct argp_option argpoptions[] = {
{"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 }, {"nodevicecheck", 'n', nullptr, 0, "Skip serial eBUS device test", 0 },
{"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 }, {"readonly", 'r', nullptr, 0, "Only read from device, never write to it", 0 },
{"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 }, {"initsend", O_INISND, nullptr, 0, "Send an initial escape symbol after connecting device", 0 },
{"latency", O_DEVLAT, "MSEC", 0, "Extra transfer latency in ms [0]", 0 }, {"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 },
{nullptr, 0, nullptr, 0, "Message configuration options:", 2 }, {nullptr, 0, nullptr, 0, "Message configuration options:", 2 },
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTP URL) [" CONFIG_PATH {"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTP URL) [" CONFIG_PATH
@@ -204,10 +204,10 @@ static const struct argp_option argpoptions[] = {
{nullptr, 0, nullptr, 0, "eBUS options:", 3 }, {nullptr, 0, nullptr, 0, "eBUS options:", 3 },
{"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 }, {"address", 'a', "ADDR", 0, "Use ADDR as own bus address [31]", 0 },
{"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 }, {"answer", O_ANSWER, nullptr, 0, "Actively answer to requests from other masters", 0 },
{"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 0 }, {"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 },
{"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 }, {"acquireretries", O_ACQRET, "COUNT", 0, "Retry bus acquisition COUNT times [3]", 0 },
{"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 }, {"sendretries", O_SNDRET, "COUNT", 0, "Repeat failed sends COUNT times [2]", 0 },
{"receivetimeout", O_RCVTIM, "MSEC", 0, "Expect a slave to answer within MSEC us [25]", 0 }, {"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 },
{"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 }, {"numbermasters", O_MASCNT, "COUNT", 0, "Expect COUNT masters on the bus, 0 for auto detection [0]", 0 },
{"generatesyn", O_GENSYN, nullptr, 0, "Enable AUTO-SYN symbol generation", 0 }, {"generatesyn", O_GENSYN, nullptr, 0, "Enable AUTO-SYN symbol generation", 0 },
@@ -267,7 +267,6 @@ static map<string, DataFieldTemplates*> s_templatesByPath;
error_t parse_opt(int key, char *arg, struct argp_state *state) { error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input; struct options *opt = (struct options*)state->input;
result_t result = RESULT_OK; result_t result = RESULT_OK;
unsigned int value;
switch (key) { switch (key) {
// Device options: // Device options:
@@ -296,13 +295,12 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} }
opt->initialSend = true; opt->initialSend = true;
break; break;
case O_DEVLAT: // --latency=10 case O_DEVLAT: // --latency=10000
value = parseInt(arg, 10, 0, 200000, &result); // backwards compatible (micros) opt->latency = parseInt(arg, 10, 0, 200000, &result);
if (result != RESULT_OK || (value<=1000 && value>200)) { // backwards compatible (micros) if (result != RESULT_OK) {
argp_error(state, "invalid latency"); argp_error(state, "invalid latency");
return EINVAL; return EINVAL;
} }
opt->extraLatency = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break; break;
// Message configuration options: // Message configuration options:
@@ -378,13 +376,12 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} }
opt->answer = true; opt->answer = true;
break; break;
case O_ACQTIM: // --acquiretimeout=10 case O_ACQTIM: // --acquiretimeout=9400
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros) opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros) if (result != RESULT_OK) {
argp_error(state, "invalid acquiretimeout"); argp_error(state, "invalid acquiretimeout");
return EINVAL; return EINVAL;
} }
opt->acquireTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break; break;
case O_ACQRET: // --acquireretries=3 case O_ACQRET: // --acquireretries=3
opt->acquireRetries = parseInt(arg, 10, 0, 10, &result); opt->acquireRetries = parseInt(arg, 10, 0, 10, &result);
@@ -400,13 +397,12 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
return EINVAL; return EINVAL;
} }
break; break;
case O_RCVTIM: // --receivetimeout=25 case O_RCVTIM: // --receivetimeout=25000
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros) opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros) if (result != RESULT_OK) {
argp_error(state, "invalid receivetimeout"); argp_error(state, "invalid receivetimeout");
return EINVAL; return EINVAL;
} }
opt->receiveTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break; break;
case O_MASCNT: // --numbermasters=0 case O_MASCNT: // --numbermasters=0
opt->masterCount = parseInt(arg, 10, 0, 25, &result); opt->masterCount = parseInt(arg, 10, 0, 25, &result);
@@ -1310,7 +1306,7 @@ int main(int argc, char* argv[]) {
} }
// open the device // open the device
Device *device = Device::create(opt.device, opt.extraLatency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend); Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend);
if (device == nullptr) { if (device == nullptr) {
logError(lf_main, "unable to create device %s", opt.device); logError(lf_main, "unable to create device %s", opt.device);
return EINVAL; return EINVAL;
+3 -3
View File
@@ -39,7 +39,7 @@ struct options {
bool noDeviceCheck; //!< skip serial eBUS device test bool noDeviceCheck; //!< skip serial eBUS device test
bool readOnly; //!< read-only access to the device bool readOnly; //!< read-only access to the device
bool initialSend; //!< send an initial escape symbol after connecting device bool initialSend; //!< send an initial escape symbol after connecting device
unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP] int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
const char* configPath; //!< path to CSV configuration files [http://ebusd.eu/config/] const char* configPath; //!< path to CSV configuration files [http://ebusd.eu/config/]
bool scanConfig; //!< pick configuration files matching initial scan bool scanConfig; //!< pick configuration files matching initial scan
@@ -54,10 +54,10 @@ struct options {
symbol_t address; //!< own bus address [31] symbol_t address; //!< own bus address [31]
bool answer; //!< answer to requests from other masters bool answer; //!< answer to requests from other masters
unsigned int acquireTimeout; //!< bus acquisition timeout in ms [10] unsigned int acquireTimeout; //!< bus acquisition timeout in us [9400]
unsigned int acquireRetries; //!< number of retries for bus acquisition [3] unsigned int acquireRetries; //!< number of retries for bus acquisition [3]
unsigned int sendRetries; //!< number of retries for failed sends [2] unsigned int sendRetries; //!< number of retries for failed sends [2]
unsigned int receiveTimeout; //!< timeout for receiving answer from slave in ms [25] unsigned int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
unsigned int masterCount; //!< expected number of masters for arbitration [0] unsigned int masterCount; //!< expected number of masters for arbitration [0]
bool generateSyn; //!< enable AUTO-SYN symbol generation bool generateSyn; //!< enable AUTO-SYN symbol generation
+9 -17
View File
@@ -141,10 +141,16 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag
} }
} }
// create BusHandler // create BusHandler
unsigned int latency;
if (opt.latency < 0) {
latency = device->getLatency();
} else {
latency = (unsigned int)opt.latency;
}
m_busHandler = new BusHandler(m_device, m_messages, m_busHandler = new BusHandler(m_device, m_messages,
m_address, opt.answer, m_address, opt.answer,
opt.acquireRetries, opt.sendRetries, opt.acquireRetries, opt.sendRetries,
opt.acquireTimeout, opt.receiveTimeout, latency, opt.acquireTimeout, opt.receiveTimeout,
opt.masterCount, opt.generateSyn, opt.masterCount, opt.generateSyn,
opt.pollInterval); opt.pollInterval);
m_busHandler->start("bushandler"); m_busHandler->start("bushandler");
@@ -467,18 +473,12 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) {
} }
if (m_logRawBuffer.tellp() == 0 || received != m_logRawLastReceived) { if (m_logRawBuffer.tellp() == 0 || received != m_logRawLastReceived) {
m_logRawLastReceived = received; m_logRawLastReceived = received;
if (m_logRawBuffer.tellp() == 0 && m_logRawLastSymbol != SYN) {
m_logRawBuffer << "...";
}
m_logRawBuffer << (received ? "<" : ">"); m_logRawBuffer << (received ? "<" : ">");
} }
m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast<unsigned>(symbol); m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast<unsigned>(symbol);
m_logRawLastSymbol = symbol;
} }
m_logRawLastSymbol = symbol; if (symbol == SYN && m_logRawBuffer.tellp() > 0) { // flush
if (m_logRawBuffer.tellp() > (symbol == SYN ? 0 : 64)) { // flush: direction+5 hdr+24 max data+crc+direction+ack+1
if (symbol != SYN) {
m_logRawBuffer << "...";
}
const string bufStr = m_logRawBuffer.str(); const string bufStr = m_logRawBuffer.str();
const char* str = bufStr.c_str(); const char* str = bufStr.c_str();
if (m_logRawFile) { if (m_logRawFile) {
@@ -490,14 +490,6 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) {
} }
} }
void MainLoop::notifyStatus(bool error, const char* message) {
if (error) {
logError(lf_bus, "device status: %s", message);
} else {
logNotice(lf_bus, "device status: %s", message);
}
}
result_t MainLoop::decodeMessage(const string &data, bool isHttp, bool* connected, ClientSettings* settings, result_t MainLoop::decodeMessage(const string &data, bool isHttp, bool* connected, ClientSettings* settings,
string* user, bool* reload, ostringstream* ostream) { string* user, bool* reload, ostringstream* ostream) {
string token, previous; string token, previous;
-3
View File
@@ -132,9 +132,6 @@ class MainLoop : public Thread, DeviceListener {
// @copydoc // @copydoc
void notifyDeviceData(symbol_t symbol, bool received) override; void notifyDeviceData(symbol_t symbol, bool received) override;
// @copydoc
void notifyStatus(bool error, const char* message) override;
protected: protected:
// @copydoc // @copydoc
+103 -415
View File
@@ -40,8 +40,6 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <ios>
#include <iomanip>
#include "lib/ebus/data.h" #include "lib/ebus/data.h"
namespace ebusd { namespace ebusd {
@@ -52,64 +50,16 @@ namespace ebusd {
#define POLLRDHUP 0 #define POLLRDHUP 0
#endif #endif
// ebusd enhanced protocol IDs:
#define ENH_REQ_INIT ((uint8_t)0x0)
#define ENH_RES_RESETTED ((uint8_t)0x0)
#define ENH_REQ_SEND ((uint8_t)0x1)
#define ENH_RES_RECEIVED ((uint8_t)0x1)
#define ENH_REQ_START ((uint8_t)0x2)
#define ENH_RES_STARTED ((uint8_t)0x2)
#define ENH_RES_FAILED ((uint8_t)0xa)
#define ENH_RES_ERROR_EBUS ((uint8_t)0xb)
#define ENH_RES_ERROR_HOST ((uint8_t)0xc)
// ebusd enhanced error codes for the ERROR_* responses
#define ENH_ERR_FRAMING ((uint8_t)0x00)
#define ENH_ERR_OVERRUN ((uint8_t)0x01)
#define ENH_BYTE_FLAG ((uint8_t)0x80)
#define ENH_BYTE_MASK ((uint8_t)0xc0)
#define ENH_BYTE1 ((uint8_t)0xc0)
#define ENH_BYTE2 ((uint8_t)0x80)
#define makeEnhancedSequence(cmd, data) {(uint8_t)(ENH_BYTE1 | ((cmd)<<2) | (((data)&0xc0)>>6)), (uint8_t)(ENH_BYTE2 | ((data)&0x3f))}
Device::Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend,
bool enhancedProto)
: m_name(name), m_checkDevice(checkDevice),
m_latency(HOST_LATENCY_MS+latency), m_readOnly(readOnly), m_initialSend(initialSend),
m_enhancedProto(enhancedProto), m_fd(-1), m_listener(nullptr), m_arbitrationMaster(SYN),
m_arbitrationCheck(false), m_bufSize(((MAX_LEN+1+3)/4)*4), m_bufLen(0), m_bufPos(0) {
m_buffer = reinterpret_cast<symbol_t*>(malloc(m_bufSize));
if (!m_buffer) {
m_bufSize = 0;
}
}
Device::~Device() { Device::~Device() {
close(); close();
if (m_buffer) {
free(m_buffer);
}
} }
Device* Device::create(const char* name, unsigned int extraLatency, bool checkDevice, bool readOnly, bool initialSend) { Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) {
bool enhanced = strncmp(name, "enh:", 4) == 0;
if (enhanced) {
name += 4;
}
if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) { if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) {
char* in = strdup(name); char* in = strdup(name);
bool udp = false; bool udp = false;
char* addrpos = in; char* addrpos = in;
char* portpos = strchr(addrpos, ':'); char* portpos = strchr(addrpos, ':');
if (!enhanced && portpos >= addrpos+3 && strncmp(addrpos, "enh", 3) == 0) {
enhanced = true; // support enhtcp:<ip>:<port> and enhudp:<ip>:<port>
addrpos += 3;
if (portpos == addrpos) {
addrpos++;
portpos = strchr(addrpos, ':');
}
} // else: support enh:<ip>:<port> defaulting to TCP
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) { if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
addrpos += 4; addrpos += 4;
portpos = strchr(addrpos, ':'); portpos = strchr(addrpos, ':');
@@ -119,7 +69,7 @@ Device* Device::create(const char* name, unsigned int extraLatency, bool checkDe
return nullptr; // invalid protocol or missing port return nullptr; // invalid protocol or missing port
} }
result_t result = RESULT_OK; result_t result = RESULT_OK;
uint16_t port = (uint16_t)parseInt(portpos+1, 10, 1, 65535, &result); unsigned int port = parseInt(portpos+1, 10, 1, 65535, &result);
if (result != RESULT_OK) { if (result != RESULT_OK) {
free(in); free(in);
return nullptr; // invalid port return nullptr; // invalid port
@@ -127,31 +77,9 @@ Device* Device::create(const char* name, unsigned int extraLatency, bool checkDe
*portpos = 0; *portpos = 0;
char* hostOrIp = strdup(addrpos); char* hostOrIp = strdup(addrpos);
free(in); free(in);
return new NetworkDevice(name, hostOrIp, port, extraLatency, readOnly, initialSend, udp, enhanced); return new NetworkDevice(name, hostOrIp, port, readOnly, initialSend, udp);
} }
// support enh:/dev/<device> return new SerialDevice(name, checkDevice, readOnly, initialSend);
return new SerialDevice(name, checkDevice, extraLatency, readOnly, initialSend, enhanced);
}
result_t Device::open() {
close();
return m_bufSize == 0 ? RESULT_ERR_DEVICE : RESULT_OK;
}
result_t Device::afterOpen() {
m_bufLen = 0;
if (m_enhancedProto) {
symbol_t buf[2] = makeEnhancedSequence(ENH_REQ_INIT, 0); // TODO define additional feature flags
if (::write(m_fd, buf, 2) != 2) {
return RESULT_ERR_SEND;
}
if (m_listener != nullptr) {
m_listener->notifyStatus(false, "resetting");
}
} else if (m_initialSend && !write(ESC)) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
} }
void Device::close() { void Device::close() {
@@ -159,7 +87,6 @@ void Device::close() {
::close(m_fd); ::close(m_fd);
m_fd = -1; m_fd = -1;
} }
m_bufLen = 0; // flush read buffer
} }
bool Device::isValid() { bool Device::isValid() {
@@ -176,7 +103,7 @@ result_t Device::send(symbol_t value) {
if (!isValid()) { if (!isValid()) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
if (m_readOnly || !write(value)) { if (m_readOnly || write(value) != 1) {
return RESULT_ERR_SEND; return RESULT_ERR_SEND;
} }
if (m_listener != nullptr) { if (m_listener != nullptr) {
@@ -185,360 +112,74 @@ result_t Device::send(symbol_t value) {
return RESULT_OK; return RESULT_OK;
} }
/** result_t Device::recv(unsigned int timeout, symbol_t* value) {
* the maximum duration in milliseconds to wait for an enhanced sequence to complete after the first part was already
* retrieved: 2* (Start+8Bit+Stop+Extra @ 9600Bd)
*/
#define ENHANCED_COMPLETE_WAIT_DURATION 3
result_t Device::recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState) {
if (m_arbitrationMaster!=SYN) {
*arbitrationState = as_running;
}
if (!isValid()) { if (!isValid()) {
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
bool repeat = false; if (!available() && timeout > 0) {
bool repeated = false; int ret;
timeout += m_latency; struct timespec tdiff;
do {
repeat = false;
bool isAvailable = available();
if (!isAvailable && timeout > 0) {
int ret;
struct timespec tdiff;
// set select timeout // set select timeout
tdiff.tv_sec = timeout/1000; tdiff.tv_sec = timeout/1000000;
tdiff.tv_nsec = (timeout%1000)*1000000; tdiff.tv_nsec = (timeout%1000000)*1000;
#ifdef HAVE_PPOLL #ifdef HAVE_PPOLL
nfds_t nfds = 1; nfds_t nfds = 1;
struct pollfd fds[nfds]; struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds)); memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd; fds[0].fd = m_fd;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP; fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
ret = ppoll(fds, nfds, &tdiff, nullptr); ret = ppoll(fds, nfds, &tdiff, nullptr);
if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) { if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) {
ret = -1; ret = -1;
} }
#else #else
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
fd_set readfds, exceptfds; fd_set readfds, exceptfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_ZERO(&exceptfds); FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds); FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr); ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr);
if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) { if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) {
ret = -1; ret = -1;
}
#else
ret = 1; // ignore timeout if neither ppoll nor pselect are available
#endif
#endif
if (ret == -1) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "poll error %d\n", errno);
#endif
close();
return RESULT_ERR_DEVICE;
}
if (ret == 0) {
return RESULT_ERR_TIMEOUT;
}
} }
#else
// directly read byte from device ret = 1; // ignore timeout if neither ppoll nor pselect are available
bool incomplete = false; #endif
if (!read(value, isAvailable, arbitrationState, &incomplete)) { #endif
if (!isAvailable && incomplete && !repeated) { if (ret == -1) {
// for a two-byte transfer another poll is needed close();
repeat = true; return RESULT_ERR_DEVICE;
repeated = true; }
timeout = m_latency+ENHANCED_COMPLETE_WAIT_DURATION; if (ret == 0) {
continue;
}
return RESULT_ERR_TIMEOUT; return RESULT_ERR_TIMEOUT;
} }
} while (repeat);
if (m_enhancedProto || *value != SYN || m_arbitrationMaster == SYN) {
if (m_listener != nullptr) {
m_listener->notifyDeviceData(*value, true);
}
if (!m_enhancedProto && m_arbitrationMaster != SYN) {
if (m_arbitrationCheck) {
*arbitrationState = *value == m_arbitrationMaster ? as_won : as_lost;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
} else {
*arbitrationState = m_arbitrationMaster == SYN ? as_none : as_start;
}
}
return RESULT_OK;
} }
// non-enhanced: arbitration executed by ebusd itself
bool wrote = write(m_arbitrationMaster); // send as fast as possible // directly read byte from device
ssize_t nbytes = read(value);
if (nbytes == 0) {
return RESULT_ERR_EOF;
}
if (nbytes < 0) {
close();
return RESULT_ERR_DEVICE;
}
if (m_listener != nullptr) { if (m_listener != nullptr) {
m_listener->notifyDeviceData(*value, true); m_listener->notifyDeviceData(*value, true);
} }
if (!wrote) {
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
return RESULT_OK;
}
if (m_listener != nullptr) {
m_listener->notifyDeviceData(m_arbitrationMaster, false);
}
m_arbitrationCheck = true;
*arbitrationState = as_running;
return RESULT_OK; return RESULT_OK;
} }
result_t Device::startArbitration(symbol_t masterAddress) {
if (m_arbitrationCheck) {
return RESULT_ERR_ARB_RUNNING; // should not occur
}
if (m_readOnly) {
return RESULT_ERR_SEND;
}
m_arbitrationMaster = masterAddress;
m_arbitrationCheck = false;
if (m_enhancedProto && masterAddress != SYN) {
if (!write(masterAddress, true)) {
m_arbitrationMaster = SYN;
return RESULT_ERR_SEND;
}
m_arbitrationCheck = true;
}
return RESULT_OK;
}
bool Device::write(symbol_t value, bool startArbitration) {
if (m_enhancedProto) {
symbol_t buf[2] = makeEnhancedSequence(startArbitration ? ENH_REQ_START : ENH_REQ_SEND, value);
return ::write(m_fd, buf, 2) == 2;
}
return ::write(m_fd, &value, 1) == 1;
}
bool Device::available() {
if (m_bufLen <= 0) {
return false;
}
if (!m_enhancedProto) {
return true;
}
// peek into the received enhanced proto bytes to determine symbol availability
for (size_t pos = 0; pos < m_bufLen; pos++) {
symbol_t ch = m_buffer[(pos+m_bufPos)%m_bufSize];
if (!(ch&ENH_BYTE_FLAG)) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail direct\n");
#endif
return true;
}
if ((ch&ENH_BYTE_MASK) == ENH_BYTE1) {
if (pos+1 >= m_bufLen) {
return false;
}
// peek into next byte to check if enhanced sequence is ok
ch = m_buffer[(pos+m_bufPos+1)%m_bufSize];
if (!(ch&ENH_BYTE_FLAG) || (ch&ENH_BYTE_MASK) != ENH_BYTE2) {
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced following bad\n");
#endif
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected available enhanced following byte 1");
}
// drop first byte of invalid sequence
m_bufPos = (m_bufPos + 1) % m_bufSize;
m_bufLen--;
pos--;
continue;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced\n");
#endif
return true;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw avail enhanced bad\n");
#endif
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected available enhanced byte 2");
}
// skip byte from erroneous protocol
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
pos--;
}
return false;
}
bool Device::read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState, bool* incomplete) {
if (!isAvailable) {
if (m_bufLen > 0 && m_bufPos != 0) {
if (m_bufLen > m_bufSize / 2) {
// more than half of input buffer consumed is taken as signal that ebusd is too slow
m_bufLen = 0;
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "buffer overflow");
}
} else {
size_t tail;
if (m_bufPos+m_bufLen > m_bufSize) {
// move wrapped tail away
tail = (m_bufPos+m_bufLen) % m_bufSize;
size_t head = m_bufLen-tail;
memmove(m_buffer+head, m_buffer, tail);
} else {
tail = 0;
}
// move head to first position
memmove(m_buffer, m_buffer + m_bufPos, m_bufLen - tail);
}
}
m_bufPos = 0;
// fill up the buffer
ssize_t size = ::read(m_fd, m_buffer + m_bufLen, m_bufSize - m_bufLen);
if (size <= 0) {
return false;
}
#ifdef DEBUG_RAW_TRAFFIC
fprintf(stdout, "raw <");
for (int pos=0; pos<size; pos++) {
fprintf(stdout, " %2.2x", m_buffer[m_bufLen+pos]);
}
fprintf(stdout, "\n");
#endif
m_bufLen += size;
}
if (!available()) {
if (incomplete) {
*incomplete = m_enhancedProto && m_bufLen > 0;
}
return false;
}
if (!m_enhancedProto) {
*value = m_buffer[m_bufPos];
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return true;
}
while (m_bufLen > 0) {
symbol_t ch = m_buffer[m_bufPos];
if (!(ch&ENH_BYTE_FLAG)) {
*value = ch;
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return true;
}
uint8_t kind = ch&ENH_BYTE_MASK;
if (kind == ENH_BYTE1 && m_bufLen<2) {
return false; // transfer not complete yet
}
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
if (kind == ENH_BYTE2) {
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "unexpected enhanced byte 2");
}
return false;
}
// kind is ENH_BYTE1
symbol_t ch2 = m_buffer[m_bufPos];
m_bufPos = (m_bufPos + 1) % m_bufSize;
m_bufLen--;
if ((ch2 & ENH_BYTE_MASK) != ENH_BYTE2) {
if (m_listener != nullptr) {
m_listener->notifyStatus(true, "missing enhanced byte 2");
}
return false;
}
symbol_t data = (symbol_t)(((ch&0x03)<<6) | (ch2&0x3f));
symbol_t cmd = (ch>>2)&0xf;
switch (cmd) {
case ENH_RES_STARTED:
*arbitrationState = as_won;
if (m_listener != NULL) {
m_listener->notifyDeviceData(data, false);
}
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
*value = data;
return true;
case ENH_RES_FAILED:
*arbitrationState = as_lost;
if (m_listener != NULL) {
m_listener->notifyDeviceData(m_arbitrationMaster, false);
}
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
*value = data;
return true;
case ENH_RES_RECEIVED:
*value = data;
return true;
case ENH_RES_RESETTED:
if (*arbitrationState != as_none) {
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
}
// TODO define additional feature flags
if (m_listener != nullptr) {
m_listener->notifyStatus(false, "reset");
}
break;
case ENH_RES_ERROR_EBUS:
case ENH_RES_ERROR_HOST:
if (m_listener != nullptr) {
ostringstream stream;
stream << (cmd==ENH_RES_ERROR_EBUS ? "eBUS comm error: " : "host comm error: ");
switch (data) {
case ENH_ERR_FRAMING:
stream << "framing";
break;
case ENH_ERR_OVERRUN:
stream << "overrun";
break;
default:
stream << "unknown 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned>(data);
break;
}
string str = stream.str();
m_listener->notifyStatus(true, str.c_str());
}
if (*arbitrationState != as_none) {
*arbitrationState = as_error;
m_arbitrationMaster = SYN;
m_arbitrationCheck = false;
}
break;
default:
if (m_listener != nullptr) {
ostringstream stream;
stream << "unexpected enhanced command 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<unsigned>(cmd);
string str = stream.str();
m_listener->notifyStatus(true, str.c_str());
}
return false;
}
}
return false;
}
result_t SerialDevice::open() { result_t SerialDevice::open() {
result_t result = Device::open(); if (m_fd != -1) {
if (result != RESULT_OK) { close();
return result;
} }
struct termios newSettings; struct termios newSettings;
@@ -582,7 +223,7 @@ result_t SerialDevice::open() {
// create new settings // create new settings
memset(&newSettings, 0, sizeof(newSettings)); memset(&newSettings, 0, sizeof(newSettings));
cfsetspeed(&newSettings, m_enhancedProto ? B9600 : B2400); cfsetspeed(&newSettings, B2400);
newSettings.c_cflag |= (CS8 | CLOCAL | CREAD); newSettings.c_cflag |= (CS8 | CLOCAL | CREAD);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors newSettings.c_iflag |= IGNPAR; // ignore parity errors
@@ -596,7 +237,7 @@ result_t SerialDevice::open() {
tcflush(m_fd, TCIFLUSH); tcflush(m_fd, TCIFLUSH);
// activate new settings of serial device // activate new settings of serial device
if (tcsetattr(m_fd, TCSANOW, &newSettings)) { if (tcsetattr(m_fd, TCSAFLUSH, &newSettings)) {
close(); close();
return RESULT_ERR_DEVICE; return RESULT_ERR_DEVICE;
} }
@@ -604,7 +245,10 @@ result_t SerialDevice::open() {
// set serial device into blocking mode // set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK); fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
return afterOpen(); if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
} }
void SerialDevice::close() { void SerialDevice::close() {
@@ -638,9 +282,8 @@ void SerialDevice::checkDevice() {
#endif #endif
result_t NetworkDevice::open() { result_t NetworkDevice::open() {
result_t result = Device::open(); if (m_fd != -1) {
if (result != RESULT_OK) { close();
return result;
} }
struct sockaddr_in address; struct sockaddr_in address;
memset(reinterpret_cast<char*>(&address), 0, sizeof(address)); memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
@@ -698,7 +341,23 @@ result_t NetworkDevice::open() {
close(); close();
return RESULT_ERR_GENERIC_IO; return RESULT_ERR_GENERIC_IO;
} }
return afterOpen(); if (m_bufSize == 0) {
m_bufSize = MAX_LEN+1;
m_buffer = reinterpret_cast<symbol_t*>(malloc(m_bufSize));
if (!m_buffer) {
m_bufSize = 0;
}
}
m_bufLen = 0;
if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
}
void NetworkDevice::close() {
m_bufLen = 0; // flush read buffer
Device::close();
} }
void NetworkDevice::checkDevice() { void NetworkDevice::checkDevice() {
@@ -708,4 +367,33 @@ void NetworkDevice::checkDevice() {
} }
} }
bool NetworkDevice::available() {
return m_buffer && m_bufLen > 0;
}
ssize_t NetworkDevice::write(symbol_t value) {
m_bufLen = 0; // flush read buffer
return Device::write(value);
}
ssize_t NetworkDevice::read(symbol_t* value) {
if (available()) {
*value = m_buffer[m_bufPos];
m_bufPos = (m_bufPos+1)%m_bufSize;
m_bufLen--;
return 1;
}
if (m_bufSize > 0) {
ssize_t size = ::read(m_fd, m_buffer, m_bufSize);
if (size <= 0) {
return size;
}
*value = m_buffer[0];
m_bufPos = 1;
m_bufLen = size-1;
return size;
}
return Device::read(value);
}
} // namespace ebusd } // namespace ebusd
+58 -116
View File
@@ -40,26 +40,6 @@ namespace ebusd {
* to a file and/or forwarding it to a logging function. * to a file and/or forwarding it to a logging function.
*/ */
/** the transfer latency of the network device [ms]. */
#define NETWORK_LATENCY_MS 10
/** the latency of the host [ms]. */
#ifdef __CYGWIN__
#define HOST_LATENCY_MS 20
#else
#define HOST_LATENCY_MS 0
#endif
/** the arbitration state handled by @a Device. */
enum ArbitrationState {
as_none, //!< no arbitration in process
as_start, //!< arbitration start requested
as_error, //!< error while sending master address
as_running, //!< arbitration currently running (master address sent, waiting for reception)
as_lost, //!< arbitration lost
as_won, //!< arbitration won
};
/** /**
* Interface for listening to data received on/sent to a device. * Interface for listening to data received on/sent to a device.
*/ */
@@ -76,13 +56,6 @@ class DeviceListener {
* @param received @a true on reception, @a false on sending. * @param received @a true on reception, @a false on sending.
*/ */
virtual void notifyDeviceData(symbol_t symbol, bool received) = 0; // abstract virtual void notifyDeviceData(symbol_t symbol, bool received) = 0; // abstract
/**
* Called to notify a status message from the device.
* @param error true for an error message, false for an info message.
* @param message the message string.
*/
virtual void notifyStatus(bool error, const char* message) = 0; // abstract
}; };
@@ -90,20 +63,18 @@ class DeviceListener {
* The base class for accessing an eBUS. * The base class for accessing an eBUS.
*/ */
class Device { class Device {
protected: public:
/** /**
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability. * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param latency the bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/ */
Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend, Device(const char* name, bool checkDevice, bool readOnly, bool initialSend)
bool enhancedProto=false); : m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
m_listener(nullptr) {}
public:
/** /**
* Destructor. * Destructor.
*/ */
@@ -112,33 +83,26 @@ class Device {
/** /**
* Factory method for creating a new instance. * Factory method for creating a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param checkDevice whether to regularly check the device availability (only for serial devices). * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @return the new @a Device, or nullptr on error. * @return the new @a Device, or nullptr on error.
* Note: the caller needs to free the created instance. * Note: the caller needs to free the created instance.
*/ */
static Device* create(const char* name, unsigned int extraLatency = 0, bool checkDevice = true, static Device* create(const char* name, bool checkDevice = true, bool readOnly = false,
bool readOnly = false, bool initialSend = false); bool initialSend = false);
/** /**
* Get the transfer latency of this device. * Get the transfer latency of this device.
* @return the transfer latency in milliseconds. * @return the transfer latency in microseconds.
*/ */
virtual unsigned int getLatency() const { return m_latency; } virtual unsigned int getLatency() const { return 0; }
/** /**
* Open the file descriptor. * Open the file descriptor.
* @return the @a result_t code. * @return the @a result_t code.
*/ */
virtual result_t open(); virtual result_t open() = 0; // abstract
/**
* Has to be called by subclasses upon successful opening the device as last action in open().
* @return the @a result_t code.
*/
result_t afterOpen();
/** /**
* Close the file descriptor if opened. * Close the file descriptor if opened.
@@ -154,27 +118,11 @@ class Device {
/** /**
* Read a single byte from the device. * Read a single byte from the device.
* @param timeout maximum time to wait for the byte in milliseconds, or 0 for infinite. * @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
* @param value the reference in which the received byte value is stored. * @param value the reference in which the received byte value is stored.
* @param arbitrationState the reference in which the current @a ArbitrationState is stored on success. When set to
* @a as_won, the received byte is the master address that was successfully arbitrated with.
* @return the result_t code. * @return the result_t code.
*/ */
result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState); result_t recv(unsigned int timeout, symbol_t* value);
/**
* Start the arbitration with the specified master address. A subsequent request while an arbitration is currently in
* checking state will always result in @a RESULT_ERR_DUPLICATE.
* @param masterAddress the master address, or @a SYN to cancel a previous arbitration request.
* @return the result_t code.
*/
result_t startArbitration(symbol_t masterAddress);
/**
* Return whether the device is currently in arbitration.
* @return true when the device is currently in arbitration.
*/
bool isArbitrating() const { return m_arbitrationMaster != SYN; };
/** /**
* Return the device name. * Return the device name.
@@ -208,47 +156,37 @@ class Device {
virtual void checkDevice() = 0; // abstract virtual void checkDevice() = 0; // abstract
/** /**
* Write a single byte. * Check whether a byte is available immediately (without waiting).
* @param value the byte value to write. * @return true when a a byte is available immediately.
* @param startArbitration true to start arbitration.
* @return true on success, false on error.
*/ */
virtual bool write(symbol_t value, bool startArbitration=false); virtual bool available() { return false; }
/** /**
* Check whether a symbol is available for reading immediately (without waiting). * Write a single byte.
* @return true when a symbol is available for reading immediately. * @param value the byte value to write.
* @return the number of bytes written, or -1 on error.
*/ */
virtual bool available(); virtual ssize_t write(symbol_t value) { return ::write(m_fd, &value, 1); }
/** /**
* Read a single byte. * Read a single byte.
* @param value the reference in which the read byte value is stored. * @param value the reference in which the read byte value is stored.
* @param isAvailable the result of the immediately preceding call to @a available(). * @return the number of bytes read, or -1 on error.
* @param arbitrationState the variable in which to store the received arbitration state (mandatory for enhanced proto).
* @param incomplete the variable in which to store when a partial transfer needs another poll.
* @return true on success, false on error.
*/ */
virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState=nullptr, bool* incomplete=nullptr); virtual ssize_t read(symbol_t* value) { return ::read(m_fd, value, 1); }
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */ /** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
const char* m_name; const char* m_name;
/** whether to regularly check the device availability. */ /** whether to regularly check the device availability (only for serial devices). */
const bool m_checkDevice; const bool m_checkDevice;
/** the bus transfer latency in milliseconds. */
const unsigned int m_latency;
/** whether to allow read access to the device only. */ /** whether to allow read access to the device only. */
const bool m_readOnly; const bool m_readOnly;
/** whether to send an initial @a ESC symbol in @a open(). */ /** whether to send an initial @a ESC symbol in @a open(). */
const bool m_initialSend; const bool m_initialSend;
/** whether the device supports the ebusd enhanced protocol. */
const bool m_enhancedProto;
/** the opened file descriptor, or -1. */ /** the opened file descriptor, or -1. */
int m_fd; int m_fd;
@@ -256,27 +194,8 @@ class Device {
private: private:
/** the @a DeviceListener, or nullptr. */ /** the @a DeviceListener, or nullptr. */
DeviceListener* m_listener; DeviceListener* m_listener;
/** the arbitration master address to send when in arbitration, or @a SYN. */
symbol_t m_arbitrationMaster;
/** true when in arbitration and the next received symbol needs to be checked against the sent master address. */
bool m_arbitrationCheck;
/** the read buffer. */
symbol_t* m_buffer;
/** the read buffer size (multiple of 4). */
size_t m_bufSize;
/** the read buffer fill length. */
size_t m_bufLen;
/** the read buffer read position. */
size_t m_bufPos;
}; };
/** /**
* The @a Device for directly connected serial interfaces (tty). * The @a Device for directly connected serial interfaces (tty).
*/ */
@@ -285,15 +204,12 @@ class SerialDevice : public Device {
/** /**
* Construct a new instance. * Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). * @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability. * @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/ */
SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency, bool readOnly, bool initialSend, SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend)
bool enhancedProto=false) : Device(name, checkDevice, readOnly, initialSend) {}
: Device(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto) {}
// @copydoc // @copydoc
result_t open() override; result_t open() override;
@@ -323,34 +239,48 @@ class NetworkDevice : public Device {
* @param address the socket address of the device. * @param address the socket address of the device.
* @param hostOrIp the host name or IP address of the device. * @param hostOrIp the host name or IP address of the device.
* @param port the TCP or UDP port of the device. * @param port the TCP or UDP port of the device.
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only. * @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open(). * @param initialSend whether to send an initial @a ESC symbol in @a open().
* @param udp true for UDP, false to TCP. * @param udp true for UDP, false to TCP.
* @param enhancedProto whether to use the ebusd enhanced protocol.
*/ */
NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, unsigned int extraLatency, bool readOnly, NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, bool readOnly, bool initialSend, bool udp)
bool initialSend, bool udp, bool enhancedProto=false) : Device(name, true, readOnly, initialSend), m_hostOrIp(hostOrIp), m_port(port), m_udp(udp),
: Device(name, true, NETWORK_LATENCY_MS+extraLatency, readOnly, initialSend, enhancedProto), m_buffer(nullptr), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {}
/** /**
* Destructor. * Destructor.
*/ */
~NetworkDevice() override { virtual ~NetworkDevice() {
if (m_hostOrIp) { if (m_hostOrIp) {
free((void*)m_hostOrIp); free((void*)m_hostOrIp);
} }
if (m_buffer) {
free(m_buffer);
}
} }
// @copydoc
unsigned int getLatency() const override { return 10000; }
// @copydoc // @copydoc
result_t open() override; result_t open() override;
// @copydoc
void close() override;
protected: protected:
// @copydoc // @copydoc
void checkDevice() override; void checkDevice() override;
// @copydoc
bool available() override;
// @copydoc
ssize_t write(symbol_t value) override;
// @copydoc
ssize_t read(symbol_t* value) override;
private: private:
/** the host name or IP address of the device. */ /** the host name or IP address of the device. */
@@ -361,6 +291,18 @@ class NetworkDevice : public Device {
/** true for UDP, false to TCP. */ /** true for UDP, false to TCP. */
const bool m_udp; const bool m_udp;
/** the buffer memory, or nullptr. */
symbol_t* m_buffer;
/** the buffer size. */
size_t m_bufSize;
/** the buffer fill length. */
size_t m_bufLen;
/** the buffer read position. */
size_t m_bufPos;
}; };
} // namespace ebusd } // namespace ebusd
-1
View File
@@ -44,7 +44,6 @@ const char* getResultCode(result_t resultCode) {
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry"; case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name"; case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost"; case RESULT_ERR_BUS_LOST: return "ERR: arbitration lost";
case RESULT_ERR_ARB_RUNNING: return "ERR: arbitration running";
case RESULT_ERR_CRC: return "ERR: CRC error"; case RESULT_ERR_CRC: return "ERR: CRC error";
case RESULT_ERR_ACK: return "ERR: ACK error"; case RESULT_ERR_ACK: return "ERR: ACK error";
case RESULT_ERR_NAK: return "ERR: NAK received"; case RESULT_ERR_NAK: return "ERR: NAK received";
+7 -8
View File
@@ -56,16 +56,15 @@ enum result_t {
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name) RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
RESULT_ERR_ARB_RUNNING = -19, //!< arbitration running RESULT_ERR_CRC = -19, //!< CRC error
RESULT_ERR_CRC = -20, //!< CRC error RESULT_ERR_ACK = -20, //!< ACK error
RESULT_ERR_ACK = -21, //!< ACK error RESULT_ERR_NAK = -21, //!< NAK received
RESULT_ERR_NAK = -22, //!< NAK received
RESULT_ERR_NO_SIGNAL = -23, //!< no signal found on the bus RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
RESULT_ERR_SYN = -24, //!< SYN received instead of answer RESULT_ERR_SYN = -23, //!< SYN received instead of answer
RESULT_ERR_SYMBOL = -25, //!< wrong symbol received instead of sent symbol RESULT_ERR_SYMBOL = -24, //!< wrong symbol received instead of sent symbol
RESULT_ERR_NOTAUTHORIZED = -26 //!< not authorized for this action RESULT_ERR_NOTAUTHORIZED = -25 //!< not authorized for this action
}; };
+5 -5
View File
@@ -72,19 +72,19 @@ using std::vector;
typedef unsigned char symbol_t; typedef unsigned char symbol_t;
/** escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA. */ /** escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA. */
#define ESC ((symbol_t)0xA9) #define ESC 0xA9
/** synchronization symbol. */ /** synchronization symbol. */
#define SYN ((symbol_t)0xAA) #define SYN 0xAA
/** positive acknowledge symbol. */ /** positive acknowledge symbol. */
#define ACK ((symbol_t)0x00) #define ACK 0x00
/** negative acknowledge symbol. */ /** negative acknowledge symbol. */
#define NAK ((symbol_t)0xFF) #define NAK 0xFF
/** the broadcast destination address. */ /** the broadcast destination address. */
#define BROADCAST ((symbol_t)0xFE) #define BROADCAST 0xFE
/** /**
* Parse an unsigned int value. * Parse an unsigned int value.
Executable → Regular
+4
View File
@@ -11,6 +11,10 @@ add_executable(test_filereader test_filereader.cpp)
target_link_libraries(test_filereader ebus pthread) target_link_libraries(test_filereader ebus pthread)
add_test(filereader test_filereader) add_test(filereader test_filereader)
add_executable(test_device test_device.cpp)
target_link_libraries(test_device ebus pthread ${test_LIBS})
add_test(device test_device)
add_executable(test_symbol test_symbol.cpp) add_executable(test_symbol test_symbol.cpp)
target_link_libraries(test_symbol ebus pthread) target_link_libraries(test_symbol ebus pthread)
add_test(symbol test_symbol) add_test(symbol test_symbol)
Executable → Regular
+5
View File
@@ -3,6 +3,7 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
-Wno-unused-parameter -Wno-unused-parameter
noinst_PROGRAMS = test_filereader \ noinst_PROGRAMS = test_filereader \
test_device \
test_symbol \ test_symbol \
test_data \ test_data \
test_message test_message
@@ -10,6 +11,9 @@ noinst_PROGRAMS = test_filereader \
test_filereader_SOURCES = test_filereader.cpp test_filereader_SOURCES = test_filereader.cpp
test_filereader_LDADD = ../libebus.a -lpthread test_filereader_LDADD = ../libebus.a -lpthread
test_device_SOURCES = test_device.cpp
test_device_LDADD = ../libebus.a -lpthread
test_symbol_SOURCES = test_symbol.cpp test_symbol_SOURCES = test_symbol.cpp
test_symbol_LDADD = ../libebus.a -lpthread test_symbol_LDADD = ../libebus.a -lpthread
@@ -20,6 +24,7 @@ test_message_SOURCES = test_message.cpp
test_message_LDADD = ../libebus.a -lpthread test_message_LDADD = ../libebus.a -lpthread
if CONTRIB if CONTRIB
test_device_LDADD += ../contrib/libebuscontrib.a
test_data_LDADD += ../contrib/libebuscontrib.a test_data_LDADD += ../contrib/libebuscontrib.a
test_message_LDADD += ../contrib/libebuscontrib.a test_message_LDADD += ../contrib/libebuscontrib.a
endif endif
-3
View File
@@ -175,9 +175,6 @@ void closeLogFile() {
} }
bool needsLog(const LogFacility facility, const LogLevel level) { bool needsLog(const LogFacility facility, const LogLevel level) {
if (s_logFile == nullptr && !s_useSyslog) {
return false;
}
return s_facilityLogLevel[facility] >= level; return s_facilityLogLevel[facility] >= level;
} }
-16
View File
@@ -50,22 +50,6 @@ bool RotateFile::setEnabled(bool enabled) {
if (enabled) { if (enabled) {
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb"); m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
m_fileSize = 0; m_fileSize = 0;
#ifdef FORWARD_RAW_TTY
if (!m_textMode && isatty(fileno(m_stream)) == 1) {
int fd = fileno(m_stream);
struct termios newSettings;
memset(&newSettings, 0, sizeof(newSettings));
cfsetspeed(&newSettings, B2400);
newSettings.c_cflag |= (CS8 | CLOCAL);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors
newSettings.c_oflag &= ~OPOST;
// activate new settings of serial device
tcsetattr(fd, TCSANOW, &newSettings);
}
#endif
} }
return true; return true;
} }
+1 -1
View File
@@ -207,7 +207,7 @@ r,,SoftwareVersion,,,,,"0000",,,HEX:4,,,
EOF EOF
echo "test,testpass,installer" > ./passwd echo "test,testpass,installer" > ./passwd
#ebusd: #ebusd:
./src/ebusd/ebusd -d tcp:127.0.0.1:8876 --initsend --latency 10 -n -c "$PWD/contrib/etc/ebusd" --pollinterval=10 -s -a 31 --acquireretries 3 --answer --generatesyn --receivetimeout 40000 --sendretries 1 --enablehex --htmlpath "$PWD/contrib/html" --httpport 8878 --pidfile "$PWD/ebusd.pid" --localhost -p 8877 -l "$PWD/ebusd.log" --logareas all --loglevel debug --lograwdata=bytes --lograwdatafile "$PWD/ebusd.raw" --lograwdatasize 1 --dumpfile "$PWD/ebusd.dump" --dumpsize 100 -D --scanconfig --aclfile=./passwd --mqttport=1883 ./src/ebusd/ebusd -d tcp:127.0.0.1:8876 --initsend --latency 10000 -n -c "$PWD/contrib/etc/ebusd" --pollinterval=10 -s -a 31 --acquireretries 3 --answer --generatesyn --receivetimeout 40000 --sendretries 1 --enablehex --htmlpath "$PWD/contrib/html" --httpport 8878 --pidfile "$PWD/ebusd.pid" --localhost -p 8877 -l "$PWD/ebusd.log" --logareas all --loglevel debug --lograwdata=bytes --lograwdatafile "$PWD/ebusd.raw" --lograwdatasize 1 --dumpfile "$PWD/ebusd.dump" --dumpsize 100 -D --scanconfig --aclfile=./passwd --mqttport=1883
sleep 3 sleep 3
pid=`head -n 1 "$PWD/ebusd.pid"` pid=`head -n 1 "$PWD/ebusd.pid"`
if [ -z "$pid" ]; then if [ -z "$pid" ]; then