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

This commit is contained in:
john30
2020-11-01 18:11:19 +01:00
21 changed files with 909 additions and 299 deletions
Regular → Executable
-1
View File
@@ -30,7 +30,6 @@ app.info
/src/lib/utils/libutils.a
/src/lib/ebus/libebus.a
/src/lib/ebus/contrib/test/test_tem
/src/lib/ebus/test/test_device
/src/lib/ebus/test/test_symbol
/src/lib/ebus/test/test_data
/src/lib/ebus/test/test_message
+3
View File
@@ -39,6 +39,9 @@
* added support for single quotes to all commands
* 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)
Regular → Executable
+1 -1
View File
@@ -14,7 +14,7 @@ Features
The main features of the daemon are:
* use USB serial, TCP connected, or UDP device
* 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)
* actively send messages to and receive answers from the eBUS
* passively listen to messages sent on the eBUS
* 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:
> docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80000
> docker run --rm -it -p 8888 john30/ebusd -f --scanconfig -d udp:192.168.178.123:10000 --latency=80
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
@@ -0,0 +1,156 @@
## 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|
+122 -66
View File
@@ -67,7 +67,8 @@ result_t PollRequest::prepare(symbol_t ownMasterAddress) {
istringstream input;
result_t result = m_message->prepareMaster(m_index, ownMasterAddress, SYN, UI_FIELD_SEPARATOR, &input, &m_master);
if (result == RESULT_OK) {
logInfo(lf_bus, "poll cmd: %s", m_master.getStr().c_str());
string str = m_master.getStr();
logInfo(lf_bus, "poll cmd: %s", str.c_str());
}
return result;
}
@@ -99,7 +100,8 @@ result_t ScanRequest::prepare(symbol_t ownMasterAddress) {
istringstream input;
m_result = m_message->prepareMaster(m_index, ownMasterAddress, dstAddress, UI_FIELD_SEPARATOR, &input, &m_master);
if (m_result >= RESULT_OK) {
logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, m_master.getStr().c_str());
string str = m_master.getStr();
logInfo(lf_bus, "scan %2.2x cmd: %s", dstAddress, str.c_str());
}
return m_result;
}
@@ -181,7 +183,8 @@ bool ScanRequest::notify(result_t result, const SlaveSymbolString& slave) {
bool ActiveBusRequest::notify(result_t result, const SlaveSymbolString& slave) {
if (result == RESULT_OK) {
logDebug(lf_bus, "read res: %s", slave.getStr().c_str());
string str = m_master.getStr();
logDebug(lf_bus, "read res: %s", str.c_str());
}
m_result = result;
*m_slave = slave;
@@ -280,7 +283,7 @@ bool GrabbedMessage::dump(bool unknown, MessageMap* messages, bool first, bool d
if (remain == 0) {
return true;
}
for (const auto it : *types) {
for (const auto& it : *types) {
const DataType* baseType = it.second;
if ((baseType->getBitCount() % 8) != 0 || baseType->isIgnored()) { // skip bit and ignored types
continue;
@@ -426,23 +429,17 @@ result_t BusHandler::handleSymbol() {
unsigned int timeout = SYN_TIMEOUT;
symbol_t sendSymbol = ESC;
bool sending = false;
BusRequest* startRequest = nullptr;
// 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+m_transferLatency : SIGNAL_TIMEOUT;
timeout = m_generateSynInterval > 0 ? m_generateSynInterval : SIGNAL_TIMEOUT;
break;
case bs_skip:
timeout = SYN_TIMEOUT;
break;
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 (!m_device->isArbitrating() && m_currentRequest == nullptr && m_remainLockCount == 0) {
BusRequest* startRequest = m_nextRequests.peek();
if (startRequest == nullptr && m_pollInterval > 0) { // check for poll/scan
time_t now;
time(&now);
@@ -450,7 +447,7 @@ result_t BusHandler::handleSymbol() {
Message* message = m_messages->getNextPoll();
if (message != nullptr) {
m_lastPoll = now;
PollRequest* request = new PollRequest(message);
auto request = new PollRequest(message);
result_t ret = request->prepare(m_ownMasterAddress);
if (ret != RESULT_OK) {
logError(lf_bus, "prepare poll message: %s", getResultCode(ret));
@@ -463,19 +460,33 @@ result_t BusHandler::handleSymbol() {
}
}
if (startRequest != nullptr) { // initiate arbitration
sendSymbol = startRequest->m_master[0];
sending = true;
logDebug(lf_bus, "start request %2.2x", startRequest->m_master[0]);
result_t ret = m_device->startArbitration(startRequest->m_master[0]);
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;
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_recvCmdCrc:
timeout = m_slaveRecvTimeout;
break;
case bs_recvCmdAck:
timeout = m_slaveRecvTimeout+(m_currentRequest ? m_transferLatency:0);
timeout = m_slaveRecvTimeout;
break;
case bs_recvRes:
@@ -488,7 +499,7 @@ result_t BusHandler::handleSymbol() {
break;
case bs_recvResAck:
timeout = m_slaveRecvTimeout+m_transferLatency;
timeout = m_slaveRecvTimeout;
break;
case bs_sendCmd:
@@ -541,11 +552,11 @@ result_t BusHandler::handleSymbol() {
// send symbol if necessary
result_t result;
struct timespec sentTime, recvTime;
struct timespec sentTime = {}, recvTime = {};
if (sending) {
if (m_state != bs_sendSyn && (sendSymbol == ESC || sendSymbol == SYN)) {
if (m_escape) {
sendSymbol = sendSymbol == ESC ? 0x00 : 0x01;
sendSymbol = (symbol_t)(sendSymbol == ESC ? 0x00 : 0x01);
} else {
m_escape = sendSymbol;
sendSymbol = ESC;
@@ -555,63 +566,109 @@ result_t BusHandler::handleSymbol() {
clockGettime(&sentTime);
if (result == RESULT_OK) {
if (m_state == bs_ready) {
timeout = m_transferLatency+m_busAcquireTimeout;
timeout = m_busAcquireTimeout;
} else {
timeout = m_transferLatency+SEND_TIMEOUT;
timeout = SEND_TIMEOUT;
}
} else {
sending = false;
timeout = SYN_TIMEOUT;
if (startRequest != nullptr && m_nextRequests.remove(startRequest)) {
m_currentRequest = startRequest; // force the failed request to be notified
}
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;
bool isAutoSyn = !sending && m_generateSynInterval == SYN_TIMEOUT && (m_state == bs_noSignal || m_state == bs_skip);
result = m_device->recv(timeout+(isAutoSyn ? 0 : m_transferLatency), &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)) {
&& 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) {
clockGettime(&sentTime);
recvSymbol = ESC;
result = m_device->recv(SEND_TIMEOUT+m_transferLatency, &recvSymbol);
clockGettime(&recvTime);
if (result == RESULT_ERR_TIMEOUT) {
return setState(bs_noSignal, result);
}
if (result != RESULT_OK) {
logError(lf_bus, "unable to receive sent AUTO-SYN symbol: %s", getResultCode(result));
} else if (recvSymbol != SYN) {
logError(lf_bus, "received %2.2x instead of AUTO-SYN symbol", recvSymbol);
} else {
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;
return setState(bs_ready, result);
}
if (result != RESULT_OK) {
return setState(bs_skip, result);
}
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:
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);
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->m_master[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 (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)
// at least one full second has passed since last received symbol
|| m_state == bs_noSignal) {
@@ -677,19 +734,14 @@ result_t BusHandler::handleSymbol() {
return RESULT_OK;
case bs_ready:
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;
if (m_currentRequest != nullptr && sending) {
// check arbitration
if (recvSymbol == sendSymbol) { // arbitration successful
// measure arbitration delay
long long 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
int latency = static_cast<int>(latencyLong);
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) {
@@ -962,6 +1014,9 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
}
m_currentRequest = nullptr;
}
if (state == bs_skip) {
m_device->startArbitration(SYN); // reset arbitration state
}
}
if (state == bs_noSignal) { // notify all requests
@@ -996,6 +1051,7 @@ result_t BusHandler::setState(BusState state, result_t result, bool firstRepetit
} else if (m_state == bs_noSignal) {
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;
if (state == bs_ready || state == bs_skip) {
@@ -1017,7 +1073,7 @@ void BusHandler::measureLatency(struct timespec* sentTime, struct timespec* recv
if (latencyLong < 0 || latencyLong > 1000) {
return; // clock skew or out of reasonable range
}
int latency = static_cast<int>(latencyLong);
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;
@@ -1299,7 +1355,7 @@ bool BusHandler::formatScanResult(symbol_t slave, bool leadingNewline, ostringst
*output << endl;
}
*output << hex << setw(2) << setfill('0') << static_cast<unsigned>(slave);
for (const auto result : it->second) {
for (const auto &result : it->second) {
*output << result;
}
return true;
@@ -1428,7 +1484,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
const auto it = m_scanResults.find(address);
if (it != m_scanResults.end()) {
*output << ",\"s\":\"";
for (const auto result : it->second) {
for (const auto& result : it->second) {
*output << result;
}
*output << "\"";
@@ -1444,7 +1500,7 @@ void BusHandler::formatUpdateInfo(ostringstream* output) const {
if (!loadedFiles.empty()) {
*output << ",\"f\":[";
bool first = true;
for (const auto loadedFile : loadedFiles) {
for (const auto& loadedFile : loadedFiles) {
if (first) {
first = false;
} else {
+19 -20
View File
@@ -45,20 +45,23 @@ namespace ebusd {
using std::string;
/** the default time [us] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15000
/** the default time [ms] for retrieving a symbol from an addressed slave. */
#define SLAVE_RECV_TIMEOUT 15
/** the maximum allowed time [us] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 50800
/** the maximum allowed time [ms] for retrieving the AUTO-SYN symbol (45ms + 2*1,2% + 1 Symbol). */
#define SYN_TIMEOUT 51
/** the time [us] for determining bus signal availability (AUTO-SYN timeout * 5). */
#define SIGNAL_TIMEOUT 250000
/** 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 4700
#define SYMBOL_DURATION_MICROS 4700
/** the maximum allowed time [us] for retrieving back a sent symbol (2x symbol duration). */
#define SEND_TIMEOUT (2*SYMBOL_DURATION)
/** 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 {
@@ -368,9 +371,8 @@ class BusHandler : public WaitThread {
* @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 failedSendRetries the number of times a failed send is repeated (other than lost arbitration).
* @param transferLatency the bus transfer latency in microseconds.
* @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 busAcquireTimeout the maximum time in milliseconds for bus acquisition.
* @param slaveRecvTimeout the maximum time in milliseconds 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 generateSyn whether to enable AUTO-SYN symbol generation.
* @param pollInterval the interval in seconds in which poll messages are cycled, or 0 if disabled.
@@ -378,14 +380,14 @@ class BusHandler : public WaitThread {
BusHandler(Device* device, MessageMap* messages,
symbol_t ownAddress, bool answer,
unsigned int busLostRetries, unsigned int failedSendRetries,
unsigned int transferLatency, unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout,
unsigned int busAcquireTimeout, unsigned int slaveRecvTimeout,
unsigned int lockCount, bool generateSyn,
unsigned int pollInterval)
: WaitThread(), m_device(device), m_reconnect(false), m_messages(messages),
m_ownMasterAddress(ownAddress), m_ownSlaveAddress(getSlaveAddress(ownAddress)),
m_answer(answer), m_addressConflict(false),
m_busLostRetries(busLostRetries), m_failedSendRetries(failedSendRetries),
m_transferLatency(transferLatency), m_busAcquireTimeout(busAcquireTimeout), m_slaveRecvTimeout(slaveRecvTimeout),
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),
@@ -686,13 +688,10 @@ class BusHandler : public WaitThread {
/** the number of times a failed send is repeated (other than lost arbitration). */
const unsigned int m_failedSendRetries;
/** the bus transfer latency in microseconds. */
const unsigned int m_transferLatency;
/** the maximum time in microseconds for bus acquisition. */
/** the maximum time in milliseconds for bus acquisition. */
const unsigned int m_busAcquireTimeout;
/** the maximum time in microseconds an addressed slave is expected to acknowledge. */
/** the maximum time in milliseconds an addressed slave is expected to acknowledge. */
const unsigned int m_slaveRecvTimeout;
/** the number of masters already seen. */
@@ -707,7 +706,7 @@ class BusHandler : public WaitThread {
/** the remaining number of AUTO-SYN symbols before sending is allowed again. */
unsigned int m_remainLockCount;
/** the interval in microseconds after which to generate an AUTO-SYN symbol, or 0 if disabled. */
/** 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. */
+19 -15
View File
@@ -79,7 +79,7 @@ static struct options opt = {
false, // noDeviceCheck
false, // readOnly
false, // initialSend
-1, // latency
0, // extraLatency
CONFIG_PATH, // configPath
false, // scanConfig
@@ -92,7 +92,7 @@ static struct options opt = {
0x31, // address
false, // answer
9400, // acquireTimeout
10, // acquireTimeout
3, // acquireRetries
2, // sendRetries
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 },
{"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 },
{"latency", O_DEVLAT, "USEC", 0, "Transfer latency in us [0 for USB, 10000 for IP]", 0 },
{"latency", O_DEVLAT, "MSEC", 0, "Extra transfer latency in ms [0]", 0 },
{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
@@ -204,10 +204,10 @@ static const struct argp_option argpoptions[] = {
{nullptr, 0, nullptr, 0, "eBUS options:", 3 },
{"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 },
{"acquiretimeout", O_ACQTIM, "USEC", 0, "Stop bus acquisition after USEC us [9400]", 0 },
{"acquiretimeout", O_ACQTIM, "MSEC", 0, "Stop bus acquisition after MSEC ms [10]", 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 },
{"receivetimeout", O_RCVTIM, "USEC", 0, "Expect a slave to answer within USEC us [25000]", 0 },
{"receivetimeout", O_RCVTIM, "MSEC", 0, "Expect a slave to answer within MSEC us [25]", 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 },
@@ -267,6 +267,7 @@ static map<string, DataFieldTemplates*> s_templatesByPath;
error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input;
result_t result = RESULT_OK;
unsigned int value;
switch (key) {
// Device options:
@@ -295,12 +296,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
opt->initialSend = true;
break;
case O_DEVLAT: // --latency=10000
opt->latency = parseInt(arg, 10, 0, 200000, &result);
if (result != RESULT_OK) {
case O_DEVLAT: // --latency=10
value = parseInt(arg, 10, 0, 200000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>200)) { // backwards compatible (micros)
argp_error(state, "invalid latency");
return EINVAL;
}
opt->extraLatency = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
// Message configuration options:
@@ -376,12 +378,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
opt->answer = true;
break;
case O_ACQTIM: // --acquiretimeout=9400
opt->acquireTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK) {
case O_ACQTIM: // --acquiretimeout=10
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros)
argp_error(state, "invalid acquiretimeout");
return EINVAL;
}
opt->acquireTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
case O_ACQRET: // --acquireretries=3
opt->acquireRetries = parseInt(arg, 10, 0, 10, &result);
@@ -397,12 +400,13 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
return EINVAL;
}
break;
case O_RCVTIM: // --receivetimeout=25000
opt->receiveTimeout = parseInt(arg, 10, 1000, 100000, &result);
if (result != RESULT_OK) {
case O_RCVTIM: // --receivetimeout=25
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value<=1000 && value>100)) { // backwards compatible (micros)
argp_error(state, "invalid receivetimeout");
return EINVAL;
}
opt->receiveTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
break;
case O_MASCNT: // --numbermasters=0
opt->masterCount = parseInt(arg, 10, 0, 25, &result);
@@ -1306,7 +1310,7 @@ int main(int argc, char* argv[]) {
}
// open the device
Device *device = Device::create(opt.device, !opt.noDeviceCheck, opt.readOnly, opt.initialSend);
Device *device = Device::create(opt.device, opt.extraLatency, !opt.noDeviceCheck, opt.readOnly, opt.initialSend);
if (device == nullptr) {
logError(lf_main, "unable to create device %s", opt.device);
return EINVAL;
+3 -3
View File
@@ -39,7 +39,7 @@ struct options {
bool noDeviceCheck; //!< skip serial eBUS device test
bool readOnly; //!< read-only access to the device
bool initialSend; //!< send an initial escape symbol after connecting device
int latency; //!< transfer latency in us [0 for USB, 10000 for IP]
unsigned int extraLatency; //!< extra transfer latency in ms [0 for USB, 10 for IP]
const char* configPath; //!< path to CSV configuration files [http://ebusd.eu/config/]
bool scanConfig; //!< pick configuration files matching initial scan
@@ -54,10 +54,10 @@ struct options {
symbol_t address; //!< own bus address [31]
bool answer; //!< answer to requests from other masters
unsigned int acquireTimeout; //!< bus acquisition timeout in us [9400]
unsigned int acquireTimeout; //!< bus acquisition timeout in ms [10]
unsigned int acquireRetries; //!< number of retries for bus acquisition [3]
unsigned int sendRetries; //!< number of retries for failed sends [2]
unsigned int receiveTimeout; //!< timeout for receiving answer from slave in us [25000]
unsigned int receiveTimeout; //!< timeout for receiving answer from slave in ms [25]
unsigned int masterCount; //!< expected number of masters for arbitration [0]
bool generateSyn; //!< enable AUTO-SYN symbol generation
+17 -9
View File
@@ -141,16 +141,10 @@ MainLoop::MainLoop(const struct options& opt, Device *device, MessageMap* messag
}
}
// 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_address, opt.answer,
opt.acquireRetries, opt.sendRetries,
latency, opt.acquireTimeout, opt.receiveTimeout,
opt.acquireTimeout, opt.receiveTimeout,
opt.masterCount, opt.generateSyn,
opt.pollInterval);
m_busHandler->start("bushandler");
@@ -473,12 +467,18 @@ void MainLoop::notifyDeviceData(symbol_t symbol, bool received) {
}
if (m_logRawBuffer.tellp() == 0 || received != m_logRawLastReceived) {
m_logRawLastReceived = received;
if (m_logRawBuffer.tellp() == 0 && m_logRawLastSymbol != SYN) {
m_logRawBuffer << "...";
}
m_logRawBuffer << (received ? "<" : ">");
}
m_logRawBuffer << setw(2) << setfill('0') << hex << static_cast<unsigned>(symbol);
m_logRawLastSymbol = symbol;
}
if (symbol == SYN && m_logRawBuffer.tellp() > 0) { // flush
m_logRawLastSymbol = symbol;
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 char* str = bufStr.c_str();
if (m_logRawFile) {
@@ -490,6 +490,14 @@ 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,
string* user, bool* reload, ostringstream* ostream) {
string token, previous;
+3
View File
@@ -132,6 +132,9 @@ class MainLoop : public Thread, DeviceListener {
// @copydoc
void notifyDeviceData(symbol_t symbol, bool received) override;
// @copydoc
void notifyStatus(bool error, const char* message) override;
protected:
// @copydoc
+414 -102
View File
@@ -40,6 +40,8 @@
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <ios>
#include <iomanip>
#include "lib/ebus/data.h"
namespace ebusd {
@@ -50,16 +52,64 @@ namespace ebusd {
#define POLLRDHUP 0
#endif
Device::~Device() {
close();
// 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::create(const char* name, bool checkDevice, bool readOnly, bool initialSend) {
Device::~Device() {
close();
if (m_buffer) {
free(m_buffer);
}
}
Device* Device::create(const char* name, unsigned int extraLatency, bool checkDevice, bool readOnly, bool initialSend) {
bool enhanced = strncmp(name, "enh:", 4) == 0;
if (enhanced) {
name += 4;
}
if (strchr(name, '/') == nullptr && strchr(name, ':') != nullptr) {
char* in = strdup(name);
bool udp = false;
char* addrpos = in;
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)))) {
addrpos += 4;
portpos = strchr(addrpos, ':');
@@ -69,7 +119,7 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i
return nullptr; // invalid protocol or missing port
}
result_t result = RESULT_OK;
unsigned int port = parseInt(portpos+1, 10, 1, 65535, &result);
uint16_t port = (uint16_t)parseInt(portpos+1, 10, 1, 65535, &result);
if (result != RESULT_OK) {
free(in);
return nullptr; // invalid port
@@ -77,9 +127,31 @@ Device* Device::create(const char* name, bool checkDevice, bool readOnly, bool i
*portpos = 0;
char* hostOrIp = strdup(addrpos);
free(in);
return new NetworkDevice(name, hostOrIp, port, readOnly, initialSend, udp);
return new NetworkDevice(name, hostOrIp, port, extraLatency, readOnly, initialSend, udp, enhanced);
}
return new SerialDevice(name, checkDevice, readOnly, initialSend);
// support enh:/dev/<device>
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() {
@@ -87,6 +159,7 @@ void Device::close() {
::close(m_fd);
m_fd = -1;
}
m_bufLen = 0; // flush read buffer
}
bool Device::isValid() {
@@ -103,7 +176,7 @@ result_t Device::send(symbol_t value) {
if (!isValid()) {
return RESULT_ERR_DEVICE;
}
if (m_readOnly || write(value) != 1) {
if (m_readOnly || !write(value)) {
return RESULT_ERR_SEND;
}
if (m_listener != nullptr) {
@@ -112,74 +185,360 @@ result_t Device::send(symbol_t value) {
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()) {
return RESULT_ERR_DEVICE;
}
if (!available() && timeout > 0) {
int ret;
struct timespec tdiff;
bool repeat = false;
bool repeated = false;
timeout += m_latency;
do {
repeat = false;
bool isAvailable = available();
if (!isAvailable && timeout > 0) {
int ret;
struct timespec tdiff;
// set select timeout
tdiff.tv_sec = timeout/1000000;
tdiff.tv_nsec = (timeout%1000000)*1000;
// set select timeout
tdiff.tv_sec = timeout/1000;
tdiff.tv_nsec = (timeout%1000)*1000000;
#ifdef HAVE_PPOLL
nfds_t nfds = 1;
struct pollfd fds[nfds];
nfds_t nfds = 1;
struct pollfd fds[nfds];
memset(fds, 0, sizeof(fds));
memset(fds, 0, sizeof(fds));
fds[0].fd = m_fd;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
ret = ppoll(fds, nfds, &tdiff, nullptr);
if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) {
ret = -1;
}
fds[0].fd = m_fd;
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLRDHUP;
ret = ppoll(fds, nfds, &tdiff, nullptr);
if (ret >= 0 && fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) {
ret = -1;
}
#else
#ifdef HAVE_PSELECT
fd_set readfds, exceptfds;
fd_set readfds, exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds);
FD_ZERO(&readfds);
FD_ZERO(&exceptfds);
FD_SET(m_fd, &readfds);
ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr);
if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) {
ret = -1;
}
ret = pselect(m_fd + 1, &readfds, nullptr, &exceptfds, &tdiff, nullptr);
if (ret >= 1 && FD_ISSET(m_fd, &exceptfds)) {
ret = -1;
}
#else
ret = 1; // ignore timeout if neither ppoll nor pselect are available
ret = 1; // ignore timeout if neither ppoll nor pselect are available
#endif
#endif
if (ret == -1) {
close();
return RESULT_ERR_DEVICE;
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;
}
}
if (ret == 0) {
// directly read byte from device
bool incomplete = false;
if (!read(value, isAvailable, arbitrationState, &incomplete)) {
if (!isAvailable && incomplete && !repeated) {
// for a two-byte transfer another poll is needed
repeat = true;
repeated = true;
timeout = m_latency+ENHANCED_COMPLETE_WAIT_DURATION;
continue;
}
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;
}
// 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;
}
// non-enhanced: arbitration executed by ebusd itself
bool wrote = write(m_arbitrationMaster); // send as fast as possible
if (m_listener != nullptr) {
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;
}
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() {
if (m_fd != -1) {
close();
result_t result = Device::open();
if (result != RESULT_OK) {
return result;
}
struct termios newSettings;
@@ -223,7 +582,7 @@ result_t SerialDevice::open() {
// create new settings
memset(&newSettings, 0, sizeof(newSettings));
cfsetspeed(&newSettings, B2400);
cfsetspeed(&newSettings, m_enhancedProto ? B9600 : B2400);
newSettings.c_cflag |= (CS8 | CLOCAL | CREAD);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors
@@ -237,7 +596,7 @@ result_t SerialDevice::open() {
tcflush(m_fd, TCIFLUSH);
// activate new settings of serial device
if (tcsetattr(m_fd, TCSAFLUSH, &newSettings)) {
if (tcsetattr(m_fd, TCSANOW, &newSettings)) {
close();
return RESULT_ERR_DEVICE;
}
@@ -245,10 +604,7 @@ result_t SerialDevice::open() {
// set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
if (m_initialSend && write(ESC) != 1) {
return RESULT_ERR_SEND;
}
return RESULT_OK;
return afterOpen();
}
void SerialDevice::close() {
@@ -282,8 +638,9 @@ void SerialDevice::checkDevice() {
#endif
result_t NetworkDevice::open() {
if (m_fd != -1) {
close();
result_t result = Device::open();
if (result != RESULT_OK) {
return result;
}
struct sockaddr_in address;
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
@@ -341,23 +698,7 @@ result_t NetworkDevice::open() {
close();
return RESULT_ERR_GENERIC_IO;
}
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();
return afterOpen();
}
void NetworkDevice::checkDevice() {
@@ -367,33 +708,4 @@ 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
+117 -59
View File
@@ -40,6 +40,26 @@ namespace ebusd {
* 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.
*/
@@ -56,6 +76,13 @@ class DeviceListener {
* @param received @a true on reception, @a false on sending.
*/
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
};
@@ -63,18 +90,20 @@ class DeviceListener {
* The base class for accessing an eBUS.
*/
class Device {
public:
protected:
/**
* Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param checkDevice whether to regularly check the device availability.
* @param latency the bus transfer latency in milliseconds.
* @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 enhancedProto whether to use the ebusd enhanced protocol.
*/
Device(const char* name, bool checkDevice, bool readOnly, bool initialSend)
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
m_listener(nullptr) {}
Device(const char* name, bool checkDevice, unsigned int latency, bool readOnly, bool initialSend,
bool enhancedProto=false);
public:
/**
* Destructor.
*/
@@ -83,26 +112,33 @@ class Device {
/**
* Factory method for creating a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param extraLatency the extra bus transfer latency in milliseconds.
* @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 initialSend whether to send an initial @a ESC symbol in @a open().
* @return the new @a Device, or nullptr on error.
* Note: the caller needs to free the created instance.
*/
static Device* create(const char* name, bool checkDevice = true, bool readOnly = false,
bool initialSend = false);
static Device* create(const char* name, unsigned int extraLatency = 0, bool checkDevice = true,
bool readOnly = false, bool initialSend = false);
/**
* Get the transfer latency of this device.
* @return the transfer latency in microseconds.
* @return the transfer latency in milliseconds.
*/
virtual unsigned int getLatency() const { return 0; }
virtual unsigned int getLatency() const { return m_latency; }
/**
* Open the file descriptor.
* @return the @a result_t code.
*/
virtual result_t open() = 0; // abstract
virtual result_t open();
/**
* 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.
@@ -118,11 +154,27 @@ class Device {
/**
* Read a single byte from the device.
* @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
* @param timeout maximum time to wait for the byte in milliseconds, or 0 for infinite.
* @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.
*/
result_t recv(unsigned int timeout, symbol_t* value);
result_t recv(unsigned int timeout, symbol_t* value, ArbitrationState* arbitrationState);
/**
* 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.
@@ -155,38 +207,48 @@ class Device {
*/
virtual void checkDevice() = 0; // abstract
/**
* Check whether a byte is available immediately (without waiting).
* @return true when a a byte is available immediately.
*/
virtual bool available() { return false; }
/**
* Write a single byte.
* @param value the byte value to write.
* @return the number of bytes written, or -1 on error.
* @param startArbitration true to start arbitration.
* @return true on success, false on error.
*/
virtual ssize_t write(symbol_t value) { return ::write(m_fd, &value, 1); }
virtual bool write(symbol_t value, bool startArbitration=false);
/**
* Check whether a symbol is available for reading immediately (without waiting).
* @return true when a symbol is available for reading immediately.
*/
virtual bool available();
/**
* Read a single byte.
* @param value the reference in which the read byte value is stored.
* @return the number of bytes read, or -1 on error.
* @param isAvailable the result of the immediately preceding call to @a available().
* @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 ssize_t read(symbol_t* value) { return ::read(m_fd, value, 1); }
virtual bool read(symbol_t* value, bool isAvailable, ArbitrationState* arbitrationState=nullptr, bool* incomplete=nullptr);
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
const char* m_name;
/** whether to regularly check the device availability (only for serial devices). */
/** whether to regularly check the device availability. */
const bool m_checkDevice;
/** the bus transfer latency in milliseconds. */
const unsigned int m_latency;
/** whether to allow read access to the device only. */
const bool m_readOnly;
/** whether to send an initial @a ESC symbol in @a open(). */
const bool m_initialSend;
/** whether the device supports the ebusd enhanced protocol. */
const bool m_enhancedProto;
/** the opened file descriptor, or -1. */
int m_fd;
@@ -194,8 +256,27 @@ class Device {
private:
/** the @a DeviceListener, or nullptr. */
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).
*/
@@ -204,12 +285,15 @@ class SerialDevice : public Device {
/**
* Construct a new instance.
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
* @param checkDevice whether to regularly check the device availability (only for serial devices).
* @param checkDevice whether to regularly check the device availability.
* @param extraLatency the extra bus transfer latency in milliseconds.
* @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 enhancedProto whether to use the ebusd enhanced protocol.
*/
SerialDevice(const char* name, bool checkDevice, bool readOnly, bool initialSend)
: Device(name, checkDevice, readOnly, initialSend) {}
SerialDevice(const char* name, bool checkDevice, unsigned int extraLatency, bool readOnly, bool initialSend,
bool enhancedProto=false)
: Device(name, checkDevice, extraLatency, readOnly, initialSend, enhancedProto) {}
// @copydoc
result_t open() override;
@@ -239,48 +323,34 @@ class NetworkDevice : public Device {
* @param address the socket address of the device.
* @param hostOrIp the host name or IP address of the device.
* @param port the TCP or UDP port of the device.
* @param extraLatency the extra bus transfer latency in milliseconds.
* @param readOnly whether to allow read access to the device only.
* @param initialSend whether to send an initial @a ESC symbol in @a open().
* @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, bool readOnly, bool initialSend, bool udp)
: Device(name, true, readOnly, initialSend), m_hostOrIp(hostOrIp), m_port(port), m_udp(udp),
m_buffer(nullptr), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
NetworkDevice(const char* name, const char* hostOrIp, uint16_t port, unsigned int extraLatency, bool readOnly,
bool initialSend, bool udp, bool enhancedProto=false)
: Device(name, true, NETWORK_LATENCY_MS+extraLatency, readOnly, initialSend, enhancedProto),
m_hostOrIp(hostOrIp), m_port(port), m_udp(udp) {}
/**
* Destructor.
*/
virtual ~NetworkDevice() {
~NetworkDevice() override {
if (m_hostOrIp) {
free((void*)m_hostOrIp);
}
if (m_buffer) {
free(m_buffer);
}
}
// @copydoc
unsigned int getLatency() const override { return 10000; }
// @copydoc
result_t open() override;
// @copydoc
void close() override;
protected:
// @copydoc
void checkDevice() override;
// @copydoc
bool available() override;
// @copydoc
ssize_t write(symbol_t value) override;
// @copydoc
ssize_t read(symbol_t* value) override;
private:
/** the host name or IP address of the device. */
@@ -291,18 +361,6 @@ class NetworkDevice : public Device {
/** true for UDP, false to TCP. */
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
+1
View File
@@ -44,6 +44,7 @@ const char* getResultCode(result_t resultCode) {
case RESULT_ERR_DUPLICATE: return "ERR: duplicate entry";
case RESULT_ERR_DUPLICATE_NAME: return "ERR: duplicate name";
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_ACK: return "ERR: ACK error";
case RESULT_ERR_NAK: return "ERR: NAK received";
+8 -7
View File
@@ -56,15 +56,16 @@ enum result_t {
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
RESULT_ERR_BUS_LOST = -18, //!< arbitration lost
RESULT_ERR_CRC = -19, //!< CRC error
RESULT_ERR_ACK = -20, //!< ACK error
RESULT_ERR_NAK = -21, //!< NAK received
RESULT_ERR_ARB_RUNNING = -19, //!< arbitration running
RESULT_ERR_CRC = -20, //!< CRC error
RESULT_ERR_ACK = -21, //!< ACK error
RESULT_ERR_NAK = -22, //!< NAK received
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
RESULT_ERR_SYMBOL = -24, //!< wrong symbol received instead of sent symbol
RESULT_ERR_NO_SIGNAL = -23, //!< no signal found on the bus
RESULT_ERR_SYN = -24, //!< SYN received instead of answer
RESULT_ERR_SYMBOL = -25, //!< wrong symbol received instead of sent symbol
RESULT_ERR_NOTAUTHORIZED = -25 //!< not authorized for this action
RESULT_ERR_NOTAUTHORIZED = -26 //!< not authorized for this action
};
+5 -5
View File
@@ -72,19 +72,19 @@ using std::vector;
typedef unsigned char symbol_t;
/** escape symbol, either followed by 0x00 for the value 0xA9, or 0x01 for the value 0xAA. */
#define ESC 0xA9
#define ESC ((symbol_t)0xA9)
/** synchronization symbol. */
#define SYN 0xAA
#define SYN ((symbol_t)0xAA)
/** positive acknowledge symbol. */
#define ACK 0x00
#define ACK ((symbol_t)0x00)
/** negative acknowledge symbol. */
#define NAK 0xFF
#define NAK ((symbol_t)0xFF)
/** the broadcast destination address. */
#define BROADCAST 0xFE
#define BROADCAST ((symbol_t)0xFE)
/**
* Parse an unsigned int value.
Regular → Executable
-4
View File
@@ -11,10 +11,6 @@ add_executable(test_filereader test_filereader.cpp)
target_link_libraries(test_filereader ebus pthread)
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)
target_link_libraries(test_symbol ebus pthread)
add_test(symbol test_symbol)
Regular → Executable
-5
View File
@@ -3,7 +3,6 @@ AM_CXXFLAGS = -I$(top_srcdir)/src \
-Wno-unused-parameter
noinst_PROGRAMS = test_filereader \
test_device \
test_symbol \
test_data \
test_message
@@ -11,9 +10,6 @@ noinst_PROGRAMS = test_filereader \
test_filereader_SOURCES = test_filereader.cpp
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_LDADD = ../libebus.a -lpthread
@@ -24,7 +20,6 @@ test_message_SOURCES = test_message.cpp
test_message_LDADD = ../libebus.a -lpthread
if CONTRIB
test_device_LDADD += ../contrib/libebuscontrib.a
test_data_LDADD += ../contrib/libebuscontrib.a
test_message_LDADD += ../contrib/libebuscontrib.a
endif
+3
View File
@@ -175,6 +175,9 @@ void closeLogFile() {
}
bool needsLog(const LogFacility facility, const LogLevel level) {
if (s_logFile == nullptr && !s_useSyslog) {
return false;
}
return s_facilityLogLevel[facility] >= level;
}
+16
View File
@@ -50,6 +50,22 @@ bool RotateFile::setEnabled(bool enabled) {
if (enabled) {
m_stream = fopen(m_fileName.c_str(), m_textMode ? "w" : "wb");
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;
}
+1 -1
View File
@@ -207,7 +207,7 @@ r,,SoftwareVersion,,,,,"0000",,,HEX:4,,,
EOF
echo "test,testpass,installer" > ./passwd
#ebusd:
./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
./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
sleep 3
pid=`head -n 1 "$PWD/ebusd.pid"`
if [ -z "$pid" ]; then