Merge remote-tracking branch 'origin/master' into enhanced_device

# Conflicts:
#	src/lib/ebus/device.cpp
This commit is contained in:
john
2019-10-06 14:26:49 +02:00
46 changed files with 887 additions and 359 deletions
Executable → Regular
+25 -11
View File
@@ -647,8 +647,11 @@ size_t SingleDataField::getLength(PartType partType, size_t maxLength) const {
return remainder ? maxLength : m_length;
}
bool SingleDataField::hasFullByteOffset(bool after) const {
bool SingleDataField::hasFullByteOffset(bool after, int16_t& previousFirstBit) const {
if (m_length > 1) {
if (after) {
previousFirstBit = -1;
}
return true;
}
int16_t firstBit;
@@ -658,8 +661,15 @@ bool SingleDataField::hasFullByteOffset(bool after) const {
} else {
firstBit = 0;
}
return (m_dataType->getBitCount() % 8) == 0
|| (after && firstBit + (m_dataType->getBitCount() % 8) >= 8);
bool ret = (m_dataType->getBitCount() % 8) == 0
|| (firstBit == previousFirstBit) || (after && firstBit + (m_dataType->getBitCount() % 8) >= 8);
// std::cout<<(after?"after,":"before,")<<"prev="<<static_cast<unsigned>(previousFirstBit)<<",first="
// <<static_cast<unsigned>(firstBit)<<",length="<<static_cast<unsigned>(m_dataType->getBitCount())
// <<" => "<<(ret?"true":"false")<<"\n";
if (after) {
previousFirstBit = ret ? -1 : firstBit;
}
return ret;
}
size_t SingleDataField::getCount(PartType partType, const char* fieldName) const {
@@ -915,9 +925,10 @@ const DataFieldSet* DataFieldSet::clone() const {
size_t DataFieldSet::getLength(PartType partType, size_t maxLength) const {
size_t length = 0;
bool previousFullByteOffset[] = { true, true, true, true };
int16_t previousFirstBit[] = { -1, -1, -1, -1 };
for (const auto field : m_fields) {
if (field->getPartType() == partType) {
if (!previousFullByteOffset[partType] && !field->hasFullByteOffset(false)) {
if (!previousFullByteOffset[partType] && !field->hasFullByteOffset(false, previousFirstBit[partType])) {
length--;
}
size_t fieldLength = field->getLength(partType, maxLength);
@@ -928,7 +939,7 @@ size_t DataFieldSet::getLength(PartType partType, size_t maxLength) const {
}
length = length + fieldLength;
previousFullByteOffset[partType] = field->hasFullByteOffset(true);
previousFullByteOffset[partType] = field->hasFullByteOffset(true, previousFirstBit[partType]);
}
}
@@ -1010,12 +1021,13 @@ void DataFieldSet::dump(bool prependFieldSeparator, bool asJson, ostream* output
result_t DataFieldSet::read(const SymbolString& data, size_t offset,
const char* fieldName, ssize_t fieldIndex, unsigned int* output) const {
bool previousFullByteOffset = true, found = false, findFieldIndex = fieldIndex >= 0;
int16_t previousFirstBit = -1;
PartType partType = data.isMaster() ? pt_masterData : pt_slaveData;
for (const auto field : m_fields) {
if (field->getPartType() != partType) {
continue;
}
if (!previousFullByteOffset && !field->hasFullByteOffset(false)) {
if (!previousFullByteOffset && !field->hasFullByteOffset(false, previousFirstBit)) {
offset--;
}
result_t result = field->read(data, offset, fieldName, fieldIndex, output);
@@ -1023,7 +1035,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
return result;
}
offset += field->getLength(partType, data.getDataSize()-offset);
previousFullByteOffset = field->hasFullByteOffset(true);
previousFullByteOffset = field->hasFullByteOffset(true, previousFirstBit);
if (result != RESULT_EMPTY) {
found = true;
}
@@ -1049,6 +1061,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
bool leadingSeparator, const char* fieldName, ssize_t fieldIndex,
OutputFormat outputFormat, ssize_t outputIndex, ostream* output) const {
bool previousFullByteOffset = true, found = false, findFieldIndex = fieldIndex >= 0;
int16_t previousFirstBit = -1;
if (outputIndex < 0 && (!m_uniqueNames || ((outputFormat & OF_JSON) && !(outputFormat & OF_NAMES)))) {
outputIndex = 0;
}
@@ -1060,7 +1073,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
}
continue;
}
if (!previousFullByteOffset && !field->hasFullByteOffset(false)) {
if (!previousFullByteOffset && !field->hasFullByteOffset(false, previousFirstBit)) {
offset--;
}
result_t result = field->read(data, offset, leadingSeparator, fieldName, fieldIndex,
@@ -1069,7 +1082,7 @@ result_t DataFieldSet::read(const SymbolString& data, size_t offset,
return result;
}
offset += field->getLength(partType, data.getDataSize()-offset);
previousFullByteOffset = field->hasFullByteOffset(true);
previousFullByteOffset = field->hasFullByteOffset(true, previousFirstBit);
if (result != RESULT_EMPTY) {
found = true;
leadingSeparator = true;
@@ -1099,12 +1112,13 @@ result_t DataFieldSet::write(char separator, size_t offset, istringstream* input
string token;
PartType partType = data->isMaster() ? pt_masterData : pt_slaveData;
bool previousFullByteOffset = true;
int16_t previousFirstBit = -1;
size_t baseOffset = offset;
for (const auto field : m_fields) {
if (field->getPartType() != partType) {
continue;
}
if (!previousFullByteOffset && !field->hasFullByteOffset(false)) {
if (!previousFullByteOffset && !field->hasFullByteOffset(false, previousFirstBit)) {
offset--;
}
result_t result;
@@ -1124,7 +1138,7 @@ result_t DataFieldSet::write(char separator, size_t offset, istringstream* input
return result;
}
offset += fieldLength;
previousFullByteOffset = field->hasFullByteOffset(true);
previousFullByteOffset = field->hasFullByteOffset(true, previousFirstBit);
}
if (usedLength != nullptr) {
+4 -1
View File
@@ -401,10 +401,13 @@ class SingleDataField : public DataField {
/**
* Get whether this field uses a full byte offset.
* @param after @p true to check after consuming the bits, @p false to check before.
* @param previousFirstBit the index to the first bit of the previous field, or -1
* if the previous field used a full byte offset. Will be updated during the call
* when after was true.
* @return @p true if this field uses a full byte offset, @p false if this field
* only consumes a part of a byte and a subsequent field may re-use the same offset.
*/
bool hasFullByteOffset(bool after) const;
bool hasFullByteOffset(bool after, int16_t& previousFirstBit) const;
// @copydoc
size_t getCount(PartType partType = pt_any, const char* fieldName = nullptr) const override;
+1 -1
View File
@@ -696,7 +696,7 @@ result_t NumberDataType::readSymbols(size_t offset, size_t length, const SymbolS
}
}
#endif
if (isnan(val)) {
if (val != val) { // !isnan(val)
if (outputFormat & OF_JSON) {
*output << "null";
} else {
+20 -3
View File
@@ -30,6 +30,9 @@
#ifdef HAVE_LINUX_SERIAL
# include <linux/serial.h>
#endif
#ifdef HAVE_FREEBSD_UFTDI
# include <dev/usb/uftdiio.h>
#endif
#include <errno.h>
#ifdef HAVE_PPOLL
# include <poll.h>
@@ -392,7 +395,7 @@ result_t SerialDevice::open() {
struct termios newSettings;
// open file descriptor
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
m_fd = ::open(m_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (m_fd < 0) {
return RESULT_ERR_NOTFOUND;
@@ -415,13 +418,24 @@ result_t SerialDevice::open() {
}
#endif
#ifdef HAVE_FREEBSD_UFTDI
int param = 0;
// flush tx/rx and set low latency on uftdi device
if (ioctl(m_fd, UFTDIIOC_GET_LATENCY, &param) == 0) {
ioctl(m_fd, UFTDIIOC_RESET_IO, &param);
param = 1;
ioctl(m_fd, UFTDIIOC_SET_LATENCY, &param);
}
#endif
// save current settings
tcgetattr(m_fd, &m_oldSettings);
// create new settings
memset(&newSettings, 0, sizeof(newSettings));
newSettings.c_cflag |= ((m_enhancedProto ? B115200 : B2400) | CS8 | CLOCAL | CREAD);
cfsetspeed(&newSettings, m_enhancedProto ? B115200 : B2400);
newSettings.c_cflag |= (CS8 | CLOCAL | CREAD);
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
newSettings.c_iflag |= IGNPAR; // ignore parity errors
newSettings.c_oflag &= ~OPOST;
@@ -434,7 +448,10 @@ result_t SerialDevice::open() {
tcflush(m_fd, TCIFLUSH);
// activate new settings of serial device
tcsetattr(m_fd, TCSAFLUSH, &newSettings);
if (tcsetattr(m_fd, TCSAFLUSH, &newSettings)) {
close();
return RESULT_ERR_DEVICE;
}
// set serial device into blocking mode
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
+1
View File
@@ -22,6 +22,7 @@
#include <unistd.h>
#include <termios.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <iostream>
#include <fstream>
+1 -1
View File
@@ -120,7 +120,7 @@ void FileReader::tolower(string* str) {
static size_t hashFunction(const string& str) {
size_t hash = 0;
for (char c : str) {
for (unsigned char c : str) {
hash = (31 * hash) ^ c;
}
return hash;
Executable → Regular
+82 -55
View File
@@ -45,12 +45,24 @@ using std::endl;
/** the bit mask for the ID length and combined ID bytes in the message key. */
#define ID_LENGTH_AND_IDS_MASK ((7LL << (8 * 7 + 5)) | 0xffffffffLL)
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active read message. */
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active write message. */
#define ID_SOURCE_ACTIVE_WRITE (0x1fLL << (8 * 7))
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active write message. */
/** the bits in the @a ID_SOURCE_MASK for arbitrary source and active read message. */
#define ID_SOURCE_ACTIVE_READ (0x1eLL << (8 * 7))
/**
* the bits in the @a ID_SOURCE_MASK for arbitrary source and active write message
* to a master (same value as ID_SOURCE_ACTIVE_WRITE for now).
*/
#define ID_SOURCE_ACTIVE_WRITE_MASTER (0x1fLL << (8 * 7))
/**
* the bits in the @a ID_SOURCE_MASK for arbitrary source and active read message
* to a master (same value as ID_SOURCE_ACTIVE_WRITE for now).
*/
#define ID_SOURCE_ACTIVE_READ_MASTER (0x1eLL << (8 * 7))
/** special value for invalid message key. */
#define INVALID_KEY 0xffffffffffffffffLL
@@ -79,6 +91,9 @@ static const char* defaultMessageFieldMap[] = { // access level not included in
"*name", "part", "type", "divisor/values", "unit", "comment",
};
/** the m_pollOrder of the last polled message. */
static unsigned int g_lastPollOrder = 0;
extern DataFieldTemplates* getTemplates(const string& filename);
extern result_t loadDefinitionsFromConfigPath(FileReader* reader, const string& filename, bool verbose,
@@ -99,7 +114,7 @@ Message::Message(const string& circuit, const string& level, const string& name,
m_data(data), m_deleteData(deleteData),
m_pollPriority(pollPriority),
m_usedByCondition(false), m_isScanMessage(false), m_condition(condition),
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) {
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollOrder(0), m_lastPollTime(0) {
if (circuit == "scan") {
setScanMessage();
m_pollPriority = 0;
@@ -116,7 +131,7 @@ Message::Message(const string& circuit, const string& level, const string& name,
m_data(data), m_deleteData(deleteData),
m_pollPriority(0),
m_usedByCondition(false), m_isScanMessage(true), m_condition(nullptr),
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollCount(0), m_lastPollTime(0) {
m_lastUpdateTime(0), m_lastChangeTime(0), m_pollOrder(0), m_lastPollTime(0) {
}
@@ -158,7 +173,8 @@ uint64_t Message::createKey(const vector<symbol_t>& id, bool isWrite, bool isPas
if (isPassive) {
key |= (uint64_t)getMasterNumber(srcAddress) << (8 * 7); // 0..25
} else {
key |= (isWrite ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active
key |= isMaster(dstAddress) ? (isWrite ? ID_SOURCE_ACTIVE_WRITE_MASTER : ID_SOURCE_ACTIVE_READ_MASTER)
: (isWrite ? ID_SOURCE_ACTIVE_WRITE : ID_SOURCE_ACTIVE_READ); // special values for active
}
key |= (uint64_t)dstAddress << (8 * 6);
int exp = 5;
@@ -199,7 +215,7 @@ uint64_t Message::createKey(const MasterSymbolString& master, size_t maxIdLength
uint64_t Message::createKey(symbol_t pb, symbol_t sb, bool broadcast) {
uint64_t key = 0;
key |= (broadcast ? 0x1fLL : 0x1eLL) << (8 * 7); // special values for active
key |= broadcast ? ID_SOURCE_ACTIVE_WRITE : ID_SOURCE_ACTIVE_READ; // special values for active
key |= (uint64_t)(broadcast ? BROADCAST : SYN) << (8 * 6);
key |= (uint64_t)pb << (8 * 5);
key |= (uint64_t)sb << (8 * 4);
@@ -596,9 +612,29 @@ bool Message::setPollPriority(size_t priority) {
}
bool ret = m_pollPriority == 0 && usePriority > 0;
m_pollPriority = usePriority;
if (ret || m_pollOrder > g_lastPollOrder+(unsigned int)m_pollPriority) {
// ensure a later increased or newly set priority does not prefer that message before all others
m_pollOrder = g_lastPollOrder+(unsigned int)m_pollPriority;
}
return ret;
}
bool Message::isLessPollWeight(const Message* other) const {
if (m_pollOrder > other->m_pollOrder) {
return true;
}
if (m_pollOrder < other->m_pollOrder) {
return false;
}
if (m_pollPriority > other->m_pollPriority) {
return true;
}
if (m_pollPriority < other->m_pollPriority) {
return false;
}
return m_lastPollTime > other->m_lastPollTime;
}
void Message::setUsedByCondition() {
if (m_usedByCondition) {
return;
@@ -786,26 +822,6 @@ result_t Message::decodeLastDataNumField(const char* fieldName, ssize_t fieldInd
return result;
}
bool Message::isLessPollWeight(const Message* other) const {
size_t tprio = m_pollPriority;
size_t oprio = other->m_pollPriority;
size_t tw = tprio * m_pollCount;
size_t ow = oprio * other->m_pollCount;
if (tw > ow) {
return true;
}
if (tw < ow) {
return false;
}
if (tprio > oprio) {
return true;
}
if (tprio < oprio) {
return false;
}
return m_lastPollTime > other->m_lastPollTime;
}
void Message::dumpHeader(const vector<string>* fieldNames, ostream* output) {
bool first = true;
if (fieldNames == nullptr) {
@@ -1267,6 +1283,7 @@ void ChainedMessage::dumpField(const string& fieldName, bool withConditions, ost
* @param sameIdExtAs the optional @a MasterSymbolString to check for having the same ID.
* @param onlyAvailable true to include only available messages (default true), false to also include messages that
* are currently not available (e.g. due to unresolved or false conditions).
* @return the first available @a Message from the list.
*/
Message* getFirstAvailable(const vector<Message*>& messages, const MasterSymbolString* sameIdExtAs,
const bool onlyAvailable = true) {
@@ -1287,6 +1304,7 @@ Message* getFirstAvailable(const vector<Message*>& messages, const MasterSymbolS
* @param sameIdExtAs the optional @a Message to check for having the same ID.
* @param onlyAvailable true to include only available messages (default true), false to also include messages that
* are currently not available (e.g. due to unresolved or false conditions).
* @return the first available @a Message from the list.
*/
Message* getFirstAvailable(const vector<Message*>& messages, const Message* sameIdExtAs = nullptr,
const bool onlyAvailable = true) {
@@ -2566,6 +2584,14 @@ void MessageMap::findAll(const string& circuit, const string& name, const string
}
}
Message* MessageMap::getFirstAvailableFromIterator(const map<uint64_t, vector<Message*> >::const_iterator& it,
const MasterSymbolString* sameIdExtAs, bool onlyAvailable) const {
if (it != m_messagesByKey.end()) {
return getFirstAvailable(it->second, sameIdExtAs, onlyAvailable);
}
return nullptr;
}
Message* MessageMap::find(const MasterSymbolString& master, bool anyDestination,
bool withRead, bool withWrite, bool withPassive, bool onlyAvailable) const {
if (anyDestination && master.size() >= 5 && master[4] == 0 && master[2] == 0x07 && master[3] == 0x04) {
@@ -2576,6 +2602,7 @@ Message* MessageMap::find(const MasterSymbolString& master, bool anyDestination,
if (baseKey == INVALID_KEY) {
return nullptr;
}
bool isWriteDest = isMaster(master[1]) || master[1] == BROADCAST;
size_t maxIdLength = Message::getKeyLength(baseKey);
for (size_t idLength = maxIdLength; true; idLength--) {
uint64_t key = baseKey;
@@ -2591,44 +2618,39 @@ Message* MessageMap::find(const MasterSymbolString& master, bool anyDestination,
}
}
}
map<uint64_t, vector<Message*> >::const_iterator it;
Message* message;
if (withPassive) {
it = m_messagesByKey.find(key);
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master, onlyAvailable);
message = getFirstAvailableFromIterator(m_messagesByKey.find(key), &master, onlyAvailable);
if (message) {
return message;
}
}
if ((key & ID_SOURCE_MASK) != 0) {
key &= ~ID_SOURCE_MASK;
if (withPassive) {
// try again without specific source master
message = getFirstAvailableFromIterator(m_messagesByKey.find(key), &master, onlyAvailable);
if (message) {
return message;
}
}
if ((key & ID_SOURCE_MASK) != 0) {
key &= ~ID_SOURCE_MASK;
it = m_messagesByKey.find(key & ~ID_SOURCE_MASK); // try again without specific source master
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master, onlyAvailable);
if (message) {
return message;
}
}
}
} else {
key &= ~ID_SOURCE_MASK;
}
if (withRead) {
it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_READ); // try again with special value for active read
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master, onlyAvailable);
if (message) {
return message;
}
// try again with special value for active read
message = getFirstAvailableFromIterator(
m_messagesByKey.find(key | (isWriteDest ? ID_SOURCE_ACTIVE_READ_MASTER : ID_SOURCE_ACTIVE_READ)),
&master, onlyAvailable);
if (message) {
return message;
}
}
if (withWrite) {
it = m_messagesByKey.find(key | ID_SOURCE_ACTIVE_WRITE); // try again with special value for active write
if (it != m_messagesByKey.end()) {
Message* message = getFirstAvailable(it->second, &master, onlyAvailable);
if (message) {
return message;
}
// try again with special value for active write
message = getFirstAvailableFromIterator(
m_messagesByKey.find(key | (isWriteDest ? ID_SOURCE_ACTIVE_WRITE_MASTER : ID_SOURCE_ACTIVE_WRITE)),
&master, onlyAvailable);
if (message) {
return message;
}
}
if (idLength == 0) {
@@ -2751,11 +2773,16 @@ Message* MessageMap::getNextPoll() {
if (m_pollMessages.empty()) {
return nullptr;
}
lock();
Message* ret = m_pollMessages.top();
m_pollMessages.pop();
ret->m_pollCount++;
if (ret->m_pollOrder > g_lastPollOrder) {
g_lastPollOrder = ret->m_pollOrder;
}
ret->m_pollOrder += (unsigned int)ret->m_pollPriority;
time(&(ret->m_lastPollTime));
m_pollMessages.push(ret); // re-insert at new position
unlock();
return ret;
}
Executable → Regular
+14 -3
View File
@@ -642,8 +642,8 @@ class Message : public AttributedItem {
/** the system time when the message content was last changed, 0 for never. */
time_t m_lastChangeTime;
/** the number of times this messages was already polled for. */
unsigned int m_pollCount;
/** the polling order of this message (roughly number of polls * priority). */
unsigned int m_pollOrder;
/** the system time when this message was last polled for, 0 for never. */
time_t m_lastPollTime;
@@ -1403,13 +1403,24 @@ class MessageMap : public MappedFileReader {
* address), or 0 to ignore.
* @param until the end time to which to add updates (exclusive, also removes messages with unset destination
* address), or 0 to ignore.
* @changedSince true to use the last change time for the since/until range, false to use the last seen time.
* @param changedSince true to use the last change time for the since/until range, false to use the last seen time.
* @param messages the @a deque to which to add the found @a Message instances.
*/
void findAll(const string& circuit, const string& name, const string& levels,
bool completeMatch, bool withRead, bool withWrite, bool withPassive, bool includeEmptyLevel, bool onlyAvailable,
time_t since, time_t until, bool changedSince, deque<Message*>* messages) const;
/**
* Get the first available @a Message from the first map iterator entry.
* @param it the map iterator with list of @a Message instances to check.
* @param sameIdExtAs the optional @a MasterSymbolString to check for having the same ID.
* @param onlyAvailable true to include only available messages (default true), false to also include messages that
* are currently not available (e.g. due to unresolved or false conditions).
* @return the first available @a Message from the first map iterator entry.
*/
Message* getFirstAvailableFromIterator(const map<uint64_t, vector<Message*> >::const_iterator& it,
const MasterSymbolString* sameIdExtAs, bool onlyAvailable) const;
/**
* Find the @a Message instance for the specified master data.
* @param master the @a MasterSymbolString for identifying the @a Message.
+3
View File
@@ -228,6 +228,9 @@ int main() {
{"x,,ttq", "23:45", "10fe0700015f", "00", ""},
{"x,,ttq", "24:00", "10fe07000160", "00", ""},
{"x,,ttq", "-:-", "10fe07000100", "00", ""},
{"x,,ttq,,,,,,ttq", "23:00;05:45", "10fe0700025c17", "00", ""},
{"x,,ttq,,,,,,bi7,,,,,,ttq", "23:00;0;05:45", "10fe0700025c17", "00", ""},
{"x,,ttq,,,,,,bi7,,,,,,ttq", "23:00;1;05:45", "10fe070002dc17", "00", ""},
{"x,,ttq", "", "10fe07000161", "00", "rw"},
{"x,,ttq,2", "", "", "", "c"},
{"x,,ttq,,,,y,,bi7", "23:45;0", "10fe0700015f", "00", ""}, // bit combination
+6 -2
View File
@@ -173,7 +173,11 @@ int main() {
{"w,,x,,,,,,b0,,BI0:1,,,,b1,,BI1:1,,,,b2,,BI2:6,,,,c0,,BI0:1,,,,c1,,BI1:1,,,,c2,,BI2:1", "1;1;1;0;0;0", "ff08b509030e0700", "00", "di" },
{"w,,x,,,,,,b0,,BI0:1,,,,b1,,BI1:1,,,,b2,,BI2:6,,,,c0,,BI0:1,,,,c1,,BI1:1,,,,c2,,BI2:1", "1;0;0;0;0;1", "ff08b509030e0104", "00", "di" },
{"w,,x,,,,,,b0,,BI0:1,,,,b1,,BI1:1,,,,b2,,BI2:6,,,,c0,,BI0:1,,,,c1,,BI1:1,,,,c2,,BI2:1", "0;0;1;0;1;1", "ff08b509030e0406", "00", "di" },
{"*r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "" },
{"r,470,ccTimer.Monday,,,15,B515,0002,,,IGN:1,,,,from,,TTM", "", "", "", "M"},
{"w,470,ccTimer.Monday,,,10,B515,0002,from,,TTM", "", "", "", "kM*"},
{"", "19:00", "3115b515020002", "080272", "kd"},
{"", "19:00", "3110b51503000272", "00", "kd"},
{"*r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", ""},
{"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff75b509030d0100", "012a", "DN"},
{"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH",
// "\"naNAMEme\": {r,cirCIRCUITcuit,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42"
@@ -420,7 +424,7 @@ int main() {
result = message->decodeLastData(false, nullptr, -1,
(decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), &output);
if (result != RESULT_OK) {
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error " << (message->isWrite() ? "write: " : "read: ")
<< getResultCode(result) << endl;
error = true;
continue;
+49 -21
View File
@@ -27,12 +27,13 @@
#include <sys/time.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include "lib/utils/clock.h"
namespace ebusd {
/** the name of each @a LogFacility. */
static const char *facilityNames[] = {
static const char *s_facilityNames[] = {
"main",
"network",
"bus",
@@ -43,7 +44,7 @@ static const char *facilityNames[] = {
};
/** the name of each @a LogLevel. */
static const char* levelNames[] = {
static const char* s_levelNames[] = {
"none",
"error",
"notice",
@@ -52,19 +53,32 @@ static const char* levelNames[] = {
nullptr
};
/** the syslog level of each @a LogLevel. */
static const int s_syslogLevels[] = {
LOG_INFO,
LOG_ERR,
LOG_NOTICE,
LOG_INFO,
LOG_DEBUG,
0
};
/** the current log level by log facility. */
static LogLevel s_facilityLogLevel[] = { ll_notice, ll_notice, ll_notice, ll_notice, ll_notice, };
/** the current log FILE. */
/** the current log FILE, or nullptr if closed or syslog is used. */
static FILE* s_logFile = stdout;
/** whether to log to syslog. */
static bool s_useSyslog = false;
LogFacility parseLogFacility(const char* facility) {
if (!facility) {
return lf_COUNT;
}
char *input = strdup(facility);
char *opt = reinterpret_cast<char*>(input), *value = nullptr;
int val = getsubopt(&opt, (char *const *)facilityNames, &value);
int val = getsubopt(&opt, (char *const *)s_facilityNames, &value);
if (val < 0 || val >= lf_COUNT || value || *opt) {
free(input);
return lf_COUNT;
@@ -78,7 +92,7 @@ int parseLogFacilities(const char* facilities) {
char *opt = reinterpret_cast<char*>(input), *value = nullptr;
int newFacilites = 0;
while (*opt) {
int val = getsubopt(&opt, (char *const *)facilityNames, &value);
int val = getsubopt(&opt, (char *const *)s_facilityNames, &value);
if (val < 0 || val > lf_COUNT || value) {
free(input);
return -1;
@@ -99,7 +113,7 @@ LogLevel parseLogLevel(const char* level) {
}
char *input = strdup(level);
char *opt = reinterpret_cast<char*>(input), *value = nullptr;
int val = getsubopt(&opt, (char *const *)levelNames, &value);
int val = getsubopt(&opt, (char *const *)s_levelNames, &value);
if (val < 0 || val >= ll_COUNT || value || *opt) {
free(input);
return ll_COUNT;
@@ -109,11 +123,11 @@ LogLevel parseLogLevel(const char* level) {
}
const char* getLogFacilityStr(LogFacility facility) {
return facilityNames[facility];
return s_facilityNames[facility];
}
const char* getLogLevelStr(LogLevel level) {
return levelNames[level];
return s_levelNames[level];
}
bool setFacilitiesLogLevel(int facilities, LogLevel level) {
@@ -132,6 +146,12 @@ LogLevel getFacilityLogLevel(LogFacility facility) {
}
bool setLogFile(const char* filename) {
if (filename[0] == 0) {
closeLogFile();
openlog("ebusd", LOG_NDELAY|LOG_PID, LOG_USER);
s_useSyslog = true;
return true;
}
FILE* newFile = fopen(filename, "a");
if (newFile == nullptr) {
return false;
@@ -148,27 +168,35 @@ void closeLogFile() {
}
s_logFile = nullptr;
}
if (s_useSyslog) {
closelog();
s_useSyslog = false;
}
}
bool needsLog(const LogFacility facility, const LogLevel level) {
return s_facilityLogLevel[facility] >= level;
}
void logWrite(const char* facility, const char* level, const char* message, va_list ap) {
if (s_logFile == nullptr) {
void logWrite(const char* facility, const LogLevel level, const char* message, va_list ap) {
if (s_logFile == nullptr && !s_useSyslog) {
return;
}
struct timespec ts;
struct tm td;
clockGettime(&ts);
localtime_r(&ts.tv_sec, &td);
char* buf;
if (vasprintf(&buf, message, ap) >= 0 && buf) {
fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n",
td.tm_year+1900, td.tm_mon+1, td.tm_mday,
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
facility, level, buf);
fflush(s_logFile);
if (s_useSyslog) {
syslog(s_syslogLevels[level], "[%s %s] %s", facility, s_levelNames[level], buf);
} else {
struct timespec ts;
struct tm td;
clockGettime(&ts);
localtime_r(&ts.tv_sec, &td);
fprintf(s_logFile, "%04d-%02d-%02d %02d:%02d:%02d.%03ld [%s %s] %s\n",
td.tm_year+1900, td.tm_mon+1, td.tm_mday,
td.tm_hour, td.tm_min, td.tm_sec, ts.tv_nsec/1000000,
facility, s_levelNames[level], buf);
fflush(s_logFile);
}
}
if (buf) {
free(buf);
@@ -178,14 +206,14 @@ void logWrite(const char* facility, const char* level, const char* message, va_l
void logWrite(const LogFacility facility, const LogLevel level, const char* message, ...) {
va_list ap;
va_start(ap, message);
logWrite(facilityNames[facility], levelNames[level], message, ap);
logWrite(s_facilityNames[facility], level, message, ap);
va_end(ap);
}
void logWrite(const char* facility, const LogLevel level, const char* message, ...) {
va_list ap;
va_start(ap, message);
logWrite(facility, levelNames[level], message, ap);
logWrite(facility, level, message, ap);
va_end(ap);
}
+1 -1
View File
@@ -98,7 +98,7 @@ LogLevel getFacilityLogLevel(LogFacility facility);
/**
* Set the log file to use.
* @param filename the name of the log file to use.
* @param filename the name of the log file to use, or the empty string for syslog.
* @return true on success, false on error.
*/
bool setLogFile(const char* filename);
+1
View File
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <stdint.h>
#include <string>