use spaces instead of tab
This commit is contained in:
@@ -22,8 +22,8 @@
|
||||
namespace ebusd {
|
||||
|
||||
bool libebus_contrib_register() {
|
||||
contrib_tem_register();
|
||||
return true;
|
||||
contrib_tem_register();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "tem.h"
|
||||
@@ -36,108 +36,108 @@ using std::setw;
|
||||
using std::dec;
|
||||
|
||||
void contrib_tem_register() {
|
||||
DataTypeList::getInstance()->add(new TemParamDataType("TEM_P"));
|
||||
DataTypeList::getInstance()->add(new TemParamDataType("TEM_P"));
|
||||
}
|
||||
|
||||
result_t TemParamDataType::derive(int divisor, unsigned char bitCount, NumberDataType* &derived) {
|
||||
if (divisor == 0) {
|
||||
divisor = 1;
|
||||
}
|
||||
if (bitCount == 0) {
|
||||
bitCount = m_bitCount;
|
||||
}
|
||||
if (divisor == 1 && bitCount == 16) {
|
||||
derived = this;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
if (divisor == 0) {
|
||||
divisor = 1;
|
||||
}
|
||||
if (bitCount == 0) {
|
||||
bitCount = m_bitCount;
|
||||
}
|
||||
if (divisor == 1 && bitCount == 16) {
|
||||
derived = this;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
result_t TemParamDataType::readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) {
|
||||
unsigned int value = 0;
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) {
|
||||
unsigned int value = 0;
|
||||
|
||||
result_t result = readRawValue(input, offset, length, value);
|
||||
if (result != RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
result_t result = readRawValue(input, offset, length, value);
|
||||
if (result != RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value == m_replacement) {
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << "null";
|
||||
} else {
|
||||
output << NULL_VALUE;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
int grp = 0, num = 0;
|
||||
if (isMaster) {
|
||||
grp = (value & 0x1f); // grp in bits 0...5
|
||||
num = ((value >> 8) & 0x7f); // num in bits 8...13
|
||||
} else {
|
||||
grp = ((value >> 7) & 0x1f); // grp in bits 7...11
|
||||
num = (value & 0x7f); // num in bits 0...6
|
||||
}
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num);
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill(' ') << setw(0); // reset
|
||||
return RESULT_OK;
|
||||
if (value == m_replacement) {
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << "null";
|
||||
} else {
|
||||
output << NULL_VALUE;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
int grp = 0, num = 0;
|
||||
if (isMaster) {
|
||||
grp = (value & 0x1f); // grp in bits 0...5
|
||||
num = ((value >> 8) & 0x7f); // num in bits 8...13
|
||||
} else {
|
||||
grp = ((value >> 7) & 0x1f); // grp in bits 7...11
|
||||
num = (value & 0x7f); // num in bits 0...6
|
||||
}
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill('0') << setw(2) << dec << static_cast<int>(grp) << '-' << setw(3) << static_cast<int>(num);
|
||||
if (outputFormat & OF_JSON) {
|
||||
output << '"';
|
||||
}
|
||||
output << setfill(' ') << setw(0); // reset
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t TemParamDataType::writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) {
|
||||
unsigned int value;
|
||||
int grp, num;
|
||||
string token;
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) {
|
||||
unsigned int value;
|
||||
int grp, num;
|
||||
string token;
|
||||
|
||||
const char* str = input.str().c_str();
|
||||
if (strcasecmp(str, NULL_VALUE) == 0) {
|
||||
value = m_replacement; // replacement value
|
||||
} else {
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
char* strEnd = NULL;
|
||||
grp = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
strEnd = NULL;
|
||||
num = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
if (isMaster) {
|
||||
value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13
|
||||
} else {
|
||||
value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6
|
||||
}
|
||||
}
|
||||
if (value < getMinValue() || value > getMaxValue()) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
return writeRawValue(value, offset, length, output, usedLength);
|
||||
const char* str = input.str().c_str();
|
||||
if (strcasecmp(str, NULL_VALUE) == 0) {
|
||||
value = m_replacement; // replacement value
|
||||
} else {
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
char* strEnd = NULL;
|
||||
grp = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (input.eof() || !getline(input, token, '-')) {
|
||||
return RESULT_ERR_EOF; // incomplete
|
||||
}
|
||||
str = token.c_str();
|
||||
if (str == NULL || *str == 0) {
|
||||
return RESULT_ERR_EOF; // input too short
|
||||
}
|
||||
strEnd = NULL;
|
||||
num = (unsigned int)strtoul(str, &strEnd, 10);
|
||||
if (strEnd == NULL || strEnd == str || *strEnd != 0) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
if (grp < 0 || grp > 0x1f || num < 0 || num > 0x7f) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
if (isMaster) {
|
||||
value = grp | (num << 8); // grp in bits 0...5, num in bits 8...13
|
||||
} else {
|
||||
value = (grp << 7) | num; // grp in bits 7...11, num in bits 0...6
|
||||
}
|
||||
}
|
||||
if (value < getMinValue() || value > getMaxValue()) {
|
||||
return RESULT_ERR_OUT_OF_RANGE; // value out of range
|
||||
}
|
||||
return writeRawValue(value, offset, length, output, usedLength);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+17
-17
@@ -41,26 +41,26 @@ namespace ebusd {
|
||||
* data.
|
||||
*/
|
||||
class TemParamDataType : public NumberDataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
*/
|
||||
explicit TemParamDataType(const string id)
|
||||
: NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
*/
|
||||
explicit TemParamDataType(const string id)
|
||||
: NumberDataType(id, 16, 0, 0xffff, 0, 0xffff, 0) {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
// @copydoc
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,167 +28,167 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
DataType* type = DataTypeList::getInstance()->get("TEM_P");
|
||||
if (type == NULL) {
|
||||
cout << "datatype not registered" << endl;
|
||||
return 1;
|
||||
}
|
||||
DataType* type = DataTypeList::getInstance()->get("TEM_P");
|
||||
if (type == NULL) {
|
||||
cout << "datatype not registered" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
string checks[][5] = {
|
||||
// entry: definition, decoded value, master data, slave data, flags
|
||||
// definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
{"x,,TEM_P", "04-033", "10fe0700020421", "00", ""},
|
||||
{"x,,TEM_P", "00-000", "10fe0700020000", "00", ""},
|
||||
{"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""},
|
||||
{"x,,TEM_P", "-", "10fe070002ffff", "00", ""},
|
||||
{"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"},
|
||||
string checks[][5] = {
|
||||
// entry: definition, decoded value, master data, slave data, flags
|
||||
// definition: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
{"x,,TEM_P", "04-033", "10fe0700020421", "00", ""},
|
||||
{"x,,TEM_P", "00-000", "10fe0700020000", "00", ""},
|
||||
{"x,,TEM_P", "31-127", "10fe0700021f7f", "00", ""},
|
||||
{"x,,TEM_P", "-", "10fe070002ffff", "00", ""},
|
||||
{"x,,TEM_P", "32-000", "10fe0700022000", "00", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "10fe0700020080", "00", "Rw"},
|
||||
|
||||
{"x,,TEM_P", "04-033", "1015070000", "022102", ""},
|
||||
{"x,,TEM_P", "00-000", "1015070000", "020000", ""},
|
||||
{"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""},
|
||||
{"x,,TEM_P", "-", "1015070000", "02ffff", ""},
|
||||
{"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"},
|
||||
};
|
||||
DataFieldTemplates* templates = new DataFieldTemplates();
|
||||
DataField* fields = NULL;
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
istringstream isstr(check[0]);
|
||||
string expectStr = check[1];
|
||||
SymbolString mstr(false);
|
||||
result_t result = mstr.parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
SymbolString sstr(false);
|
||||
result = sstr.parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
string flags = check[4];
|
||||
bool isSet = flags.find('s') != string::npos;
|
||||
bool failedRead = flags.find('r') != string::npos;
|
||||
bool failedReadMatch = flags.find('R') != string::npos;
|
||||
bool failedWrite = flags.find('w') != string::npos;
|
||||
bool failedWriteMatch = flags.find('W') != string::npos;
|
||||
string item;
|
||||
vector<string> entries;
|
||||
{"x,,TEM_P", "04-033", "1015070000", "022102", ""},
|
||||
{"x,,TEM_P", "00-000", "1015070000", "020000", ""},
|
||||
{"x,,TEM_P", "31-127", "1015070000", "02ff0f", ""},
|
||||
{"x,,TEM_P", "-", "1015070000", "02ffff", ""},
|
||||
{"x,,TEM_P", "32-000", "1015070000", "022000", "Rw"},
|
||||
{"x,,TEM_P", "00-128", "1015070000", "020080", "Rw"},
|
||||
};
|
||||
DataFieldTemplates* templates = new DataFieldTemplates();
|
||||
DataField* fields = NULL;
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
istringstream isstr(check[0]);
|
||||
string expectStr = check[1];
|
||||
SymbolString mstr(false);
|
||||
result_t result = mstr.parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
SymbolString sstr(false);
|
||||
result = sstr.parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
string flags = check[4];
|
||||
bool isSet = flags.find('s') != string::npos;
|
||||
bool failedRead = flags.find('r') != string::npos;
|
||||
bool failedReadMatch = flags.find('R') != string::npos;
|
||||
bool failedWrite = flags.find('w') != string::npos;
|
||||
bool failedWriteMatch = flags.find('W') != string::npos;
|
||||
string item;
|
||||
vector<string> entries;
|
||||
|
||||
while (getline(isstr, item, FIELD_SEPARATOR))
|
||||
entries.push_back(item);
|
||||
while (getline(isstr, item, FIELD_SEPARATOR))
|
||||
entries.push_back(item);
|
||||
|
||||
if (fields != NULL) {
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1])));
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (fields == NULL) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\"=\"";
|
||||
fields->dump(cout);
|
||||
cout << "\": create OK" << endl;
|
||||
if (fields != NULL) {
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, isSet, false, (mstr[1] == BROADCAST || isMaster(mstr[1])));
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (fields == NULL) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input" << endl;
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\"=\"";
|
||||
fields->dump(cout);
|
||||
cout << "\": create OK" << endl;
|
||||
|
||||
ostringstream output;
|
||||
SymbolString writeMstr(false);
|
||||
result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
SymbolString writeSstr(false);
|
||||
result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty());
|
||||
}
|
||||
if (failedRead) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0;
|
||||
verify(failedReadMatch, "read", check[2], match, expectStr, output.str());
|
||||
}
|
||||
ostringstream output;
|
||||
SymbolString writeMstr(false);
|
||||
result = writeMstr.parseHex(mstr.getDataStr(true, false).substr(0, 10));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << mstr.getDataStr(true, false).substr(0, 10) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
SymbolString writeSstr(false);
|
||||
result = writeSstr.parseHex(sstr.getDataStr(true, false).substr(0, 2));
|
||||
if (result != RESULT_OK) {
|
||||
cout << " parse \"" << sstr.getDataStr(true, false).substr(0, 2) << "\" error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
}
|
||||
result = fields->read(pt_masterData, mstr, 0, output, 0, -1, false);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->read(pt_slaveData, sstr, 0, output, 0, -1, !output.str().empty());
|
||||
}
|
||||
if (failedRead) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " read " << fields->getName() << " >" << check[2] << " " << check[3]
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = strcasecmp(output.str().c_str(), expectStr.c_str()) == 0;
|
||||
verify(failedReadMatch, "read", check[2], match, expectStr, output.str());
|
||||
}
|
||||
|
||||
istringstream input(expectStr);
|
||||
result = fields->write(input, pt_masterData, writeMstr, 0);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->write(input, pt_slaveData, writeSstr, 0);
|
||||
}
|
||||
if (failedWrite) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " write " << fields->getName() << " >" << expectStr
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = mstr == writeMstr && sstr == writeSstr;
|
||||
verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false));
|
||||
}
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
istringstream input(expectStr);
|
||||
result = fields->write(input, pt_masterData, writeMstr, 0);
|
||||
if (result >= RESULT_OK) {
|
||||
result = fields->write(input, pt_slaveData, writeSstr, 0);
|
||||
}
|
||||
if (failedWrite) {
|
||||
if (result >= RESULT_OK) {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed write " << fields->getName() << " >"
|
||||
<< expectStr << "< OK" << endl;
|
||||
}
|
||||
} else if (result < RESULT_OK) {
|
||||
cout << " write " << fields->getName() << " >" << expectStr
|
||||
<< "< error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
bool match = mstr == writeMstr && sstr == writeSstr;
|
||||
verify(failedWriteMatch, "write", expectStr, match, mstr.getDataStr(true, false) + " " + sstr.getDataStr(true, false), writeMstr.getDataStr(true, false) + " " + writeSstr.getDataStr(true, false));
|
||||
}
|
||||
delete fields;
|
||||
fields = NULL;
|
||||
}
|
||||
|
||||
delete templates;
|
||||
return error ? 1 : 0;
|
||||
delete templates;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
+877
-877
File diff suppressed because it is too large
Load Diff
+483
-483
File diff suppressed because it is too large
Load Diff
+934
-934
File diff suppressed because it is too large
Load Diff
+341
-341
@@ -89,9 +89,9 @@ static const unsigned int OF_JSON = 0x10; //!< JSON format.
|
||||
|
||||
/** the message part in which a data field is stored. */
|
||||
enum PartType {
|
||||
pt_any, //!< stored in any data (master or slave)
|
||||
pt_masterData, //!< stored in master data
|
||||
pt_slaveData, //!< stored in slave data
|
||||
pt_any, //!< stored in any data (master or slave)
|
||||
pt_masterData, //!< stored in master data
|
||||
pt_slaveData, //!< stored in slave data
|
||||
};
|
||||
|
||||
/* flags for @a DataType. */
|
||||
@@ -151,121 +151,121 @@ void printErrorPos(ostream& out, vector<string>::iterator begin, const vector<st
|
||||
* Base class for all kinds of data types.
|
||||
*/
|
||||
class DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||
*/
|
||||
DataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement)
|
||||
: m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||
*/
|
||||
DataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement)
|
||||
: m_id(id), m_bitCount(bitCount), m_flags(flags), m_replacement(replacement) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataType() { }
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataType() { }
|
||||
|
||||
/**
|
||||
* @return the type identifier.
|
||||
*/
|
||||
string getId() const { return m_id; }
|
||||
/**
|
||||
* @return the type identifier.
|
||||
*/
|
||||
string getId() const { return m_id; }
|
||||
|
||||
/**
|
||||
* @return the number of bits (maximum length if #ADJ flag is set).
|
||||
*/
|
||||
unsigned char getBitCount() const { return m_bitCount; }
|
||||
/**
|
||||
* @return the number of bits (maximum length if #ADJ flag is set).
|
||||
*/
|
||||
unsigned char getBitCount() const { return m_bitCount; }
|
||||
|
||||
/**
|
||||
* Check whether a flag is set.
|
||||
* @param flag the flag to check (like #BCD).
|
||||
* @return whether the flag is set.
|
||||
*/
|
||||
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
|
||||
/**
|
||||
* Check whether a flag is set.
|
||||
* @param flag the flag to check (like #BCD).
|
||||
* @return whether the flag is set.
|
||||
*/
|
||||
bool hasFlag(const unsigned int flag) const { return (m_flags & flag) != 0; }
|
||||
|
||||
/**
|
||||
* @return whether this type is ignored.
|
||||
*/
|
||||
bool isIgnored() const { return hasFlag(IGN); }
|
||||
/**
|
||||
* @return whether this type is ignored.
|
||||
*/
|
||||
bool isIgnored() const { return hasFlag(IGN); }
|
||||
|
||||
/**
|
||||
* @return whether this type has an adjustable length.
|
||||
*/
|
||||
bool isAdjustableLength() const { return hasFlag(ADJ); }
|
||||
/**
|
||||
* @return whether this type has an adjustable length.
|
||||
*/
|
||||
bool isAdjustableLength() const { return hasFlag(ADJ); }
|
||||
|
||||
/**
|
||||
* @return whether this field is derived from @a NumberDataType.
|
||||
*/
|
||||
bool isNumeric() const { return hasFlag(NUM); }
|
||||
/**
|
||||
* @return whether this field is derived from @a NumberDataType.
|
||||
*/
|
||||
bool isNumeric() const { return hasFlag(NUM); }
|
||||
|
||||
/**
|
||||
* @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||
*/
|
||||
unsigned int getReplacement() const { return m_replacement; }
|
||||
/**
|
||||
* @return the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue).
|
||||
*/
|
||||
unsigned int getReplacement() const { return m_replacement; }
|
||||
|
||||
/**
|
||||
* Dump the type identifier with the specified length and optionally the
|
||||
* divisor to the output (@a FIELD_SEPARATOR is always appended!).
|
||||
* @param output the @a ostream to dump to.
|
||||
* @param length the number of symbols to read/write.
|
||||
* @return true when a non-default divisor was written to the output.
|
||||
*/
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
/**
|
||||
* Dump the type identifier with the specified length and optionally the
|
||||
* divisor to the output (@a FIELD_SEPARATOR is always appended!).
|
||||
* @param output the @a ostream to dump to.
|
||||
* @param length the number of symbols to read/write.
|
||||
* @return true when a non-default divisor was written to the output.
|
||||
*/
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
|
||||
/**
|
||||
* Internal method for reading the numeric raw value from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param value the variable in which to store the numeric raw value.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value) = 0;
|
||||
/**
|
||||
* Internal method for reading the numeric raw value from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param value the variable in which to store the numeric raw value.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value) = 0;
|
||||
|
||||
/**
|
||||
* Internal method for reading the field from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param output the ostringstream to append the formatted value to.
|
||||
* @param outputFormat the @a OutputFormat options to use.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||
/**
|
||||
* Internal method for reading the field from a @a SymbolString.
|
||||
* @param input the unescaped @a SymbolString to read the binary value from.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to read.
|
||||
* @param output the ostringstream to append the formatted value to.
|
||||
* @param outputFormat the @a OutputFormat options to use.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat) = 0;
|
||||
|
||||
/**
|
||||
* Internal method for writing the field to a @a SymbolString.
|
||||
* @param input the @a istringstream to parse the formatted value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param usedLength the variable in which to store the used length in bytes, or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0;
|
||||
/**
|
||||
* Internal method for writing the field to a @a SymbolString.
|
||||
* @param input the @a istringstream to parse the formatted value from.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||
* @param isMaster whether the @a SymbolString is the master part.
|
||||
* @param usedLength the variable in which to store the used length in bytes, or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
/** the type identifier. */
|
||||
const string m_id;
|
||||
protected:
|
||||
/** the type identifier. */
|
||||
const string m_id;
|
||||
|
||||
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
|
||||
const unsigned char m_bitCount;
|
||||
/** the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD). */
|
||||
const unsigned char m_bitCount;
|
||||
|
||||
/** the combination of flags (like #BCD). */
|
||||
const uint16_t m_flags;
|
||||
/** the combination of flags (like #BCD). */
|
||||
const uint16_t m_flags;
|
||||
|
||||
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
|
||||
const unsigned int m_replacement;
|
||||
/** the replacement value (fill-up value for @a StringDataType, no replacement if equal to @a NumberDataType#minValue). */
|
||||
const unsigned int m_replacement;
|
||||
};
|
||||
|
||||
|
||||
@@ -273,43 +273,43 @@ class DataType {
|
||||
* A string based @a DataType.
|
||||
*/
|
||||
class StringDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value).
|
||||
* @param isHex true for hex digits instead of characters.
|
||||
*/
|
||||
StringDataType(const string id, const unsigned char bitCount, const uint16_t flags,
|
||||
const unsigned int replacement, bool isHex = false)
|
||||
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (fill-up value).
|
||||
* @param isHex true for hex digits instead of characters.
|
||||
*/
|
||||
StringDataType(const string id, const unsigned char bitCount, const uint16_t flags,
|
||||
const unsigned int replacement, bool isHex = false)
|
||||
: DataType(id, bitCount, flags, replacement), m_isHex(isHex) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~StringDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~StringDataType() {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** true for hex digits instead of characters. */
|
||||
const bool m_isHex;
|
||||
private:
|
||||
/** true for hex digits instead of characters. */
|
||||
const bool m_isHex;
|
||||
};
|
||||
|
||||
|
||||
@@ -317,66 +317,66 @@ class StringDataType : public DataType {
|
||||
* A date/time based @a DataType.
|
||||
*/
|
||||
class DateTimeDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value.
|
||||
* @param hasDate true if date part is present.
|
||||
* @param hasTime true if time part is present.
|
||||
* @param resolution the the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
DateTimeDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const bool hasDate, const bool hasTime, const int16_t resolution)
|
||||
: DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set, must be multiple of 8 with flag #BCD).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value.
|
||||
* @param hasDate true if date part is present.
|
||||
* @param hasTime true if time part is present.
|
||||
* @param resolution the the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
DateTimeDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const bool hasDate, const bool hasTime, const int16_t resolution)
|
||||
: DataType(id, bitCount, flags, replacement), m_hasDate(hasDate), m_hasTime(hasTime), m_resolution(resolution) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DateTimeDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DateTimeDataType() {}
|
||||
|
||||
/**
|
||||
* @return true if date part is present.
|
||||
*/
|
||||
bool hasDate() const { return m_hasDate; }
|
||||
/**
|
||||
* @return true if date part is present.
|
||||
*/
|
||||
bool hasDate() const { return m_hasDate; }
|
||||
|
||||
/**
|
||||
* @return true if time part is present.
|
||||
*/
|
||||
bool hasTime() const { return m_hasTime; }
|
||||
/**
|
||||
* @return true if time part is present.
|
||||
*/
|
||||
bool hasTime() const { return m_hasTime; }
|
||||
|
||||
/**
|
||||
* @return the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
int16_t getResolution() const { return m_resolution; }
|
||||
/**
|
||||
* @return the resolution in minutes for time types, or 1.
|
||||
*/
|
||||
int16_t getResolution() const { return m_resolution; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** true if date part is present. */
|
||||
const bool m_hasDate;
|
||||
private:
|
||||
/** true if date part is present. */
|
||||
const bool m_hasDate;
|
||||
|
||||
/** true if time part is present. */
|
||||
const bool m_hasTime;
|
||||
/** true if time part is present. */
|
||||
const bool m_hasTime;
|
||||
|
||||
/** the resolution in minutes for time types, or 1. */
|
||||
const int16_t m_resolution;
|
||||
/** the resolution in minutes for time types, or 1. */
|
||||
const int16_t m_resolution;
|
||||
};
|
||||
|
||||
|
||||
@@ -384,135 +384,135 @@ class DateTimeDataType : public DataType {
|
||||
* A number based @a DataType.
|
||||
*/
|
||||
class NumberDataType : public DataType {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance for multiple of 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (no replacement if equal to minValue).
|
||||
* @param minValue the minimum raw value.
|
||||
* @param maxValue the maximum raw value.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const unsigned int minValue, const unsigned int maxValue, const int divisor)
|
||||
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(NULL) {}
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance for multiple of 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #BCD).
|
||||
* @param replacement the replacement value (no replacement if equal to minValue).
|
||||
* @param minValue the minimum raw value.
|
||||
* @param maxValue the maximum raw value.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const unsigned int minValue, const unsigned int maxValue, const int divisor)
|
||||
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(minValue), m_maxValue(maxValue), m_divisor(divisor), m_precision(calcPrecision(divisor)), m_firstBit(0), m_baseType(NULL) {}
|
||||
|
||||
/**
|
||||
* Constructs a new instance for less than 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #ADJ, may not include flag #BCD).
|
||||
* @param replacement the replacement value (no replacement if zero).
|
||||
* @param firstBit the offset to the first bit.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const int16_t firstBit, const int divisor)
|
||||
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor), m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {}
|
||||
/**
|
||||
* Constructs a new instance for less than 8 bits.
|
||||
* @param id the type identifier.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set).
|
||||
* @param flags the combination of flags (like #ADJ, may not include flag #BCD).
|
||||
* @param replacement the replacement value (no replacement if zero).
|
||||
* @param firstBit the offset to the first bit.
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
*/
|
||||
NumberDataType(const string id, const unsigned char bitCount, const uint16_t flags, const unsigned int replacement,
|
||||
const int16_t firstBit, const int divisor)
|
||||
: DataType(id, bitCount, flags|NUM, replacement), m_minValue(0), m_maxValue((1 << bitCount)-1), m_divisor(divisor), m_precision(0), m_firstBit(firstBit), m_baseType(NULL) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~NumberDataType() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~NumberDataType() {}
|
||||
|
||||
/**
|
||||
* Calculate the precision from the divisor.
|
||||
*
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
static unsigned char calcPrecision(const int divisor);
|
||||
/**
|
||||
* Calculate the precision from the divisor.
|
||||
*
|
||||
* @param divisor the divisor (negative for reciprocal).
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
static unsigned char calcPrecision(const int divisor);
|
||||
|
||||
// @copydoc
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
// @copydoc
|
||||
virtual bool dump(ostream& output, const unsigned char length) const;
|
||||
|
||||
/**
|
||||
* Derive a new @a NumberDataType from this.
|
||||
* @param divisor the extra divisor (negative for reciprocal) to apply, or
|
||||
* 1 for none (if applicable), or 0 to keep the current value.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set,
|
||||
* must be multiple of 8 with flag #BCD), or 0 to keep the current value.
|
||||
* @param derived the derived @a NumberDataType, or this if derivation is
|
||||
* not necessary.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
/**
|
||||
* Derive a new @a NumberDataType from this.
|
||||
* @param divisor the extra divisor (negative for reciprocal) to apply, or
|
||||
* 1 for none (if applicable), or 0 to keep the current value.
|
||||
* @param bitCount the number of bits (maximum length if #ADJ flag is set,
|
||||
* must be multiple of 8 with flag #BCD), or 0 to keep the current value.
|
||||
* @param derived the derived @a NumberDataType, or this if derivation is
|
||||
* not necessary.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t derive(int divisor, unsigned char bitCount, NumberDataType* &derived);
|
||||
|
||||
/**
|
||||
* @return the minimum raw value.
|
||||
*/
|
||||
unsigned int getMinValue() const { return m_minValue; }
|
||||
/**
|
||||
* @return the minimum raw value.
|
||||
*/
|
||||
unsigned int getMinValue() const { return m_minValue; }
|
||||
|
||||
/**
|
||||
* @return the maximum raw value.
|
||||
*/
|
||||
unsigned int getMaxValue() const { return m_maxValue; }
|
||||
/**
|
||||
* @return the maximum raw value.
|
||||
*/
|
||||
unsigned int getMaxValue() const { return m_maxValue; }
|
||||
|
||||
/**
|
||||
* @return the divisor (negative for reciprocal).
|
||||
*/
|
||||
int getDivisor() const { return m_divisor; }
|
||||
/**
|
||||
* @return the divisor (negative for reciprocal).
|
||||
*/
|
||||
int getDivisor() const { return m_divisor; }
|
||||
|
||||
/**
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
unsigned char getPrecision() const { return m_precision; }
|
||||
/**
|
||||
* @return the precision for formatting the value.
|
||||
*/
|
||||
unsigned char getPrecision() const { return m_precision; }
|
||||
|
||||
/**
|
||||
* @return the offset to the first bit.
|
||||
*/
|
||||
int16_t getFirstBit() const { return m_firstBit; }
|
||||
/**
|
||||
* @return the offset to the first bit.
|
||||
*/
|
||||
int16_t getFirstBit() const { return m_firstBit; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
// @copydoc
|
||||
virtual result_t readRawValue(SymbolString& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
unsigned int& value);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
// @copydoc
|
||||
virtual result_t readSymbols(SymbolString& input, const bool isMaster,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
ostringstream& output, OutputFormat outputFormat);
|
||||
|
||||
/**
|
||||
* Internal method for writing the numeric raw value to a @a SymbolString.
|
||||
* @param value the numeric raw value to write.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||
* @param usedLength the variable in which to store the used length in bytes,
|
||||
* or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeRawValue(unsigned int value,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, unsigned char* usedLength = NULL);
|
||||
/**
|
||||
* Internal method for writing the numeric raw value to a @a SymbolString.
|
||||
* @param value the numeric raw value to write.
|
||||
* @param offset the offset in the @a SymbolString.
|
||||
* @param length the number of symbols to write, or @a REMAIN_LEN.
|
||||
* @param output the unescaped @a SymbolString to write the binary value to.
|
||||
* @param usedLength the variable in which to store the used length in bytes,
|
||||
* or NULL.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t writeRawValue(unsigned int value,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, unsigned char* usedLength = NULL);
|
||||
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
// @copydoc
|
||||
virtual result_t writeSymbols(istringstream& input,
|
||||
const unsigned char offset, const unsigned char length,
|
||||
SymbolString& output, const bool isMaster, unsigned char* usedLength);
|
||||
|
||||
|
||||
private:
|
||||
/** the minimum raw value. */
|
||||
const unsigned int m_minValue;
|
||||
private:
|
||||
/** the minimum raw value. */
|
||||
const unsigned int m_minValue;
|
||||
|
||||
/** the maximum raw value. */
|
||||
const unsigned int m_maxValue;
|
||||
/** the maximum raw value. */
|
||||
const unsigned int m_maxValue;
|
||||
|
||||
/** the divisor (negative for reciprocal). */
|
||||
const int m_divisor;
|
||||
/** the divisor (negative for reciprocal). */
|
||||
const int m_divisor;
|
||||
|
||||
/** the precision for formatting the value. */
|
||||
const unsigned char m_precision;
|
||||
/** the precision for formatting the value. */
|
||||
const unsigned char m_precision;
|
||||
|
||||
/** the offset to the first bit. */
|
||||
const int16_t m_firstBit;
|
||||
/** the offset to the first bit. */
|
||||
const int16_t m_firstBit;
|
||||
|
||||
/** the base @a NumberDataType for derived instances. */
|
||||
NumberDataType* m_baseType;
|
||||
/** the base @a NumberDataType for derived instances. */
|
||||
NumberDataType* m_baseType;
|
||||
};
|
||||
|
||||
|
||||
@@ -520,71 +520,71 @@ class NumberDataType : public DataType {
|
||||
* A map of base @a DataType instances.
|
||||
*/
|
||||
class DataTypeList {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance and registers the known base data types.
|
||||
*/
|
||||
DataTypeList();
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance and registers the known base data types.
|
||||
*/
|
||||
DataTypeList();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataTypeList() {
|
||||
clear();
|
||||
}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DataTypeList() {
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
* @return the singleton @a DataTypeList instance.
|
||||
*/
|
||||
static DataTypeList* getInstance();
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
* @return the singleton @a DataTypeList instance.
|
||||
*/
|
||||
static DataTypeList* getInstance();
|
||||
|
||||
/**
|
||||
* Removes all @a DataType instances.
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* Removes all @a DataType instances.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Adds a @a DataType instance to this map.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
* Note: the caller may not free the added instance on success.
|
||||
*/
|
||||
result_t add(DataType* dataType);
|
||||
/**
|
||||
* Adds a @a DataType instance to this map.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
* Note: the caller may not free the added instance on success.
|
||||
*/
|
||||
result_t add(DataType* dataType);
|
||||
|
||||
/**
|
||||
* Adds a @a DataType instance for later cleanup.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
*/
|
||||
void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); }
|
||||
/**
|
||||
* Adds a @a DataType instance for later cleanup.
|
||||
* @param dataType the @a DataType instance to add.
|
||||
*/
|
||||
void addCleanup(DataType* dataType) { m_cleanupTypes.push_back(dataType); }
|
||||
|
||||
/**
|
||||
* Gets the @a DataType instance with the specified ID.
|
||||
* @param id the ID string (excluding optional length suffix).
|
||||
* @param length the length in bytes, or 0 for default.
|
||||
* @return the @a DataType instance, or NULL if not available.
|
||||
* Note: the caller may not free the instance.
|
||||
*/
|
||||
DataType* get(const string id, const unsigned char length = 0);
|
||||
/**
|
||||
* Gets the @a DataType instance with the specified ID.
|
||||
* @param id the ID string (excluding optional length suffix).
|
||||
* @param length the length in bytes, or 0 for default.
|
||||
* @return the @a DataType instance, or NULL if not available.
|
||||
* Note: the caller may not free the instance.
|
||||
*/
|
||||
DataType* get(const string id, const unsigned char length = 0);
|
||||
|
||||
|
||||
private:
|
||||
/** the known @a DataType instances by ID only. */
|
||||
map<string, DataType*> m_typesById;
|
||||
private:
|
||||
/** the known @a DataType instances by ID only. */
|
||||
map<string, DataType*> m_typesById;
|
||||
|
||||
/** the known @a DataType instances by ID and length (i.e. "ID:BITS").
|
||||
* Note: adjustable length types are stored by ID only. */
|
||||
map<string, DataType*> m_typesByIdLength;
|
||||
/** the known @a DataType instances by ID and length (i.e. "ID:BITS").
|
||||
* Note: adjustable length types are stored by ID only. */
|
||||
map<string, DataType*> m_typesByIdLength;
|
||||
|
||||
/** the @a DataType instances to cleanup. */
|
||||
list<DataType*> m_cleanupTypes;
|
||||
/** the @a DataType instances to cleanup. */
|
||||
list<DataType*> m_cleanupTypes;
|
||||
|
||||
/** the singleton instance. */
|
||||
static DataTypeList s_instance;
|
||||
/** the singleton instance. */
|
||||
static DataTypeList s_instance;
|
||||
|
||||
#ifdef HAVE_CONTRIB
|
||||
/** true when contributed datatypes were successfully initialized. */
|
||||
static bool s_contrib_initialized;
|
||||
/** true when contributed datatypes were successfully initialized. */
|
||||
static bool s_contrib_initialized;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+218
-218
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "device.h"
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_PPOLL
|
||||
# include <poll.h>
|
||||
# include <poll.h>
|
||||
#endif
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -39,289 +39,289 @@
|
||||
namespace ebusd {
|
||||
|
||||
Device::~Device() {
|
||||
close();
|
||||
close();
|
||||
}
|
||||
|
||||
Device* Device::create(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend) {
|
||||
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
|
||||
char* in = strdup(name);
|
||||
bool udp = false;
|
||||
char* addrpos = in;
|
||||
char* portpos = strchr(addrpos, ':');
|
||||
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
|
||||
addrpos += 4;
|
||||
portpos = strchr(addrpos, ':');
|
||||
}
|
||||
if (portpos == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid protocol or missing port
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
free(in);
|
||||
return NULL; // invalid port
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
|
||||
*portpos = 0;
|
||||
if (inet_aton(addrpos, &address.sin_addr) == 0) {
|
||||
struct hostent* h = gethostbyname(addrpos);
|
||||
if (h == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid host
|
||||
}
|
||||
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
|
||||
}
|
||||
free(in);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons((uint16_t)port);
|
||||
return new NetworkDevice(name, address, readOnly, initialSend, udp);
|
||||
}
|
||||
return new SerialDevice(name, checkDevice, readOnly, initialSend);
|
||||
if (strchr(name, '/') == NULL && strchr(name, ':') != NULL) {
|
||||
char* in = strdup(name);
|
||||
bool udp = false;
|
||||
char* addrpos = in;
|
||||
char* portpos = strchr(addrpos, ':');
|
||||
if (portpos == addrpos+3 && (strncmp(addrpos, "tcp", 3) == 0 || (udp=(strncmp(addrpos, "udp", 3) == 0)))) {
|
||||
addrpos += 4;
|
||||
portpos = strchr(addrpos, ':');
|
||||
}
|
||||
if (portpos == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid protocol or missing port
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
unsigned int port = parseInt(portpos+1, 10, 1, 65535, result);
|
||||
if (result != RESULT_OK) {
|
||||
free(in);
|
||||
return NULL; // invalid port
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
|
||||
*portpos = 0;
|
||||
if (inet_aton(addrpos, &address.sin_addr) == 0) {
|
||||
struct hostent* h = gethostbyname(addrpos);
|
||||
if (h == NULL) {
|
||||
free(in);
|
||||
return NULL; // invalid host
|
||||
}
|
||||
memcpy(&address.sin_addr, h->h_addr_list[0], h->h_length);
|
||||
}
|
||||
free(in);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = (in_port_t)htons((uint16_t)port);
|
||||
return new NetworkDevice(name, address, readOnly, initialSend, udp);
|
||||
}
|
||||
return new SerialDevice(name, checkDevice, readOnly, initialSend);
|
||||
}
|
||||
|
||||
void Device::close() {
|
||||
if (m_fd != -1) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
if (m_fd != -1) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::isValid() {
|
||||
if (m_fd == -1) {
|
||||
return false;
|
||||
}
|
||||
if (m_checkDevice) {
|
||||
checkDevice();
|
||||
}
|
||||
return m_fd != -1;
|
||||
if (m_fd == -1) {
|
||||
return false;
|
||||
}
|
||||
if (m_checkDevice) {
|
||||
checkDevice();
|
||||
}
|
||||
return m_fd != -1;
|
||||
}
|
||||
|
||||
result_t Device::send(const unsigned char value) {
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_readOnly || write(value) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, false);
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_readOnly || write(value) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, false);
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
result_t Device::recv(const unsigned int timeout, unsigned char& value) {
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (!available() && timeout > 0) {
|
||||
int ret;
|
||||
struct timespec tdiff;
|
||||
if (!isValid()) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (!available() && 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/1000000;
|
||||
tdiff.tv_nsec = (timeout%1000000)*1000;
|
||||
|
||||
#ifdef HAVE_PPOLL
|
||||
int nfds = 1;
|
||||
struct pollfd fds[nfds];
|
||||
int 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;
|
||||
fds[0].fd = m_fd;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
ret = ppoll(fds, nfds, &tdiff, NULL);
|
||||
#else
|
||||
#ifdef HAVE_PSELECT
|
||||
fd_set readfds;
|
||||
fd_set readfds;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(m_fd, &readfds);
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(m_fd, &readfds);
|
||||
|
||||
ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
ret = pselect(m_fd + 1, &readfds, NULL, NULL, &tdiff, NULL);
|
||||
#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) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return RESULT_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
if (ret == -1) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return RESULT_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// directly read byte from device
|
||||
ssize_t nbytes = read(value);
|
||||
if (nbytes == 0) {
|
||||
return RESULT_ERR_EOF;
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, true);
|
||||
}
|
||||
return RESULT_OK;
|
||||
// directly read byte from device
|
||||
ssize_t nbytes = read(value);
|
||||
if (nbytes == 0) {
|
||||
return RESULT_ERR_EOF;
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (m_listener != NULL) {
|
||||
m_listener->notifyDeviceData(value, true);
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
result_t SerialDevice::open() {
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
struct termios newSettings;
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
struct termios newSettings;
|
||||
|
||||
// open file descriptor
|
||||
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
|
||||
// open file descriptor
|
||||
m_fd = ::open(m_name, O_RDWR | O_NOCTTY);
|
||||
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (isatty(m_fd) == 0) {
|
||||
close();
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
if (isatty(m_fd) == 0) {
|
||||
close();
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
if (flock(m_fd, LOCK_EX|LOCK_NB)) {
|
||||
close();
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
if (flock(m_fd, LOCK_EX|LOCK_NB)) {
|
||||
close();
|
||||
return RESULT_ERR_DEVICE;
|
||||
}
|
||||
|
||||
// save current settings
|
||||
tcgetattr(m_fd, &m_oldSettings);
|
||||
// save current settings
|
||||
tcgetattr(m_fd, &m_oldSettings);
|
||||
|
||||
// create new settings
|
||||
memset(&newSettings, '\0', sizeof(newSettings));
|
||||
// create new settings
|
||||
memset(&newSettings, '\0', sizeof(newSettings));
|
||||
|
||||
newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD);
|
||||
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
|
||||
newSettings.c_iflag |= IGNPAR; // ignore parity errors
|
||||
newSettings.c_oflag &= ~OPOST;
|
||||
newSettings.c_cflag |= (B2400 | CS8 | CLOCAL | CREAD);
|
||||
newSettings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // non-canonical mode
|
||||
newSettings.c_iflag |= IGNPAR; // ignore parity errors
|
||||
newSettings.c_oflag &= ~OPOST;
|
||||
|
||||
// non-canonical mode: read() blocks until at least one byte is available
|
||||
newSettings.c_cc[VMIN] = 1;
|
||||
newSettings.c_cc[VTIME] = 0;
|
||||
// non-canonical mode: read() blocks until at least one byte is available
|
||||
newSettings.c_cc[VMIN] = 1;
|
||||
newSettings.c_cc[VTIME] = 0;
|
||||
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIFLUSH);
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIFLUSH);
|
||||
|
||||
// activate new settings of serial device
|
||||
tcsetattr(m_fd, TCSAFLUSH, &newSettings);
|
||||
// activate new settings of serial device
|
||||
tcsetattr(m_fd, TCSAFLUSH, &newSettings);
|
||||
|
||||
// set serial device into blocking mode
|
||||
fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL) & ~O_NONBLOCK);
|
||||
// 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;
|
||||
if (m_initialSend && write(ESC) != 1) {
|
||||
return RESULT_ERR_SEND;
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void SerialDevice::close() {
|
||||
if (m_fd != -1) {
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIOFLUSH);
|
||||
if (m_fd != -1) {
|
||||
// empty device buffer
|
||||
tcflush(m_fd, TCIOFLUSH);
|
||||
|
||||
// restore previous settings of the device
|
||||
tcsetattr(m_fd, TCSANOW, &m_oldSettings);
|
||||
}
|
||||
Device::close();
|
||||
// restore previous settings of the device
|
||||
tcsetattr(m_fd, TCSANOW, &m_oldSettings);
|
||||
}
|
||||
Device::close();
|
||||
}
|
||||
|
||||
void SerialDevice::checkDevice() {
|
||||
int port;
|
||||
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
|
||||
close();
|
||||
}
|
||||
int port;
|
||||
if (ioctl(m_fd, TIOCMGET, &port) == -1) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result_t NetworkDevice::open() {
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int ret;
|
||||
if (m_udp) {
|
||||
struct sockaddr_in address = m_address;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address));
|
||||
} else {
|
||||
int value = 1;
|
||||
ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
value = 1;
|
||||
setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
|
||||
}
|
||||
if (ret < 0) {
|
||||
close();
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int cnt;
|
||||
if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) {
|
||||
// skip buffered input
|
||||
unsigned char buf[256];
|
||||
while (::read(m_fd, &buf, 256) > 0) {
|
||||
}
|
||||
}
|
||||
if (m_bufSize == 0) {
|
||||
m_bufSize = MAX_LEN+1;
|
||||
m_buffer = (unsigned char*)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;
|
||||
if (m_fd != -1) {
|
||||
close();
|
||||
}
|
||||
m_fd = socket(AF_INET, m_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
if (m_fd < 0) {
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int ret;
|
||||
if (m_udp) {
|
||||
struct sockaddr_in address = m_address;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
ret = bind(m_fd, (struct sockaddr*)&address, sizeof(address));
|
||||
} else {
|
||||
int value = 1;
|
||||
ret = setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
value = 1;
|
||||
setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<void*>(&value), sizeof(value));
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = connect(m_fd, (struct sockaddr*)&m_address, sizeof(m_address));
|
||||
}
|
||||
if (ret < 0) {
|
||||
close();
|
||||
return RESULT_ERR_GENERIC_IO;
|
||||
}
|
||||
int cnt;
|
||||
if (ioctl(m_fd, FIONREAD, &cnt) >= 0 && cnt > 1) {
|
||||
// skip buffered input
|
||||
unsigned char buf[256];
|
||||
while (::read(m_fd, &buf, 256) > 0) {
|
||||
}
|
||||
}
|
||||
if (m_bufSize == 0) {
|
||||
m_bufSize = MAX_LEN+1;
|
||||
m_buffer = (unsigned char*)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::checkDevice() {
|
||||
unsigned char value;
|
||||
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
if (c == 0 || (c < 0 && errno != EAGAIN)) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
close();
|
||||
}
|
||||
unsigned char value;
|
||||
ssize_t c = ::recv(m_fd, &value, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
if (c == 0 || (c < 0 && errno != EAGAIN)) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkDevice::available() {
|
||||
return m_buffer && m_bufLen > 0;
|
||||
return m_buffer && m_bufLen > 0;
|
||||
}
|
||||
|
||||
ssize_t NetworkDevice::write(const unsigned char value) {
|
||||
m_bufLen = 0; // flush read buffer
|
||||
return Device::write(value);
|
||||
m_bufLen = 0; // flush read buffer
|
||||
return Device::write(value);
|
||||
}
|
||||
|
||||
ssize_t NetworkDevice::read(unsigned char& value) {
|
||||
if (available()) {
|
||||
value = m_buffer[m_bufPos];
|
||||
m_bufPos = (unsigned char)((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 = (unsigned char)(size-1);
|
||||
return size;
|
||||
}
|
||||
return Device::read(value);
|
||||
if (available()) {
|
||||
value = m_buffer[m_bufPos];
|
||||
m_bufPos = (unsigned char)((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 = (unsigned char)(size-1);
|
||||
return size;
|
||||
}
|
||||
return Device::read(value);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+177
-177
@@ -42,18 +42,18 @@ namespace ebusd {
|
||||
* Interface for listening to data received on/sent to a device.
|
||||
*/
|
||||
class DeviceListener {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DeviceListener() {}
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DeviceListener() {}
|
||||
|
||||
/**
|
||||
* Listener method that is called when a data byte was received/sent.
|
||||
* @param byte the data byte received/sent.
|
||||
* @param received @a true on reception, @a false on sending.
|
||||
*/
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract
|
||||
/**
|
||||
* Listener method that is called when a data byte was received/sent.
|
||||
* @param byte the data byte received/sent.
|
||||
* @param received @a true on reception, @a false on sending.
|
||||
*/
|
||||
virtual void notifyDeviceData(const unsigned char byte, bool received) = 0; // abstract
|
||||
};
|
||||
|
||||
|
||||
@@ -61,227 +61,227 @@ class DeviceListener {
|
||||
* The base class for accessing an eBUS.
|
||||
*/
|
||||
class Device {
|
||||
public:
|
||||
/**
|
||||
* 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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
*/
|
||||
Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
|
||||
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
|
||||
m_listener(NULL) {}
|
||||
public:
|
||||
/**
|
||||
* 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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
*/
|
||||
Device(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
|
||||
: m_name(name), m_checkDevice(checkDevice), m_readOnly(readOnly), m_initialSend(initialSend), m_fd(-1),
|
||||
m_listener(NULL) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Device();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~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 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 NULL on error.
|
||||
* Note: the caller needs to free the created instance.
|
||||
*/
|
||||
static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false);
|
||||
/**
|
||||
* 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 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 NULL on error.
|
||||
* Note: the caller needs to free the created instance.
|
||||
*/
|
||||
static Device* create(const char* name, const bool checkDevice = true, const bool readOnly = false, const bool initialSend = false);
|
||||
|
||||
/**
|
||||
* Get the transfer latency of this device.
|
||||
* @return the transfer latency in microseconds.
|
||||
*/
|
||||
virtual unsigned int getLatency() const { return 0; }
|
||||
/**
|
||||
* Get the transfer latency of this device.
|
||||
* @return the transfer latency in microseconds.
|
||||
*/
|
||||
virtual unsigned int getLatency() const { return 0; }
|
||||
|
||||
/**
|
||||
* Open the file descriptor.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t open() = 0; // abstract
|
||||
/**
|
||||
* Open the file descriptor.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
virtual result_t open() = 0; // abstract
|
||||
|
||||
/**
|
||||
* Close the file descriptor if opened.
|
||||
*/
|
||||
virtual void close();
|
||||
/**
|
||||
* Close the file descriptor if opened.
|
||||
*/
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* Write a single byte to the device.
|
||||
* @param value the byte value to write.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
result_t send(const unsigned char value);
|
||||
/**
|
||||
* Write a single byte to the device.
|
||||
* @param value the byte value to write.
|
||||
* @return the @a result_t code.
|
||||
*/
|
||||
result_t send(const unsigned char value);
|
||||
|
||||
/**
|
||||
* Read a single byte from the device.
|
||||
* @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
|
||||
* @param value the reference in which the received byte value is stored.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
result_t recv(const unsigned int timeout, unsigned char& value);
|
||||
/**
|
||||
* Read a single byte from the device.
|
||||
* @param timeout maximum time to wait for the byte in microseconds, or 0 for infinite.
|
||||
* @param value the reference in which the received byte value is stored.
|
||||
* @return the result_t code.
|
||||
*/
|
||||
result_t recv(const unsigned int timeout, unsigned char& value);
|
||||
|
||||
/**
|
||||
* Return the device name.
|
||||
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
*/
|
||||
const char* getName() { return m_name; }
|
||||
/**
|
||||
* Return the device name.
|
||||
* @return the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
*/
|
||||
const char* getName() { return m_name; }
|
||||
|
||||
/**
|
||||
* Return whether the device is opened and available.
|
||||
* @return whether the device is opened and available.
|
||||
*/
|
||||
bool isValid();
|
||||
/**
|
||||
* Return whether the device is opened and available.
|
||||
* @return whether the device is opened and available.
|
||||
*/
|
||||
bool isValid();
|
||||
|
||||
/**
|
||||
* Return whether to allow read access to the device only.
|
||||
* @return whether to allow read access to the device only.
|
||||
*/
|
||||
bool isReadOnly() const { return m_readOnly; }
|
||||
/**
|
||||
* Return whether to allow read access to the device only.
|
||||
* @return whether to allow read access to the device only.
|
||||
*/
|
||||
bool isReadOnly() const { return m_readOnly; }
|
||||
|
||||
/**
|
||||
* Set the @a DeviceListener.
|
||||
* @param listener the @a DeviceListener.
|
||||
*/
|
||||
void setListener(DeviceListener* listener) { m_listener = listener; }
|
||||
/**
|
||||
* Set the @a DeviceListener.
|
||||
* @param listener the @a DeviceListener.
|
||||
*/
|
||||
void setListener(DeviceListener* listener) { m_listener = listener; }
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Check if the device is still available and close it if not.
|
||||
*/
|
||||
virtual void checkDevice() = 0; // abstract
|
||||
protected:
|
||||
/**
|
||||
* Check if the device is still available and close it if not.
|
||||
*/
|
||||
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; }
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); }
|
||||
/**
|
||||
* Write a single byte.
|
||||
* @param value the byte value to write.
|
||||
* @return the number of bytes written, or -1 on error.
|
||||
*/
|
||||
virtual ssize_t write(const unsigned char value) { return ::write(m_fd, &value, 1); }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); }
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual ssize_t read(unsigned char& value) { return ::read(m_fd, &value, 1); }
|
||||
|
||||
/** the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network). */
|
||||
const char* m_name;
|
||||
/** 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). */
|
||||
const bool m_checkDevice;
|
||||
/** whether to regularly check the device availability (only for serial devices). */
|
||||
const bool m_checkDevice;
|
||||
|
||||
/** whether to allow read access to the device only. */
|
||||
const bool m_readOnly;
|
||||
/** 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 to send an initial @a ESC symbol in @a open(). */
|
||||
const bool m_initialSend;
|
||||
|
||||
/** the opened file descriptor, or -1. */
|
||||
int m_fd;
|
||||
/** the opened file descriptor, or -1. */
|
||||
int m_fd;
|
||||
|
||||
|
||||
private:
|
||||
/** the @a DeviceListener, or NULL. */
|
||||
DeviceListener* m_listener;
|
||||
private:
|
||||
/** the @a DeviceListener, or NULL. */
|
||||
DeviceListener* m_listener;
|
||||
};
|
||||
|
||||
/**
|
||||
* The @a Device for directly connected serial interfaces (tty).
|
||||
*/
|
||||
class SerialDevice : public Device {
|
||||
public:
|
||||
/**
|
||||
* 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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
*/
|
||||
SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
|
||||
: Device(name, checkDevice, readOnly, initialSend) {}
|
||||
public:
|
||||
/**
|
||||
* 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 readOnly whether to allow read access to the device only.
|
||||
* @param initialSend whether to send an initial @a ESC symbol in @a open().
|
||||
*/
|
||||
SerialDevice(const char* name, const bool checkDevice, const bool readOnly, const bool initialSend)
|
||||
: Device(name, checkDevice, readOnly, initialSend) {}
|
||||
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
|
||||
// @copydoc
|
||||
virtual void close();
|
||||
// @copydoc
|
||||
virtual void close();
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
|
||||
|
||||
private:
|
||||
/** the previous settings of the device for restoring. */
|
||||
termios m_oldSettings;
|
||||
private:
|
||||
/** the previous settings of the device for restoring. */
|
||||
termios m_oldSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* The @a Device for remote network interfaces.
|
||||
*/
|
||||
class NetworkDevice : public Device {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
* @param address the socket address of the device.
|
||||
* @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.
|
||||
*/
|
||||
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
|
||||
const bool udp)
|
||||
: Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp),
|
||||
m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param name the device name (e.g. "/dev/ttyUSB0" for serial, "127.0.0.1:1234" for network).
|
||||
* @param address the socket address of the device.
|
||||
* @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.
|
||||
*/
|
||||
NetworkDevice(const char* name, const struct sockaddr_in address, const bool readOnly, const bool initialSend,
|
||||
const bool udp)
|
||||
: Device(name, true, readOnly, initialSend), m_address(address), m_udp(udp),
|
||||
m_buffer(NULL), m_bufSize(0), m_bufLen(0), m_bufPos(0) {}
|
||||
|
||||
// @copydoc
|
||||
virtual unsigned int getLatency() const { return 10000; }
|
||||
// @copydoc
|
||||
virtual unsigned int getLatency() const { return 10000; }
|
||||
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
// @copydoc
|
||||
virtual result_t open();
|
||||
|
||||
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
protected:
|
||||
// @copydoc
|
||||
virtual void checkDevice();
|
||||
|
||||
// @copydoc
|
||||
virtual bool available();
|
||||
// @copydoc
|
||||
virtual bool available();
|
||||
|
||||
// @copydoc
|
||||
virtual ssize_t write(const unsigned char value);
|
||||
// @copydoc
|
||||
virtual ssize_t write(const unsigned char value);
|
||||
|
||||
// @copydoc
|
||||
virtual ssize_t read(unsigned char& value);
|
||||
// @copydoc
|
||||
virtual ssize_t read(unsigned char& value);
|
||||
|
||||
|
||||
private:
|
||||
/** the socket address of the device. */
|
||||
const struct sockaddr_in m_address;
|
||||
private:
|
||||
/** the socket address of the device. */
|
||||
const struct sockaddr_in m_address;
|
||||
|
||||
/** true for UDP, false to TCP. */
|
||||
const bool m_udp;
|
||||
/** true for UDP, false to TCP. */
|
||||
const bool m_udp;
|
||||
|
||||
/** the buffer memory, or NULL. */
|
||||
unsigned char* m_buffer;
|
||||
/** the buffer memory, or NULL. */
|
||||
unsigned char* m_buffer;
|
||||
|
||||
/** the buffer size. */
|
||||
unsigned char m_bufSize;
|
||||
/** the buffer size. */
|
||||
unsigned char m_bufSize;
|
||||
|
||||
/** the buffer fill length. */
|
||||
unsigned char m_bufLen;
|
||||
/** the buffer fill length. */
|
||||
unsigned char m_bufLen;
|
||||
|
||||
/** the buffer read position. */
|
||||
unsigned char m_bufPos;
|
||||
/** the buffer read position. */
|
||||
unsigned char m_bufPos;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+298
-298
@@ -70,319 +70,319 @@ extern unsigned int parseInt(const char* str, int base, const unsigned int minVa
|
||||
* An abstract class that support reading definitions from a file.
|
||||
*/
|
||||
class FileReader {
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
*/
|
||||
explicit FileReader(bool supportsDefaults)
|
||||
: m_supportsDefaults(supportsDefaults) {}
|
||||
public:
|
||||
/**
|
||||
* Construct a new instance.
|
||||
*/
|
||||
explicit FileReader(bool supportsDefaults)
|
||||
: m_supportsDefaults(supportsDefaults) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~FileReader() {}
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~FileReader() {}
|
||||
|
||||
/**
|
||||
* Read the definitions from a file.
|
||||
* @param filename the name of the file being read.
|
||||
* @param verbose whether to verbosely log problems.
|
||||
* @param defaultDest the default destination address (may be overwritten by file name), or empty.
|
||||
* @param defaultCircuit the default circuit name (may be overwritten by file name), or empty.
|
||||
* @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readFromFile(const string filename, bool verbose = false,
|
||||
string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") {
|
||||
ifstream ifs;
|
||||
ifs.open(filename.c_str(), ifstream::in);
|
||||
if (!ifs.is_open()) {
|
||||
m_lastError = filename;
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
size_t lastSep = filename.find_last_of('/');
|
||||
if (lastSep != string::npos) { // potential destination address, matches "^ZZ."
|
||||
// extract defaultDest, defaultCircuit, defaultSuffix from filename:
|
||||
// ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv
|
||||
unsigned char checkDest;
|
||||
string checkIdent, useCircuit, useSuffix;
|
||||
unsigned int checkSw, checkHw;
|
||||
if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) {
|
||||
defaultDest = filename.substr(lastSep+1, 2);
|
||||
if (!useCircuit.empty()) {
|
||||
defaultCircuit = useCircuit;
|
||||
}
|
||||
if (!useSuffix.empty()) {
|
||||
defaultSuffix = useSuffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
vector< vector<string> > defaults;
|
||||
while (splitFields(ifs, row, lineNo)) {
|
||||
if (row.empty()) {
|
||||
continue;
|
||||
}
|
||||
result_t result;
|
||||
vector<string>::iterator it = row.begin();
|
||||
const vector<string>::iterator end = row.end();
|
||||
if (m_supportsDefaults) {
|
||||
if (row[0][0] == '*') {
|
||||
row[0] = row[0].substr(1);
|
||||
result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
if (result == RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
if (!verbose) {
|
||||
ifs.close();
|
||||
ostringstream error;
|
||||
error << filename << ":" << static_cast<unsigned>(lineNo);
|
||||
if (m_lastError.length() > 0) {
|
||||
error << ": " << m_lastError;
|
||||
}
|
||||
m_lastError = error.str();
|
||||
return result;
|
||||
}
|
||||
if (m_lastError.length() > 0) {
|
||||
cout << m_lastError << endl;
|
||||
}
|
||||
printErrorPos(cout, row.begin(), end, it, filename, lineNo, result);
|
||||
} else if (!verbose) {
|
||||
m_lastError = "";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read the definitions from a file.
|
||||
* @param filename the name of the file being read.
|
||||
* @param verbose whether to verbosely log problems.
|
||||
* @param defaultDest the default destination address (may be overwritten by file name), or empty.
|
||||
* @param defaultCircuit the default circuit name (may be overwritten by file name), or empty.
|
||||
* @param defaultSuffix the default circuit name suffix (starting with a ".", may be overwritten by file name, or empty.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t readFromFile(const string filename, bool verbose = false,
|
||||
string defaultDest = "", string defaultCircuit = "", string defaultSuffix = "") {
|
||||
ifstream ifs;
|
||||
ifs.open(filename.c_str(), ifstream::in);
|
||||
if (!ifs.is_open()) {
|
||||
m_lastError = filename;
|
||||
return RESULT_ERR_NOTFOUND;
|
||||
}
|
||||
size_t lastSep = filename.find_last_of('/');
|
||||
if (lastSep != string::npos) { // potential destination address, matches "^ZZ."
|
||||
// extract defaultDest, defaultCircuit, defaultSuffix from filename:
|
||||
// ZZ.IDENT[.CIRCUIT][.SUFFIX].*csv
|
||||
unsigned char checkDest;
|
||||
string checkIdent, useCircuit, useSuffix;
|
||||
unsigned int checkSw, checkHw;
|
||||
if (extractDefaultsFromFilename(filename.substr(lastSep+1), checkDest, checkIdent, useCircuit, useSuffix, checkSw, checkHw)) {
|
||||
defaultDest = filename.substr(lastSep+1, 2);
|
||||
if (!useCircuit.empty()) {
|
||||
defaultCircuit = useCircuit;
|
||||
}
|
||||
if (!useSuffix.empty()) {
|
||||
defaultSuffix = useSuffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
vector< vector<string> > defaults;
|
||||
while (splitFields(ifs, row, lineNo)) {
|
||||
if (row.empty()) {
|
||||
continue;
|
||||
}
|
||||
result_t result;
|
||||
vector<string>::iterator it = row.begin();
|
||||
const vector<string>::iterator end = row.end();
|
||||
if (m_supportsDefaults) {
|
||||
if (row[0][0] == '*') {
|
||||
row[0] = row[0].substr(1);
|
||||
result = addDefaultFromFile(defaults, row, it, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
if (result == RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, &defaults, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
} else {
|
||||
result = addFromFile(it, end, NULL, defaultDest, defaultCircuit, defaultSuffix, filename, lineNo);
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
if (!verbose) {
|
||||
ifs.close();
|
||||
ostringstream error;
|
||||
error << filename << ":" << static_cast<unsigned>(lineNo);
|
||||
if (m_lastError.length() > 0) {
|
||||
error << ": " << m_lastError;
|
||||
}
|
||||
m_lastError = error.str();
|
||||
return result;
|
||||
}
|
||||
if (m_lastError.length() > 0) {
|
||||
cout << m_lastError << endl;
|
||||
}
|
||||
printErrorPos(cout, row.begin(), end, it, filename, lineNo, result);
|
||||
} else if (!verbose) {
|
||||
m_lastError = "";
|
||||
}
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
return RESULT_OK;
|
||||
}
|
||||
ifs.close();
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a @a string describing the last error position.
|
||||
* @return a @a string describing the last error position.
|
||||
*/
|
||||
virtual string getLastError() { return m_lastError; }
|
||||
/**
|
||||
* Return a @a string describing the last error position.
|
||||
* @return a @a string describing the last error position.
|
||||
*/
|
||||
virtual string getLastError() { return m_lastError; }
|
||||
|
||||
/**
|
||||
* Add a default row that was read from a file.
|
||||
* @param defaults the list to add the default row to.
|
||||
* @param row the default row (initial star char removed).
|
||||
* @param begin an iterator to the first column of the default row to read (for error reporting).
|
||||
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
|
||||
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
|
||||
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
|
||||
* @param filename the name of the file being read.
|
||||
* @param lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
|
||||
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) {
|
||||
defaults.push_back(row);
|
||||
begin = row.end();
|
||||
return RESULT_OK;
|
||||
}
|
||||
/**
|
||||
* Add a default row that was read from a file.
|
||||
* @param defaults the list to add the default row to.
|
||||
* @param row the default row (initial star char removed).
|
||||
* @param begin an iterator to the first column of the default row to read (for error reporting).
|
||||
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
|
||||
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
|
||||
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
|
||||
* @param filename the name of the file being read.
|
||||
* @param lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addDefaultFromFile(vector< vector<string> >& defaults, vector<string>& row,
|
||||
vector<string>::iterator& begin, string defaultDest, string defaultCircuit, string defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) {
|
||||
defaults.push_back(row);
|
||||
begin = row.end();
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a definition that was read from a file.
|
||||
* @param begin an iterator to the first column of the definition row to read.
|
||||
* @param end the end iterator of the definition row to read.
|
||||
* @param defaults all previously read default rows (initial star char removed), or NULL if not supported.
|
||||
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
|
||||
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
|
||||
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
|
||||
* @param filename the name of the file being read.
|
||||
* @param lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end,
|
||||
vector< vector<string> >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) = 0;
|
||||
/**
|
||||
* Add a definition that was read from a file.
|
||||
* @param begin an iterator to the first column of the definition row to read.
|
||||
* @param end the end iterator of the definition row to read.
|
||||
* @param defaults all previously read default rows (initial star char removed), or NULL if not supported.
|
||||
* @param defaultDest the valid destination address extracted from the file name (from ZZ part), or empty.
|
||||
* @param defaultCircuit the valid circuit name extracted from the file name (from IDENT part), or empty.
|
||||
* @param defaultSuffix the valid circuit name suffix (starting with a ".") extracted from the file name (number after after IDENT part and "."), or empty.
|
||||
* @param filename the name of the file being read.
|
||||
* @param lineNo the current line number in the file being read.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
virtual result_t addFromFile(vector<string>::iterator& begin, const vector<string>::iterator end,
|
||||
vector< vector<string> >* defaults, const string& defaultDest, const string& defaultCircuit, const string& defaultSuffix,
|
||||
const string& filename, unsigned int lineNo) = 0;
|
||||
|
||||
/**
|
||||
* Left and right trim the string.
|
||||
* @param str the @a string to trim.
|
||||
*/
|
||||
static void trim(string& str) {
|
||||
size_t pos = str.find_first_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(0, pos);
|
||||
}
|
||||
pos = str.find_last_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(pos+1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Left and right trim the string.
|
||||
* @param str the @a string to trim.
|
||||
*/
|
||||
static void trim(string& str) {
|
||||
size_t pos = str.find_first_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(0, pos);
|
||||
}
|
||||
pos = str.find_last_not_of(" \t");
|
||||
if (pos != string::npos) {
|
||||
str.erase(pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all upper case characters in the string to lower case.
|
||||
* @param str the @a string to convert.
|
||||
*/
|
||||
static void tolower(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
}
|
||||
/**
|
||||
* Convert all upper case characters in the string to lower case.
|
||||
* @param str the @a string to convert.
|
||||
*/
|
||||
static void tolower(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the next line(s) from the @a istring into fields.
|
||||
* @param ifs the @a istream to read from.
|
||||
* @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines.
|
||||
* @param lineNo the current line number (incremented with each line read).
|
||||
* @return true if there are more lines to read, false when there are no more lines left.
|
||||
*/
|
||||
static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) {
|
||||
row.clear();
|
||||
string line;
|
||||
bool quotedText = false, wasQuoted = false;
|
||||
ostringstream field;
|
||||
char prev = FIELD_SEPARATOR;
|
||||
bool empty = true, read = false;
|
||||
while (getline(ifs, line)) {
|
||||
read = true;
|
||||
lineNo++;
|
||||
trim(line);
|
||||
/**
|
||||
* Split the next line(s) from the @a istring into fields.
|
||||
* @param ifs the @a istream to read from.
|
||||
* @param row the @a vector to which to add the fields. This will be empty for completely empty and comment lines.
|
||||
* @param lineNo the current line number (incremented with each line read).
|
||||
* @return true if there are more lines to read, false when there are no more lines left.
|
||||
*/
|
||||
static bool splitFields(istream& ifs, vector<string>& row, unsigned int& lineNo) {
|
||||
row.clear();
|
||||
string line;
|
||||
bool quotedText = false, wasQuoted = false;
|
||||
ostringstream field;
|
||||
char prev = FIELD_SEPARATOR;
|
||||
bool empty = true, read = false;
|
||||
while (getline(ifs, line)) {
|
||||
read = true;
|
||||
lineNo++;
|
||||
trim(line);
|
||||
|
||||
size_t length = line.length();
|
||||
if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) {
|
||||
continue; // skip empty lines and comments
|
||||
}
|
||||
for (size_t pos = 0; pos < length; pos++) {
|
||||
char ch = line[pos];
|
||||
switch (ch) {
|
||||
case FIELD_SEPARATOR:
|
||||
if (quotedText) {
|
||||
field << ch;
|
||||
} else {
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
empty &= str.empty();
|
||||
row.push_back(str);
|
||||
field.str("");
|
||||
wasQuoted = false;
|
||||
}
|
||||
break;
|
||||
case TEXT_SEPARATOR:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote
|
||||
field << ch;
|
||||
quotedText = true;
|
||||
} else if (quotedText) {
|
||||
quotedText = false;
|
||||
} else if (prev == FIELD_SEPARATOR) {
|
||||
quotedText = wasQuoted = true;
|
||||
} else {
|
||||
field << ch;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) {
|
||||
field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text
|
||||
quotedText = true;
|
||||
} else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) {
|
||||
field << VALUE_SEPARATOR;
|
||||
}
|
||||
field << ch;
|
||||
break;
|
||||
}
|
||||
prev = ch;
|
||||
}
|
||||
if (!quotedText) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
if (empty && str.empty()) {
|
||||
row.clear();
|
||||
return read;
|
||||
}
|
||||
row.push_back(str);
|
||||
return true;
|
||||
}
|
||||
size_t length = line.length();
|
||||
if (!quotedText && (length == 0 || line[0] == '#' || (line.length() > 1 && line[0] == '/' && line[1] == '/'))) {
|
||||
continue; // skip empty lines and comments
|
||||
}
|
||||
for (size_t pos = 0; pos < length; pos++) {
|
||||
char ch = line[pos];
|
||||
switch (ch) {
|
||||
case FIELD_SEPARATOR:
|
||||
if (quotedText) {
|
||||
field << ch;
|
||||
} else {
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
empty &= str.empty();
|
||||
row.push_back(str);
|
||||
field.str("");
|
||||
wasQuoted = false;
|
||||
}
|
||||
break;
|
||||
case TEXT_SEPARATOR:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText) { // double dquote
|
||||
field << ch;
|
||||
quotedText = true;
|
||||
} else if (quotedText) {
|
||||
quotedText = false;
|
||||
} else if (prev == FIELD_SEPARATOR) {
|
||||
quotedText = wasQuoted = true;
|
||||
} else {
|
||||
field << ch;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
if (prev == TEXT_SEPARATOR && !quotedText && wasQuoted) {
|
||||
field << TEXT_SEPARATOR; // single dquote in the middle of formerly quoted text
|
||||
quotedText = true;
|
||||
} else if (quotedText && pos == 0 && field.tellp() > 0 && *(field.str().end()-1) != VALUE_SEPARATOR) {
|
||||
field << VALUE_SEPARATOR;
|
||||
}
|
||||
field << ch;
|
||||
break;
|
||||
}
|
||||
prev = ch;
|
||||
}
|
||||
if (!quotedText) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
string str = field.str();
|
||||
trim(str);
|
||||
if (empty && str.empty()) {
|
||||
row.clear();
|
||||
return read;
|
||||
}
|
||||
row.push_back(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract default values from the file name.
|
||||
* @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv".
|
||||
* @param dest the output destination address ZZ (hex digits).
|
||||
* @param ident the identification part IDENT (up to 5 characters, set to empty if not present).
|
||||
* @param circuit the circuit part CIRCUIT (set to IDENT if not present).
|
||||
* @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present).
|
||||
* @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present).
|
||||
* @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present).
|
||||
* @return true if at least the address and the identification part were extracted, false otherwise.
|
||||
*/
|
||||
static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit,
|
||||
string& suffix, unsigned int& software, unsigned int& hardware) {
|
||||
ident = circuit = suffix = "";
|
||||
software = hardware = UINT_MAX;
|
||||
if (name.length() > 4 && name.substr(name.length()-4) == ".csv") {
|
||||
name = name.substr(0, name.length()-3); // including trailing "."
|
||||
}
|
||||
size_t pos = name.find('.');
|
||||
if (pos != 2) {
|
||||
return false; // missing "ZZ."
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL);
|
||||
if (result != RESULT_OK || !isValidAddress(dest)) {
|
||||
return false; // invalid "ZZ"
|
||||
}
|
||||
name.erase(0, pos);
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".SW"); // check for ".SWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "SWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".HW"); // check for ".HWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "HWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.find('.', 1); // check for ".IDENT."
|
||||
if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".."
|
||||
ident = circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".CIRCUIT."
|
||||
if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) {
|
||||
circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".SUFFIX."
|
||||
}
|
||||
if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') {
|
||||
suffix = name.substr(0, 2);
|
||||
name.erase(0, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Extract default values from the file name.
|
||||
* @param name the file name (without path) in the form "ZZ[.IDENT][.CIRCUIT][.SUFFIX][.SWXXXX][.HWXXXX][.*].csv".
|
||||
* @param dest the output destination address ZZ (hex digits).
|
||||
* @param ident the identification part IDENT (up to 5 characters, set to empty if not present).
|
||||
* @param circuit the circuit part CIRCUIT (set to IDENT if not present).
|
||||
* @param suffix the suffix part SUFFIX including the leading dot (decimal digit, set to empty if not present).
|
||||
* @param software the software version part SWXXXX (BCD digits, set to @a UINT_MAX if not present).
|
||||
* @param hardware the hardware version part HWXXXX (BCD digits, set to @a UINT_MAX if not present).
|
||||
* @return true if at least the address and the identification part were extracted, false otherwise.
|
||||
*/
|
||||
static bool extractDefaultsFromFilename(string name, unsigned char& dest, string& ident, string& circuit,
|
||||
string& suffix, unsigned int& software, unsigned int& hardware) {
|
||||
ident = circuit = suffix = "";
|
||||
software = hardware = UINT_MAX;
|
||||
if (name.length() > 4 && name.substr(name.length()-4) == ".csv") {
|
||||
name = name.substr(0, name.length()-3); // including trailing "."
|
||||
}
|
||||
size_t pos = name.find('.');
|
||||
if (pos != 2) {
|
||||
return false; // missing "ZZ."
|
||||
}
|
||||
result_t result = RESULT_OK;
|
||||
dest = (unsigned char)parseInt(name.substr(0, pos).c_str(), 16, 0, 0xff, result, NULL);
|
||||
if (result != RESULT_OK || !isValidAddress(dest)) {
|
||||
return false; // invalid "ZZ"
|
||||
}
|
||||
name.erase(0, pos);
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".SW"); // check for ".SWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
software = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "SWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.rfind(".HW"); // check for ".HWxxxx."
|
||||
if (pos != string::npos && name.find(".", pos+1) == pos+7) {
|
||||
hardware = parseInt(name.substr(pos+3, 4).c_str(), 10, 0, 9999, result, NULL);
|
||||
if (result != RESULT_OK) {
|
||||
return false; // invalid "HWxxxx"
|
||||
}
|
||||
name.erase(pos, 7);
|
||||
}
|
||||
}
|
||||
if (name.length() > 1) {
|
||||
pos = name.find('.', 1); // check for ".IDENT."
|
||||
if (pos != string::npos && pos >= 1 && pos <= 6) { // up to 5 chars between two "."s, immediately after "ZZ.", or ".."
|
||||
ident = circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".CIRCUIT."
|
||||
if (pos != string::npos && (pos>2 || name[1]<'0' || name[1]>'9')) {
|
||||
circuit = name.substr(1, pos-1);
|
||||
name.erase(0, pos);
|
||||
pos = name.find('.', 1); // check for ".SUFFIX."
|
||||
}
|
||||
if (pos != string::npos && pos == 2 && name[1] >= '0' && name[1] <= '9') {
|
||||
suffix = name.substr(0, 2);
|
||||
name.erase(0, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/** whether this instance supports rows with defaults (starting with a star). */
|
||||
bool m_supportsDefaults;
|
||||
private:
|
||||
/** whether this instance supports rows with defaults (starting with a star). */
|
||||
bool m_supportsDefaults;
|
||||
|
||||
|
||||
protected:
|
||||
/** a @a string describing the last error position. */
|
||||
string m_lastError;
|
||||
protected:
|
||||
/** a @a string describing the last error position. */
|
||||
string m_lastError;
|
||||
};
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+1871
-1871
File diff suppressed because it is too large
Load Diff
+1080
-1080
File diff suppressed because it is too large
Load Diff
+33
-33
@@ -22,40 +22,40 @@
|
||||
namespace ebusd {
|
||||
|
||||
const char* getResultCode(result_t resultCode) {
|
||||
switch (resultCode) {
|
||||
case RESULT_OK: return "done";
|
||||
case RESULT_CONTINUE: return "continue";
|
||||
case RESULT_EMPTY: return "empty";
|
||||
case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error";
|
||||
case RESULT_ERR_DEVICE: return "ERR: generic device error";
|
||||
case RESULT_ERR_SEND: return "ERR: send error";
|
||||
case RESULT_ERR_ESC: return "ERR: invalid escape sequence";
|
||||
case RESULT_ERR_TIMEOUT: return "ERR: read timeout";
|
||||
case RESULT_ERR_NOTFOUND: return "ERR: element not found";
|
||||
case RESULT_ERR_EOF: return "ERR: end of input reached";
|
||||
case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument";
|
||||
case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument";
|
||||
case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address";
|
||||
case RESULT_ERR_INVALID_POS: return "ERR: invalid position";
|
||||
case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range";
|
||||
case RESULT_ERR_INVALID_PART: return "ERR: invalid part type";
|
||||
case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type";
|
||||
case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list";
|
||||
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_CRC: return "ERR: CRC error";
|
||||
case RESULT_ERR_ACK: return "ERR: ACK error";
|
||||
case RESULT_ERR_NAK: return "ERR: NAK received";
|
||||
case RESULT_ERR_NO_SIGNAL: return "ERR: no signal";
|
||||
case RESULT_ERR_SYN: return "ERR: SYN received";
|
||||
switch (resultCode) {
|
||||
case RESULT_OK: return "done";
|
||||
case RESULT_CONTINUE: return "continue";
|
||||
case RESULT_EMPTY: return "empty";
|
||||
case RESULT_ERR_GENERIC_IO: return "ERR: generic I/O error";
|
||||
case RESULT_ERR_DEVICE: return "ERR: generic device error";
|
||||
case RESULT_ERR_SEND: return "ERR: send error";
|
||||
case RESULT_ERR_ESC: return "ERR: invalid escape sequence";
|
||||
case RESULT_ERR_TIMEOUT: return "ERR: read timeout";
|
||||
case RESULT_ERR_NOTFOUND: return "ERR: element not found";
|
||||
case RESULT_ERR_EOF: return "ERR: end of input reached";
|
||||
case RESULT_ERR_INVALID_ARG: return "ERR: invalid argument";
|
||||
case RESULT_ERR_INVALID_NUM: return "ERR: invalid numeric argument";
|
||||
case RESULT_ERR_INVALID_ADDR: return "ERR: invalid address";
|
||||
case RESULT_ERR_INVALID_POS: return "ERR: invalid position";
|
||||
case RESULT_ERR_OUT_OF_RANGE: return "ERR: argument value out of valid range";
|
||||
case RESULT_ERR_INVALID_PART: return "ERR: invalid part type";
|
||||
case RESULT_ERR_MISSING_TYPE: return "ERR: missing data type";
|
||||
case RESULT_ERR_INVALID_LIST: return "ERR: invalid value list";
|
||||
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_CRC: return "ERR: CRC error";
|
||||
case RESULT_ERR_ACK: return "ERR: ACK error";
|
||||
case RESULT_ERR_NAK: return "ERR: NAK received";
|
||||
case RESULT_ERR_NO_SIGNAL: return "ERR: no signal";
|
||||
case RESULT_ERR_SYN: return "ERR: SYN received";
|
||||
|
||||
default:
|
||||
if (resultCode >= 0) {
|
||||
return "done: unknown result code";
|
||||
}
|
||||
return "ERR: unknown result code";
|
||||
}
|
||||
default:
|
||||
if (resultCode >= 0) {
|
||||
return "done: unknown result code";
|
||||
}
|
||||
return "ERR: unknown result code";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+26
-26
@@ -31,37 +31,37 @@ namespace ebusd {
|
||||
|
||||
/** type for result code. */
|
||||
enum result_t {
|
||||
RESULT_OK = 0, //!< success
|
||||
RESULT_OK = 0, //!< success
|
||||
|
||||
RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received)
|
||||
RESULT_EMPTY = 2, //!< empty result
|
||||
RESULT_CONTINUE = 1, //!< more input data is needed (e.g. start of escape sequence received)
|
||||
RESULT_EMPTY = 2, //!< empty result
|
||||
|
||||
RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal)
|
||||
RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal)
|
||||
RESULT_ERR_SEND = -3, //!< send error
|
||||
RESULT_ERR_ESC = -4, //!< invalid escape sequence
|
||||
RESULT_ERR_TIMEOUT = -5, //!< read timeout
|
||||
RESULT_ERR_GENERIC_IO = -1, //!< generic I/O error (usually fatal)
|
||||
RESULT_ERR_DEVICE = -2, //!< generic device error (usually fatal)
|
||||
RESULT_ERR_SEND = -3, //!< send error
|
||||
RESULT_ERR_ESC = -4, //!< invalid escape sequence
|
||||
RESULT_ERR_TIMEOUT = -5, //!< read timeout
|
||||
|
||||
RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable
|
||||
RESULT_ERR_EOF = -7, //!< end of input reached
|
||||
RESULT_ERR_INVALID_ARG = -8, //!< invalid argument
|
||||
RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument
|
||||
RESULT_ERR_INVALID_ADDR = -10, //!< invalid address
|
||||
RESULT_ERR_INVALID_POS = -11, //!< invalid position
|
||||
RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range
|
||||
RESULT_ERR_INVALID_PART = -13, //!< invalid part type value
|
||||
RESULT_ERR_MISSING_TYPE = -14, //!< missing data type
|
||||
RESULT_ERR_INVALID_LIST = -15, //!< invalid value list
|
||||
RESULT_ERR_DUPLICATE = -16, //!< duplicate entry
|
||||
RESULT_ERR_DUPLICATE_NAME = -17, //!< duplicate entry (name)
|
||||
RESULT_ERR_NOTFOUND = -6, //!< file/element not found or not readable
|
||||
RESULT_ERR_EOF = -7, //!< end of input reached
|
||||
RESULT_ERR_INVALID_ARG = -8, //!< invalid argument
|
||||
RESULT_ERR_INVALID_NUM = -9, //!< invalid numeric argument
|
||||
RESULT_ERR_INVALID_ADDR = -10, //!< invalid address
|
||||
RESULT_ERR_INVALID_POS = -11, //!< invalid position
|
||||
RESULT_ERR_OUT_OF_RANGE = -12, //!< argument value out of valid range
|
||||
RESULT_ERR_INVALID_PART = -13, //!< invalid part type value
|
||||
RESULT_ERR_MISSING_TYPE = -14, //!< missing data type
|
||||
RESULT_ERR_INVALID_LIST = -15, //!< invalid value list
|
||||
RESULT_ERR_DUPLICATE = -16, //!< duplicate entry
|
||||
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_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_NO_SIGNAL = -22, //!< no signal found on the bus
|
||||
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
|
||||
RESULT_ERR_NO_SIGNAL = -22, //!< no signal found on the bus
|
||||
RESULT_ERR_SYN = -23, //!< SYN received instead of answer
|
||||
};
|
||||
|
||||
|
||||
|
||||
+178
-178
@@ -35,160 +35,160 @@ using std::setfill;
|
||||
* CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1.
|
||||
*/
|
||||
static const unsigned char CRC_LOOKUP_TABLE[] = {
|
||||
0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
|
||||
0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
|
||||
0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
|
||||
0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
|
||||
0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
|
||||
0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
|
||||
0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
|
||||
0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
|
||||
0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
|
||||
0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
|
||||
0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
|
||||
0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
|
||||
0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
|
||||
0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
|
||||
0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
|
||||
0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
|
||||
0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
|
||||
0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
|
||||
0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
|
||||
0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
|
||||
0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
|
||||
0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
|
||||
0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
|
||||
0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
|
||||
0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
|
||||
0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
|
||||
0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
|
||||
0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
|
||||
0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
|
||||
0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
|
||||
0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
|
||||
0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
|
||||
};
|
||||
|
||||
|
||||
void SymbolString::addAll(const SymbolString& str, bool skipLastSymbol) {
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
bool isEscaped = str.m_unescapeState == 0;
|
||||
vector<unsigned char> data = str.m_data;
|
||||
size_t end = data.size();
|
||||
if (end > 0 && skipLastSymbol) {
|
||||
end--;
|
||||
}
|
||||
for (size_t i = 0; i < end; i++) {
|
||||
push_back(data[i], isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
bool isEscaped = str.m_unescapeState == 0;
|
||||
vector<unsigned char> data = str.m_data;
|
||||
size_t end = data.size();
|
||||
if (end > 0 && skipLastSymbol) {
|
||||
end--;
|
||||
}
|
||||
for (size_t i = 0; i < end; i++) {
|
||||
push_back(data[i], isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
}
|
||||
|
||||
result_t SymbolString::parseHex(const string& str, const bool isEscaped) {
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
for (size_t i = 0; i < str.size(); i += 2) {
|
||||
char* strEnd = NULL;
|
||||
const char* strBegin = str.substr(i, 2).c_str();
|
||||
unsigned long value = strtoul(strBegin, &strEnd, 16);
|
||||
bool addCrc = m_unescapeState == 0;
|
||||
for (size_t i = 0; i < str.size(); i += 2) {
|
||||
char* strEnd = NULL;
|
||||
const char* strBegin = str.substr(i, 2).c_str();
|
||||
unsigned long value = strtoul(strBegin, &strEnd, 16);
|
||||
|
||||
if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
push_back((unsigned char)value, isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
return RESULT_OK;
|
||||
if (strEnd == NULL || strEnd != strBegin+2 || value > 0xff) {
|
||||
return RESULT_ERR_INVALID_NUM; // invalid value
|
||||
}
|
||||
push_back((unsigned char)value, isEscaped, addCrc);
|
||||
}
|
||||
if (addCrc) {
|
||||
push_back(m_crc, false, false); // add CRC
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
const string SymbolString::getDataStr(const bool unescape, const bool skipLastSymbol) {
|
||||
ostringstream sstr;
|
||||
bool previousEscape = false;
|
||||
ostringstream sstr;
|
||||
bool previousEscape = false;
|
||||
|
||||
for (size_t i = 0; i < m_data.size(); i++) {
|
||||
unsigned char value = m_data[i];
|
||||
if (m_unescapeState == 0 && unescape && previousEscape) {
|
||||
if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
if (value == 0x00) {
|
||||
sstr << "a9"; // ESC
|
||||
} else if (value == 0x01) {
|
||||
sstr << "aa"; // SYN
|
||||
} else {
|
||||
sstr << "XX"; // invalid escape sequence
|
||||
}
|
||||
}
|
||||
previousEscape = false;
|
||||
} else if (m_unescapeState == 0 && unescape && value == ESC) {
|
||||
previousEscape = true; // escape sequence not yet finished
|
||||
} else if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
sstr << nouppercase << setw(2) << hex
|
||||
<< setfill('0') << static_cast<unsigned>(value);
|
||||
}
|
||||
}
|
||||
return sstr.str();
|
||||
for (size_t i = 0; i < m_data.size(); i++) {
|
||||
unsigned char value = m_data[i];
|
||||
if (m_unescapeState == 0 && unescape && previousEscape) {
|
||||
if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
if (value == 0x00) {
|
||||
sstr << "a9"; // ESC
|
||||
} else if (value == 0x01) {
|
||||
sstr << "aa"; // SYN
|
||||
} else {
|
||||
sstr << "XX"; // invalid escape sequence
|
||||
}
|
||||
}
|
||||
previousEscape = false;
|
||||
} else if (m_unescapeState == 0 && unescape && value == ESC) {
|
||||
previousEscape = true; // escape sequence not yet finished
|
||||
} else if (!skipLastSymbol || i+1 < m_data.size()) {
|
||||
sstr << nouppercase << setw(2) << hex
|
||||
<< setfill('0') << static_cast<unsigned>(value);
|
||||
}
|
||||
}
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
result_t SymbolString::push_back(const unsigned char value, const bool isEscaped, const bool updateCRC) {
|
||||
if (m_unescapeState == 0) { // store escaped data
|
||||
if (!isEscaped && value == ESC) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x00);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
}
|
||||
} else if (!isEscaped && value == SYN) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x01);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
}
|
||||
} else {
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (!isEscaped) {
|
||||
if (m_unescapeState != 1) {
|
||||
return RESULT_ERR_ESC; // invalid unescape state
|
||||
}
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
if (value == ESC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
} else if (value == SYN) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
} else {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (m_unescapeState != 1) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
if (value == 0x00) {
|
||||
m_data.push_back(ESC);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (value == 0x01) {
|
||||
m_data.push_back(SYN);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_ESC; // invalid escape sequence
|
||||
}
|
||||
if (value == ESC) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_unescapeState = 2;
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_data.push_back(value);
|
||||
return RESULT_OK;
|
||||
if (m_unescapeState == 0) { // store escaped data
|
||||
if (!isEscaped && value == ESC) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x00);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
}
|
||||
} else if (!isEscaped && value == SYN) {
|
||||
m_data.push_back(ESC);
|
||||
m_data.push_back(0x01);
|
||||
if (updateCRC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
}
|
||||
} else {
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (!isEscaped) {
|
||||
if (m_unescapeState != 1) {
|
||||
return RESULT_ERR_ESC; // invalid unescape state
|
||||
}
|
||||
m_data.push_back(value);
|
||||
if (updateCRC) {
|
||||
if (value == ESC) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x00);
|
||||
} else if (value == SYN) {
|
||||
addCRC(ESC);
|
||||
addCRC(0x01);
|
||||
} else {
|
||||
addCRC(value);
|
||||
}
|
||||
}
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (m_unescapeState != 1) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
if (value == 0x00) {
|
||||
m_data.push_back(ESC);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
if (value == 0x01) {
|
||||
m_data.push_back(SYN);
|
||||
m_unescapeState = 1;
|
||||
return RESULT_OK;
|
||||
}
|
||||
return RESULT_ERR_ESC; // invalid escape sequence
|
||||
}
|
||||
if (value == ESC) {
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_unescapeState = 2;
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
if (updateCRC) {
|
||||
addCRC(value);
|
||||
}
|
||||
m_data.push_back(value);
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void SymbolString::addCRC(const unsigned char value) {
|
||||
m_crc = CRC_LOOKUP_TABLE[m_crc]^value;
|
||||
m_crc = CRC_LOOKUP_TABLE[m_crc]^value;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,66 +199,66 @@ void SymbolString::addCRC(const unsigned char value) {
|
||||
* @return the 1-based index of the upper or lower 4 bits of a master address (1 to 5), or 0.
|
||||
*/
|
||||
unsigned char getMasterPartIndex(unsigned char bits) {
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return 1;
|
||||
case 0x1:
|
||||
return 2;
|
||||
case 0x3:
|
||||
return 3;
|
||||
case 0x7:
|
||||
return 4;
|
||||
case 0xF:
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return 1;
|
||||
case 0x1:
|
||||
return 2;
|
||||
case 0x3:
|
||||
return 3;
|
||||
case 0x7:
|
||||
return 4;
|
||||
case 0xF:
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMaster(unsigned char addr) {
|
||||
return getMasterPartIndex(addr & 0x0F) > 0
|
||||
&& getMasterPartIndex((addr & 0xF0)>>4) > 0;
|
||||
return getMasterPartIndex(addr & 0x0F) > 0
|
||||
&& getMasterPartIndex((addr & 0xF0)>>4) > 0;
|
||||
}
|
||||
|
||||
bool isSlaveMaster(unsigned char addr) {
|
||||
return isMaster((unsigned char)(addr+256-5));
|
||||
return isMaster((unsigned char)(addr+256-5));
|
||||
}
|
||||
|
||||
unsigned char getSlaveAddress(unsigned char addr) {
|
||||
if (isMaster(addr)) {
|
||||
return (unsigned char)(addr+5);
|
||||
}
|
||||
if (isValidAddress(addr, false)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
if (isMaster(addr)) {
|
||||
return (unsigned char)(addr+5);
|
||||
}
|
||||
if (isValidAddress(addr, false)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
}
|
||||
|
||||
unsigned char getMasterAddress(unsigned char addr) {
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
addr = (unsigned char)(addr+256-5);
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
addr = (unsigned char)(addr+256-5);
|
||||
if (isMaster(addr)) {
|
||||
return addr;
|
||||
}
|
||||
return SYN;
|
||||
}
|
||||
|
||||
unsigned char getMasterNumber(unsigned char addr) {
|
||||
unsigned char priority = getMasterPartIndex(addr & 0x0F);
|
||||
if (priority == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4);
|
||||
if (index == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (unsigned char)(5*(priority-1) + index);
|
||||
unsigned char priority = getMasterPartIndex(addr & 0x0F);
|
||||
if (priority == 0) {
|
||||
return 0;
|
||||
}
|
||||
unsigned char index = getMasterPartIndex((addr & 0xF0) >> 4);
|
||||
if (index == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (unsigned char)(5*(priority-1) + index);
|
||||
}
|
||||
|
||||
bool isValidAddress(unsigned char addr, bool allowBroadcast) {
|
||||
return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST);
|
||||
return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST);
|
||||
}
|
||||
|
||||
} // namespace ebusd
|
||||
|
||||
+117
-117
@@ -79,141 +79,141 @@ static const unsigned char BROADCAST = 0xFE; //!< the broadcast destination addr
|
||||
* A string of escaped or unescaped bus symbols.
|
||||
*/
|
||||
class SymbolString {
|
||||
public:
|
||||
/**
|
||||
* Creates a new empty escaped or unescaped instance.
|
||||
* @param escaped whether to create an escaped instance.
|
||||
*/
|
||||
explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {}
|
||||
public:
|
||||
/**
|
||||
* Creates a new empty escaped or unescaped instance.
|
||||
* @param escaped whether to create an escaped instance.
|
||||
*/
|
||||
explicit SymbolString(const bool escaped = true) : m_unescapeState(escaped ? 0 : 1), m_crc(0) {}
|
||||
|
||||
/**
|
||||
* Add all symbols from the other @a SymbolString and the calculated CRC if escaped.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
*/
|
||||
void addAll(const SymbolString& str, const bool skipLastSymbol = false);
|
||||
/**
|
||||
* Add all symbols from the other @a SymbolString and the calculated CRC if escaped.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
*/
|
||||
void addAll(const SymbolString& str, const bool skipLastSymbol = false);
|
||||
|
||||
/**
|
||||
* Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped.
|
||||
* @param str the hex @a string.
|
||||
* @param isEscaped whether the hex string is escaped.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
result_t parseHex(const string& str, const bool isEscaped = false);
|
||||
/**
|
||||
* Parse the escaped or unescaped hex @a string, add all symbols, and add the calculated CRC if escaped.
|
||||
* @param str the hex @a string.
|
||||
* @param isEscaped whether the hex string is escaped.
|
||||
* @return @a RESULT_OK on success, or an error code.
|
||||
*/
|
||||
result_t parseHex(const string& str, const bool isEscaped = false);
|
||||
|
||||
/**
|
||||
* Return the symbols as hex string.
|
||||
* @param unescape whether to unescape an escaped instance.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
* @return the symbols as hex string.
|
||||
*/
|
||||
const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true);
|
||||
/**
|
||||
* Return the symbols as hex string.
|
||||
* @param unescape whether to unescape an escaped instance.
|
||||
* @param skipLastSymbol whether to skip the last symbol (probably the CRC).
|
||||
* @return the symbols as hex string.
|
||||
*/
|
||||
const string getDataStr(const bool unescape = true, const bool skipLastSymbol = true);
|
||||
|
||||
/**
|
||||
* Return a reference to the symbol at the specified index.
|
||||
* @param index the index of the symbol to return.
|
||||
* @return the reference to the symbol at the specified index.
|
||||
*/
|
||||
unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; }
|
||||
/**
|
||||
* Return a reference to the symbol at the specified index.
|
||||
* @param index the index of the symbol to return.
|
||||
* @return the reference to the symbol at the specified index.
|
||||
*/
|
||||
unsigned char& operator[](const size_t index) { if (index >= m_data.size()) { m_data.resize(index+1, 0); } return m_data[index]; }
|
||||
|
||||
/**
|
||||
* Return whether this instance is equal to the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols).
|
||||
*/
|
||||
bool operator == (SymbolString& other) { return m_unescapeState == other.m_unescapeState && m_data == other.m_data; }
|
||||
/**
|
||||
* Return whether this instance is equal to the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols).
|
||||
*/
|
||||
bool operator == (SymbolString& other) { return m_unescapeState == other.m_unescapeState && m_data == other.m_data; }
|
||||
|
||||
/**
|
||||
* Return whether this instance is different from the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is different from the other instance.
|
||||
*/
|
||||
bool operator != (SymbolString& other) { return m_unescapeState != other.m_unescapeState || m_data != other.m_data; }
|
||||
/**
|
||||
* Return whether this instance is different from the other instance.
|
||||
* @param other the other instance.
|
||||
* @return true if this instance is different from the other instance.
|
||||
*/
|
||||
bool operator != (SymbolString& other) { return m_unescapeState != other.m_unescapeState || m_data != other.m_data; }
|
||||
|
||||
/**
|
||||
* Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC).
|
||||
* @param other the other instance.
|
||||
* @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols),
|
||||
* 1 if this instance is completely different to the other instance,
|
||||
* 2 if this instance only differs from the other instance in the first byte (the master address).
|
||||
*/
|
||||
int compareMaster(SymbolString& other) {
|
||||
if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) {
|
||||
return 1;
|
||||
}
|
||||
if (m_data == other.m_data) {
|
||||
return 0;
|
||||
}
|
||||
if (m_data.size() == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* Compares this instance to the other instance while treating both as master data (i.e. starting with the master address and ending with the CRC).
|
||||
* @param other the other instance.
|
||||
* @return 0 if this instance is equal to the other instance (i.e. both escaped or both unescaped and same symbols),
|
||||
* 1 if this instance is completely different to the other instance,
|
||||
* 2 if this instance only differs from the other instance in the first byte (the master address).
|
||||
*/
|
||||
int compareMaster(SymbolString& other) {
|
||||
if (m_unescapeState != other.m_unescapeState || m_data.size() != other.m_data.size()) {
|
||||
return 1;
|
||||
}
|
||||
if (m_data == other.m_data) {
|
||||
return 0;
|
||||
}
|
||||
if (m_data.size() == 1) {
|
||||
return 2;
|
||||
}
|
||||
if (equal(m_data.begin()+1, m_data.end()-1, other.m_data.begin()+1)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary.
|
||||
* @param value the symbol to append.
|
||||
* @param isEscaped whether the symbol is escaped.
|
||||
* @param updateCRC whether to update the calculated CRC in @a m_crc.
|
||||
* @return RESULT_OK if another symbol was appended,
|
||||
* RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received,
|
||||
* RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected.
|
||||
*/
|
||||
result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true);
|
||||
/**
|
||||
* Appends a the symbol to the end of the symbol string and escapes/unescapes it if necessary.
|
||||
* @param value the symbol to append.
|
||||
* @param isEscaped whether the symbol is escaped.
|
||||
* @param updateCRC whether to update the calculated CRC in @a m_crc.
|
||||
* @return RESULT_OK if another symbol was appended,
|
||||
* RESULT_IN_ESC if this is an unescaped instance and the symbol is escaped and the start of the escape sequence was received,
|
||||
* RESULT_ERR_ESC if this is an unescaped instance and an invalid escaped sequence was detected.
|
||||
*/
|
||||
result_t push_back(const unsigned char value, const bool isEscaped = true, const bool updateCRC = true);
|
||||
|
||||
/**
|
||||
* Return the number of symbols in this symbol string.
|
||||
* @return the number of available symbols.
|
||||
*/
|
||||
unsigned char size() const { return (unsigned char)m_data.size(); }
|
||||
/**
|
||||
* Return the number of symbols in this symbol string.
|
||||
* @return the number of available symbols.
|
||||
*/
|
||||
unsigned char size() const { return (unsigned char)m_data.size(); }
|
||||
|
||||
/**
|
||||
* Return the calculated CRC.
|
||||
* @return the calculated CRC.
|
||||
*/
|
||||
unsigned char getCRC() const { return m_crc; }
|
||||
/**
|
||||
* Return the calculated CRC.
|
||||
* @return the calculated CRC.
|
||||
*/
|
||||
unsigned char getCRC() const { return m_crc; }
|
||||
|
||||
/**
|
||||
* Clear the symbols.
|
||||
*/
|
||||
void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; }
|
||||
/**
|
||||
* Clear the symbols.
|
||||
*/
|
||||
void clear() { m_data.clear(); m_unescapeState = m_unescapeState == 0 ? 0 : 1; m_crc = 0; }
|
||||
|
||||
/**
|
||||
* Clear the symbols and adjust the escape mode.
|
||||
* @param escape true to set to an escaped instance, false to set to an unescaped instance.
|
||||
*/
|
||||
void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; }
|
||||
/**
|
||||
* Clear the symbols and adjust the escape mode.
|
||||
* @param escape true to set to an escaped instance, false to set to an unescaped instance.
|
||||
*/
|
||||
void clear(const bool escape) { m_data.clear(); m_unescapeState = escape ? 0 : 1; m_crc = 0; }
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
*/
|
||||
SymbolString(const SymbolString& str)
|
||||
: m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {}
|
||||
private:
|
||||
/**
|
||||
* Hidden copy constructor.
|
||||
* @param str the @a SymbolString to copy from.
|
||||
*/
|
||||
SymbolString(const SymbolString& str)
|
||||
: m_data(str.m_data), m_unescapeState(str.m_unescapeState), m_crc(str.m_crc) {}
|
||||
|
||||
/**
|
||||
* Update the calculated CRC in @a m_crc by adding a value.
|
||||
* @param value the (escaped) value to add to the calculated CRC in @a m_crc.
|
||||
*/
|
||||
void addCRC(const unsigned char value);
|
||||
/**
|
||||
* Update the calculated CRC in @a m_crc by adding a value.
|
||||
* @param value the (escaped) value to add to the calculated CRC in @a m_crc.
|
||||
*/
|
||||
void addCRC(const unsigned char value);
|
||||
|
||||
/** the string of bus symbols. */
|
||||
vector<unsigned char> m_data;
|
||||
/** the string of bus symbols. */
|
||||
vector<unsigned char> m_data;
|
||||
|
||||
/**
|
||||
* 0 if the symbols in @a m_data are escaped,
|
||||
* 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol,
|
||||
* 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol.
|
||||
*/
|
||||
int m_unescapeState;
|
||||
/**
|
||||
* 0 if the symbols in @a m_data are escaped,
|
||||
* 1 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was a normal symbol,
|
||||
* 2 if the symbols in @a m_data are unescaped and the last symbol passed to @a push_back was the escape symbol.
|
||||
*/
|
||||
int m_unescapeState;
|
||||
|
||||
/** the calculated CRC. */
|
||||
unsigned char m_crc;
|
||||
/** the calculated CRC. */
|
||||
unsigned char m_crc;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+581
-581
File diff suppressed because it is too large
Load Diff
@@ -24,39 +24,39 @@ using namespace std;
|
||||
using namespace ebusd;
|
||||
|
||||
int main() {
|
||||
Device* device = Device::create("/dev/ttyUSB20", true, false, false);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device" << endl;
|
||||
return -1;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != RESULT_OK) {
|
||||
cout << "open failed: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
if (!device->isValid()) {
|
||||
cout << "device not available." << endl;
|
||||
}
|
||||
int count = 0;
|
||||
Device* device = Device::create("/dev/ttyUSB20", true, false, false);
|
||||
if (device == NULL) {
|
||||
cout << "unable to create device" << endl;
|
||||
return -1;
|
||||
}
|
||||
result_t result = device->open();
|
||||
if (result != RESULT_OK) {
|
||||
cout << "open failed: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
if (!device->isValid()) {
|
||||
cout << "device not available." << endl;
|
||||
}
|
||||
int count = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned char byte = 0;
|
||||
result = device->recv(0, byte);
|
||||
while (1) {
|
||||
unsigned char byte = 0;
|
||||
result = device->recv(0, byte);
|
||||
|
||||
if (result == RESULT_OK) {
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (result == RESULT_OK) {
|
||||
cout << hex << setw(2) << setfill('0')
|
||||
<< static_cast<unsigned>(byte) << endl;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
device->close();
|
||||
device->close();
|
||||
|
||||
if (!device->isValid()) {
|
||||
cout << "close successful." << endl;
|
||||
}
|
||||
}
|
||||
if (!device->isValid()) {
|
||||
cout << "close successful." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
delete device;
|
||||
delete device;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,70 +28,70 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
istringstream ifs(
|
||||
"line 1 col 1,line 1 col 2,line 1 col 3\n"
|
||||
"line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n"
|
||||
"line 4 col 1,\"line 4 col 2 part 1\n"
|
||||
"line 4 col 2 part 2\",line 4 col 3\n"
|
||||
",,,\n"
|
||||
"line 6 col 1,,line 6 col 3\n"
|
||||
"line 8 col 1,\"line 8 col 2 part 1;\n"
|
||||
"line 8 col 2 part 2\",line 8 col 3\n"
|
||||
);
|
||||
string resultlines[][3] = {
|
||||
{"line 1 col 1", "line 1 col 2", "line 1 col 3"},
|
||||
{"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""},
|
||||
{"", "", ""},
|
||||
{"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"},
|
||||
{"", "", ""},
|
||||
{"line 6 col 1", "", "line 6 col 3"},
|
||||
{"", "", ""},
|
||||
{"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"},
|
||||
};
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
istringstream ifs(
|
||||
"line 1 col 1,line 1 col 2,line 1 col 3\n"
|
||||
"line 2 col 1,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\"\n"
|
||||
"line 4 col 1,\"line 4 col 2 part 1\n"
|
||||
"line 4 col 2 part 2\",line 4 col 3\n"
|
||||
",,,\n"
|
||||
"line 6 col 1,,line 6 col 3\n"
|
||||
"line 8 col 1,\"line 8 col 2 part 1;\n"
|
||||
"line 8 col 2 part 2\",line 8 col 3\n"
|
||||
);
|
||||
string resultlines[][3] = {
|
||||
{"line 1 col 1", "line 1 col 2", "line 1 col 3"},
|
||||
{"line 2 col 1", "line 2 col 2", "line 2 \"col 3\""},
|
||||
{"", "", ""},
|
||||
{"line 4 col 1", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3"},
|
||||
{"", "", ""},
|
||||
{"line 6 col 1", "", "line 6 col 3"},
|
||||
{"", "", ""},
|
||||
{"line 8 col 1", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3"},
|
||||
};
|
||||
unsigned int lineNo = 0;
|
||||
vector<string> row;
|
||||
|
||||
while (FileReader::splitFields(ifs, row, lineNo)) {
|
||||
cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl;
|
||||
string resultline[3] = resultlines[lineNo-1];
|
||||
if (row.empty()) {
|
||||
cout << " result empty";
|
||||
if (resultline[0] == "") {
|
||||
cout << ": OK" << endl;
|
||||
} else {
|
||||
cout << ": error" << endl;
|
||||
error = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (vector<string>::iterator it = row.begin(); it != row.end(); it++) {
|
||||
string got = *it;
|
||||
string expect = resultline[distance(row.begin(), it)];
|
||||
ostringstream type;
|
||||
type << "line " << static_cast<unsigned>(lineNo) << " col " << static_cast<size_t>(distance(row.begin(), it)+1);
|
||||
verify(false, type.str(), expect, got == expect, expect, got);
|
||||
}
|
||||
}
|
||||
while (FileReader::splitFields(ifs, row, lineNo)) {
|
||||
cout << "line " << static_cast<unsigned>(lineNo) << ": split OK" << endl;
|
||||
string resultline[3] = resultlines[lineNo-1];
|
||||
if (row.empty()) {
|
||||
cout << " result empty";
|
||||
if (resultline[0] == "") {
|
||||
cout << ": OK" << endl;
|
||||
} else {
|
||||
cout << ": error" << endl;
|
||||
error = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (vector<string>::iterator it = row.begin(); it != row.end(); it++) {
|
||||
string got = *it;
|
||||
string expect = resultline[distance(row.begin(), it)];
|
||||
ostringstream type;
|
||||
type << "line " << static_cast<unsigned>(lineNo) << " col " << static_cast<size_t>(distance(row.begin(), it)+1);
|
||||
verify(false, type.str(), expect, got == expect, expect, got);
|
||||
}
|
||||
}
|
||||
|
||||
return error ? 1 : 0;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
+377
-377
@@ -27,397 +27,397 @@
|
||||
using namespace ebusd;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
DataFieldTemplates* templates = NULL;
|
||||
|
||||
namespace ebusd {
|
||||
DataFieldTemplates* getTemplates(const string filename) {
|
||||
if (filename == "") { // avoid compiler warning
|
||||
return templates;
|
||||
}
|
||||
return templates;
|
||||
if (filename == "") { // avoid compiler warning
|
||||
return templates;
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
|
||||
// field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// template: name,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values
|
||||
string checks[][5] = {
|
||||
// "message", "decoded", "master", "slave", "flags"
|
||||
{"date,HDA:3,,,Datum", "", "", "", "template"},
|
||||
{"time,VTI,,,", "", "", "", "template"},
|
||||
{"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"},
|
||||
{"temp,D2C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp1,D1C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp2,D2B,,°C,Temperatur", "", "", "", "template"},
|
||||
{"power,UCH,,kW", "", "", "", "template"},
|
||||
{"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"},
|
||||
{"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"},
|
||||
{"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"},
|
||||
{"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"},
|
||||
{"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"},
|
||||
{"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
|
||||
{"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"},
|
||||
{"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"},
|
||||
{"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"},
|
||||
{"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""},
|
||||
{"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"},
|
||||
{"w,,first,,,15,b509", "", "ff15b50900", "00", "m"},
|
||||
{"w,,,,,,b505,2d", "", "", "", "defaults"},
|
||||
{"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"},
|
||||
{"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"},
|
||||
{"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"},
|
||||
{"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"},
|
||||
{"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"},
|
||||
{"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"},
|
||||
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"},
|
||||
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"},
|
||||
{"u,ehp,ActualEnvironmentPower,Energiebezug,,08,B509,29BA00,,s,IGN:2,,,,,s,power", "8", "1008b5090329ba00", "03ba0008", "pm"},
|
||||
{"uw,ehp,test,Test,,08,B5de,ab,,,power,,,,,s,hex:1", "8;39", "1008b5de02ab08", "0139", "pm"},
|
||||
{"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"},
|
||||
{"", "55.50;ok", "1025b50903290000", "050000780300", "d"},
|
||||
{"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" },
|
||||
{"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
|
||||
{"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
|
||||
{"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" },
|
||||
{"r,ehp,,,,08,b509,0d", "", "", "", "defaults" },
|
||||
{"w,ehp,,,,08,b509,0e", "", "", "", "defaults" },
|
||||
{"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" },
|
||||
{"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" },
|
||||
{"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" },
|
||||
{"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" },
|
||||
{"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" },
|
||||
{"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" },
|
||||
{"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" },
|
||||
{"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" },
|
||||
{"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" },
|
||||
{"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" },
|
||||
{"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" },
|
||||
{"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"},
|
||||
};
|
||||
templates = new DataFieldTemplates();
|
||||
MessageMap* messages = new MessageMap();
|
||||
vector< vector<string> > defaultsRows;
|
||||
map<string, Condition*> &conditions = messages->getConditions();
|
||||
Message* message = NULL;
|
||||
vector<Message*> deleteMessages;
|
||||
vector<SymbolString*> mstrs;
|
||||
vector<SymbolString*> sstrs;
|
||||
mstrs.resize(1);
|
||||
sstrs.resize(1);
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
string inputStr = check[1];
|
||||
string flags = check[4];
|
||||
bool isTemplate = flags == "template";
|
||||
bool isCondition = flags == "condition";
|
||||
bool isDefaults = isCondition || flags == "defaults";
|
||||
bool dontMap = flags.find('m') != string::npos;
|
||||
bool onlyMap = flags.find('M') != string::npos;
|
||||
bool failedCreate = flags.find('c') != string::npos;
|
||||
bool isChain = flags.find('C') != string::npos;
|
||||
bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos;
|
||||
bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos;
|
||||
bool withMessageDump = flags.find('N') != string::npos;
|
||||
bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos);
|
||||
bool failedPrepare = flags.find('p') != string::npos;
|
||||
bool failedPrepareMatch = flags.find('P') != string::npos;
|
||||
bool multi = flags.find('*') != string::npos;
|
||||
bool withInput = flags.find('i') != string::npos;
|
||||
result_t result = RESULT_EMPTY;
|
||||
vector<string> entries;
|
||||
istringstream ifs(check[0]);
|
||||
unsigned int lineNo = 0;
|
||||
if (!FileReader::splitFields(ifs, entries, lineNo)) {
|
||||
entries.clear();
|
||||
}
|
||||
if (isTemplate) {
|
||||
// store new template
|
||||
DataField* fields = NULL;
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, false, true, false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": create template OK" << endl;
|
||||
result = templates->add(fields, "", true);
|
||||
if (result == RESULT_OK) {
|
||||
cout << " store template OK" << endl;
|
||||
} else {
|
||||
cout << " store template error: " << getResultCode(result) << endl;
|
||||
delete fields;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isDefaults) {
|
||||
// store defaults or condition
|
||||
vector<string>::iterator it = entries.begin();
|
||||
size_t oldSize = conditions.size();
|
||||
result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": read defaults OK" << endl;
|
||||
if (isCondition) {
|
||||
if (conditions.size() == oldSize) {
|
||||
cout << " create condition error" << endl;
|
||||
} else {
|
||||
result = messages->resolveConditions();
|
||||
if (result != RESULT_OK) {
|
||||
cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl;
|
||||
} else {
|
||||
cout << " resolve conditions OK" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isChain) {
|
||||
size_t pos = 0;
|
||||
string token;
|
||||
istringstream stream(check[2]);
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= mstrs.size()) {
|
||||
mstrs.resize(pos+1);
|
||||
} else if (mstrs[pos] != NULL) {
|
||||
delete mstrs[pos];
|
||||
}
|
||||
mstrs[pos] = new SymbolString(false);
|
||||
result = mstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
pos = 0;
|
||||
stream.str(check[3]);
|
||||
stream.clear();
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= sstrs.size()) {
|
||||
sstrs.resize(pos+1);
|
||||
} else if (sstrs[pos] != NULL) {
|
||||
delete sstrs[pos];
|
||||
}
|
||||
sstrs[pos] = new SymbolString(false);
|
||||
result = sstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (mstrs[0] != NULL) {
|
||||
delete mstrs[0];
|
||||
}
|
||||
mstrs[0] = new SymbolString(false);
|
||||
result = mstrs[0]->parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
if (sstrs[0] != NULL) {
|
||||
delete sstrs[0];
|
||||
}
|
||||
sstrs[0] = new SymbolString(false);
|
||||
result = sstrs[0]->parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// message: [type],[circuit],name,[comment],[QQ[;QQ]*],[ZZ],[PBSB],[ID],fields...
|
||||
// field: name,part,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// template: name,type[:len][,[divisor|values][,[unit][,[comment]]]]
|
||||
// condition: name,circuit,messagename,[comment],[fieldname],[ZZ],values
|
||||
string checks[][5] = {
|
||||
// "message", "decoded", "master", "slave", "flags"
|
||||
{"date,HDA:3,,,Datum", "", "", "", "template"},
|
||||
{"time,VTI,,,", "", "", "", "template"},
|
||||
{"dcfstate,UCH,0=nosignal;1=ok;2=sync;3=valid,,", "", "", "", "template"},
|
||||
{"temp,D2C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp1,D1C,,°C,Temperatur", "", "", "", "template"},
|
||||
{"temp2,D2B,,°C,Temperatur", "", "", "", "template"},
|
||||
{"power,UCH,,kW", "", "", "", "template"},
|
||||
{"sensor,UCH,0=ok;85=circuit;170=cutoff,,Fühlerstatus", "", "", "", "template"},
|
||||
{"sensorc,UCH,=85,,Fühlerstatus", "", "", "", "template"},
|
||||
{"pumpstate,UCH,0=off;1=on;2=overrun,,Pumpenstatus", "", "", "", "template"},
|
||||
{"tempsensor,temp;sensor,,Temperatursensor", "", "", "", "template"},
|
||||
{"tempsensorc,temp;sensorc,,Temperatursensor", "", "", "", "template"},
|
||||
{"r,,Status01,VL/RL/AussenTemp/VLWW/SpeicherTemp/Status,,08,B511,01,,,temp1;temp1;temp2;temp1;temp1;pumpstate", "28.0;24.0;4.938;35.0;41.0;4", "ff08b5110101", "093830f00446520400ff", "d"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor", "temp=-14.00 Temperatursensor [Temperatur];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00},\n \"sensor\": {\"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,tempsensor,,field unit,field comment", "\n \"temp\": {\"value\": -14.00, \"unit\": \"field unit\", \"comment\": \"field comment\"},\n \"sensor\": {\"value\": \"ok\", \"comment\": \"Fühlerstatus\"}", "ff25b509030d2800", "0320ff00", "mJ"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,temp,,field unit,field comment,,,sensor", "temp=-14.00 field unit [field comment];sensor=ok [Fühlerstatus]", "ff25b509030d2800", "0320ff00", "mD"},
|
||||
{"r,message circuit,message name,message comment,,25,B509,0d2800,,,D2C,,°C,Temperatur,,,sensor", "\n \"0\": {\"name\": \"\", \"value\": -14.00},\n \"1\": {\"name\": \"sensor\", \"value\": \"ok\"}", "ff25b509030d2800", "0320ff00", "mj"},
|
||||
{"r,,name,,,25,B509,0d2800,,,tempsensorc", "-14.00", "ff25b509030d2800", "0320ff55", "m"},
|
||||
{"r,,name,,,25,B509,0d28,,m,sensorc,,,,,,temp", "-14.00", "ff25b509030d2855", "0220ff", "m"},
|
||||
{"u,,first,,,fe,0700,,x,,bda", "26.10.2014", "fffe07000426100614", "00", "p"},
|
||||
{"u,broadcast,hwStatus,,,fe,b505,27,,,UCH,,,,,,UCH,,,,,,UCH,,,", "0;19;0", "10feb505042700130097", "00", ""},
|
||||
{"w,,first,,,15,b509,0400,date,,bda", "26.10.2014", "ff15b50906040026100614", "00", "m"},
|
||||
{"w,,first,,,15,b509", "", "ff15b50900", "00", "m"},
|
||||
{"w,,,,,,b505,2d", "", "", "", "defaults"},
|
||||
{"w,,offset,,,50,,,,,temp", "0.50", "ff50b505042d080000", "00", "md"},
|
||||
{"r,ehp,time,,,08,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md"},
|
||||
{"r,ehp,time,,,08;10,b509,0d2800,,,time", "", "", "", "c"},
|
||||
{"r,ehp,time,,,08;09,b509,0d2800,,,time", "15:00:17", "ff08b509030d2800", "0311000f", "md*"},
|
||||
{"r,ehp,date,,,08,b509,0d2900,,,date", "23.11.2014", "ff08b509030d2900", "03170b0e", "md"},
|
||||
{"r,700,date,,,15,b524,020000003400,,,IGN:4,,,,,,date", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"r,700,time,,,15,b524,030000003500,,,IGN:4,,,,,,HTI", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"", "23.11.2015", "ff15b52406020000003400", "0703003400170b0f", "d"},
|
||||
{"", "12:29:06", "ff15b52406030000003500", "07030035000c1d06", "d"},
|
||||
{"w,700,date,,,15,b524,020000003400,,,date", "23.11.2015", "ff15b52409020000003400170b0f", "00", "m"},
|
||||
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "3;15:00:17", "ff08b509040d280003", "0311000f", "mdi"},
|
||||
{"r,ehp,error,,,08,b509,0d2800,index,m,UCH,,,,,,time", "index=3;time=15:00:17", "ff08b509040d280003", "0311000f", "mD"},
|
||||
{"u,ehp,ActualEnvironmentPower,Energiebezug,,08,B509,29BA00,,s,IGN:2,,,,,s,power", "8", "1008b5090329ba00", "03ba0008", "pm"},
|
||||
{"uw,ehp,test,Test,,08,B5de,ab,,,power,,,,,s,hex:1", "8;39", "1008b5de02ab08", "0139", "pm"},
|
||||
{"u,ehp,hwTankTemp,Speichertemperatur IST,,25,B509,290000,,,IGN:2,,,,,,tempsensor", "", "", "", "M"},
|
||||
{"", "55.50;ok", "1025b50903290000", "050000780300", "d"},
|
||||
{"r,ehp,datetime,Datum Uhrzeit,,50,B504,00,,,dcfstate,,,,time,,BTI,,,,date,,BDA,,,,temp,,temp2", "valid;08:24:51;31.12.2014;-0.875", "1050b5040100", "0a035124083112031420ff", "md" },
|
||||
{"r,ehp,bad,invalid pos,,50,B5ff,000102,,m,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
|
||||
{"r,ehp,bad,invalid pos,,50,B5ff,,,s,HEX:8;tempsensor;tempsensor;tempsensor;tempsensor;tempsensor;power;power,,,", "", "", "", "c" },
|
||||
{"r,ehp,ApplianceCode,,,08,b509,0d4301,,,UCH,", "9", "ff08b509030d4301", "0109", "d" },
|
||||
{"r,ehp,,,,08,b509,0d", "", "", "", "defaults" },
|
||||
{"w,ehp,,,,08,b509,0e", "", "", "", "defaults" },
|
||||
{"[brinetowater],ehp,ApplianceCode,,,,4;6;8;9;10", "", "", "", "condition" },
|
||||
{"[airtowater]r,ehp,notavailable,,,,,0100,,,uch", "1", "", "", "c" },
|
||||
{"[brinetowater]r,ehp,available,,,,,0100,,,uch", "1", "ff08b509030d0100", "0101", "d" },
|
||||
{"r,,x,,,,,\"6800\",,,UCH,,,bit0=\"comment, continued comment", "", "", "", "c" },
|
||||
{"r,,x,,,,,\"6800\",,,UCH,,\"\",\"bit0=\"comment, continued comment\"", "=1 [bit0=\"comment, continued comment]", "ff08b509030d6800", "0101", "mD" },
|
||||
{"r,ehp,multi,,,,,0001:5;0002;0003,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509030d0001;ff08b509030d0003;ff08b509030d0002", "054142434445;054b4c4d4e4f;05464748494a", "mdC" },
|
||||
{"r,ehp,multi,,,,,01;02;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b509020d01;ff08b509020d03;ff08b509020d02", "084142434445464748;054b4c4d4e4f;02494a", "mdC" },
|
||||
{"w,ehp,multi,,,,,01:8;02:2;03,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "mdC" },
|
||||
{"w,ehp,multi,,,,,01:8;02:2;0304,longname,,STR:15", "ABCDEFGHIJKLMNO", "ff08b5090a0e014142434445464748;ff08b509040e02494a;ff08b509070e034b4c4d4e4f", "00;00;00", "cC" },
|
||||
{"r,ehp,scan,chained scan,,08,B509,24:9;25;26;27,,,IGN,,,,id4,,STR:28", "21074500100027790000000000N8", "ff08b5090124;ff08b5090125;ff08b5090126;ff08b5090127", "09003231303734353030;09313030303237373930;09303030303030303030;024E38", "mdC" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B61", "ff08b509030d6900", "03138040", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B71;B60", "ff08b509030d6900", "0313ffbf", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B61", "ff08b509030d6900", "03137fff", "md" },
|
||||
{"r,,x,,,,,6900,,,UCH,10,bar,,Bit7,,BI7:1,0=B70;1=B71,,,Bit6,,BI6:1,0=B60;1=B61", "1.9;B70;B60", "ff08b509030d6900", "03137fbf", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B71", "ff08b509030d6900", "0213ff", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B71", "ff08b509030d6900", "0213bf", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B61;B70", "ff08b509030d6900", "02137f", "md" },
|
||||
{"r,,x,,,,,6a00,,,UCH,10,bar,,Bit6,,BI6:1,0=B60;1=B61,,,Bit7,,BI7:1,0=B70;1=B71", "1.9;B60;B70", "ff08b509030d6900", "02133f", "md" },
|
||||
{"r,cir*cuit#level,na*me,com*ment,ff,75,b509,0d", "", "", "", "defaults" },
|
||||
{"r,CIRCUIT,NAME,COMMENT,,,,0100,field,,UCH", "r,cirCIRCUITcuit#level,naNAMEme,comCOMMENTment,ff,75,b509,0d0100,field,s,UCH,,,: field=42", "ff08b509030d0100", "012a", "mDN"},
|
||||
};
|
||||
templates = new DataFieldTemplates();
|
||||
MessageMap* messages = new MessageMap();
|
||||
vector< vector<string> > defaultsRows;
|
||||
map<string, Condition*> &conditions = messages->getConditions();
|
||||
Message* message = NULL;
|
||||
vector<Message*> deleteMessages;
|
||||
vector<SymbolString*> mstrs;
|
||||
vector<SymbolString*> sstrs;
|
||||
mstrs.resize(1);
|
||||
sstrs.resize(1);
|
||||
for (size_t i = 0; i < sizeof(checks) / sizeof(checks[0]); i++) {
|
||||
string check[5] = checks[i];
|
||||
string inputStr = check[1];
|
||||
string flags = check[4];
|
||||
bool isTemplate = flags == "template";
|
||||
bool isCondition = flags == "condition";
|
||||
bool isDefaults = isCondition || flags == "defaults";
|
||||
bool dontMap = flags.find('m') != string::npos;
|
||||
bool onlyMap = flags.find('M') != string::npos;
|
||||
bool failedCreate = flags.find('c') != string::npos;
|
||||
bool isChain = flags.find('C') != string::npos;
|
||||
bool decodeJson = flags.find('j') != string::npos || flags.find('J') != string::npos;
|
||||
bool decodeVerbose = flags.find('D') != string::npos || flags.find('J') != string::npos;
|
||||
bool withMessageDump = flags.find('N') != string::npos;
|
||||
bool decode = decodeJson || decodeVerbose || (flags.find('d') != string::npos);
|
||||
bool failedPrepare = flags.find('p') != string::npos;
|
||||
bool failedPrepareMatch = flags.find('P') != string::npos;
|
||||
bool multi = flags.find('*') != string::npos;
|
||||
bool withInput = flags.find('i') != string::npos;
|
||||
result_t result = RESULT_EMPTY;
|
||||
vector<string> entries;
|
||||
istringstream ifs(check[0]);
|
||||
unsigned int lineNo = 0;
|
||||
if (!FileReader::splitFields(ifs, entries, lineNo)) {
|
||||
entries.clear();
|
||||
}
|
||||
if (isTemplate) {
|
||||
// store new template
|
||||
DataField* fields = NULL;
|
||||
vector<string>::iterator it = entries.begin();
|
||||
result = DataField::create(it, entries.end(), templates, fields, false, true, false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": template fields create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": create template OK" << endl;
|
||||
result = templates->add(fields, "", true);
|
||||
if (result == RESULT_OK) {
|
||||
cout << " store template OK" << endl;
|
||||
} else {
|
||||
cout << " store template error: " << getResultCode(result) << endl;
|
||||
delete fields;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isDefaults) {
|
||||
// store defaults or condition
|
||||
vector<string>::iterator it = entries.begin();
|
||||
size_t oldSize = conditions.size();
|
||||
result = messages->addDefaultFromFile(defaultsRows, entries, it, "", "", "", "no file", 1);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: " << getResultCode(result) << endl;
|
||||
} else if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": defaults read error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": read defaults OK" << endl;
|
||||
if (isCondition) {
|
||||
if (conditions.size() == oldSize) {
|
||||
cout << " create condition error" << endl;
|
||||
} else {
|
||||
result = messages->resolveConditions();
|
||||
if (result != RESULT_OK) {
|
||||
cout << " resolve conditions error: " << getResultCode(result) << " " << messages->getLastError() << endl;
|
||||
} else {
|
||||
cout << " resolve conditions OK" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isChain) {
|
||||
size_t pos = 0;
|
||||
string token;
|
||||
istringstream stream(check[2]);
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= mstrs.size()) {
|
||||
mstrs.resize(pos+1);
|
||||
} else if (mstrs[pos] != NULL) {
|
||||
delete mstrs[pos];
|
||||
}
|
||||
mstrs[pos] = new SymbolString(false);
|
||||
result = mstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
pos = 0;
|
||||
stream.str(check[3]);
|
||||
stream.clear();
|
||||
while (getline(stream, token, VALUE_SEPARATOR)) {
|
||||
if (pos >= sstrs.size()) {
|
||||
sstrs.resize(pos+1);
|
||||
} else if (sstrs[pos] != NULL) {
|
||||
delete sstrs[pos];
|
||||
}
|
||||
sstrs[pos] = new SymbolString(false);
|
||||
result = sstrs[pos]->parseHex(token);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << token << "\" error: " << getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (mstrs[0] != NULL) {
|
||||
delete mstrs[0];
|
||||
}
|
||||
mstrs[0] = new SymbolString(false);
|
||||
result = mstrs[0]->parseHex(check[2]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[2] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
if (sstrs[0] != NULL) {
|
||||
delete sstrs[0];
|
||||
}
|
||||
sstrs[0] = new SymbolString(false);
|
||||
result = sstrs[0]->parseHex(check[3]);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": parse \"" << check[3] << "\" error: " << getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
message = messages->find(*mstrs[0]);
|
||||
if (message == NULL) {
|
||||
cout << "\"" << check[2] << "\": find error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[2] << "\": find OK" << endl;
|
||||
} else {
|
||||
vector<string>::iterator it = entries.begin();
|
||||
string types = *it;
|
||||
Condition* condition = NULL;
|
||||
result = messages->readConditions(types, "no file", condition);
|
||||
if (result == RESULT_OK) {
|
||||
*it = types;
|
||||
result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages);
|
||||
}
|
||||
if (failedCreate) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": failed create OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: "
|
||||
<< getResultCode(result) << endl;
|
||||
printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result);
|
||||
continue;
|
||||
}
|
||||
if (deleteMessages.size() == 0) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
continue;
|
||||
}
|
||||
if (multi && deleteMessages.size() == 1) {
|
||||
cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl;
|
||||
continue;
|
||||
}
|
||||
if (!multi && deleteMessages.size() > 1) {
|
||||
cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\": create OK" << endl;
|
||||
if (!dontMap) {
|
||||
result_t result = RESULT_OK;
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
result_t result = messages->add(deleteMessage);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": add error: "
|
||||
<< getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
cout << " map OK" << endl;
|
||||
message = deleteMessages.front();
|
||||
deleteMessages.clear();
|
||||
if (onlyMap) {
|
||||
continue;
|
||||
}
|
||||
Message* foundMessage = messages->find(*mstrs[0]);
|
||||
if (foundMessage == message) {
|
||||
cout << " find OK" << endl;
|
||||
} else if (foundMessage == NULL) {
|
||||
cout << " find error: NULL" << endl;
|
||||
} else {
|
||||
cout << " find error: different" << endl;
|
||||
}
|
||||
} else {
|
||||
message = deleteMessages.front();
|
||||
}
|
||||
}
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
message = messages->find(*mstrs[0]);
|
||||
if (message == NULL) {
|
||||
cout << "\"" << check[2] << "\": find error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[2] << "\": find OK" << endl;
|
||||
} else {
|
||||
vector<string>::iterator it = entries.begin();
|
||||
string types = *it;
|
||||
Condition* condition = NULL;
|
||||
result = messages->readConditions(types, "no file", condition);
|
||||
if (result == RESULT_OK) {
|
||||
*it = types;
|
||||
result = Message::create(it, entries.end(), &defaultsRows, condition, "no file", templates, deleteMessages);
|
||||
}
|
||||
if (failedCreate) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": failed create error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << "\"" << check[0] << "\": failed create OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": create error: "
|
||||
<< getResultCode(result) << endl;
|
||||
printErrorPos(cout, entries.begin(), entries.end(), it, "", 0, result);
|
||||
continue;
|
||||
}
|
||||
if (deleteMessages.size() == 0) {
|
||||
cout << "\"" << check[0] << "\": create error: NULL" << endl;
|
||||
continue;
|
||||
}
|
||||
if (it != entries.end()) {
|
||||
cout << "\"" << check[0] << "\": create error: trailing input " << static_cast<unsigned>(entries.end()-it) << endl;
|
||||
continue;
|
||||
}
|
||||
if (multi && deleteMessages.size() == 1) {
|
||||
cout << "\"" << check[0] << "\": create error: single message instead of multiple" << endl;
|
||||
continue;
|
||||
}
|
||||
if (!multi && deleteMessages.size() > 1) {
|
||||
cout << "\"" << check[0] << "\": create error: multiple messages instead of single" << endl;
|
||||
continue;
|
||||
}
|
||||
cout << "\"" << check[0] << "\": create OK" << endl;
|
||||
if (!dontMap) {
|
||||
result_t result = RESULT_OK;
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
result_t result = messages->add(deleteMessage);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "\"" << check[0] << "\": add error: "
|
||||
<< getResultCode(result) << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != RESULT_OK) {
|
||||
continue;
|
||||
}
|
||||
cout << " map OK" << endl;
|
||||
message = deleteMessages.front();
|
||||
deleteMessages.clear();
|
||||
if (onlyMap) {
|
||||
continue;
|
||||
}
|
||||
Message* foundMessage = messages->find(*mstrs[0]);
|
||||
if (foundMessage == message) {
|
||||
cout << " find OK" << endl;
|
||||
} else if (foundMessage == NULL) {
|
||||
cout << " find error: NULL" << endl;
|
||||
} else {
|
||||
cout << " find error: different" << endl;
|
||||
}
|
||||
} else {
|
||||
message = deleteMessages.front();
|
||||
}
|
||||
}
|
||||
|
||||
if (message->isPassive() || decode) {
|
||||
ostringstream output;
|
||||
for (unsigned char index = 0; index < message->getCount(); index++) {
|
||||
message->storeLastData(*mstrs[index], *sstrs[index]);
|
||||
}
|
||||
if (withMessageDump && !decodeJson) {
|
||||
message->dump(output, NULL, true);
|
||||
output << ": ";
|
||||
}
|
||||
result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl;
|
||||
bool match = inputStr == output.str();
|
||||
verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str());
|
||||
}
|
||||
if (!message->isPassive() && (withInput || !decode)) {
|
||||
istringstream input(inputStr);
|
||||
SymbolString writeMstr(false);
|
||||
result = message->prepareMaster(0xff, writeMstr, input);
|
||||
if (failedPrepare) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " \"" << inputStr << "\": failed prepare OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (message->isPassive() || decode) {
|
||||
ostringstream output;
|
||||
for (unsigned char index = 0; index < message->getCount(); index++) {
|
||||
message->storeLastData(*mstrs[index], *sstrs[index]);
|
||||
}
|
||||
if (withMessageDump && !decodeJson) {
|
||||
message->dump(output, NULL, true);
|
||||
output << ": ";
|
||||
}
|
||||
result = message->decodeLastData(output, (decodeVerbose?OF_NAMES|OF_UNITS|OF_COMMENTS:0)|(decodeJson?OF_NAMES|OF_JSON:0), false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << check[2] << "\" / \"" << check[3] << "\": decode OK" << endl;
|
||||
bool match = inputStr == output.str();
|
||||
verify(false, "decode", check[2] + "/" + check[3], match, inputStr, output.str());
|
||||
}
|
||||
if (!message->isPassive() && (withInput || !decode)) {
|
||||
istringstream input(inputStr);
|
||||
SymbolString writeMstr(false);
|
||||
result = message->prepareMaster(0xff, writeMstr, input);
|
||||
if (failedPrepare) {
|
||||
if (result == RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": failed prepare error: unexpectedly succeeded" << endl;
|
||||
} else {
|
||||
cout << " \"" << inputStr << "\": failed prepare OK" << endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": prepare error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << inputStr << "\": prepare OK" << endl;
|
||||
if (result != RESULT_OK) {
|
||||
cout << " \"" << inputStr << "\": prepare error: "
|
||||
<< getResultCode(result) << endl;
|
||||
continue;
|
||||
}
|
||||
cout << " \"" << inputStr << "\": prepare OK" << endl;
|
||||
|
||||
bool match = writeMstr == *mstrs[0];
|
||||
verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false));
|
||||
}
|
||||
}
|
||||
bool match = writeMstr == *mstrs[0];
|
||||
verify(failedPrepareMatch, "prepare", inputStr, match, mstrs[0]->getDataStr(true, false), writeMstr.getDataStr(true, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
if (deleteMessages.size() > 0) {
|
||||
for (vector<Message*>::iterator it = deleteMessages.begin(); it != deleteMessages.end(); it++) {
|
||||
Message* deleteMessage = *it;
|
||||
delete deleteMessage;
|
||||
}
|
||||
deleteMessages.clear();
|
||||
}
|
||||
|
||||
delete templates;
|
||||
delete messages;
|
||||
for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
return 0;
|
||||
delete templates;
|
||||
delete messages;
|
||||
for (vector<SymbolString*>::iterator it = mstrs.begin(); it != mstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
for (vector<SymbolString*>::iterator it = sstrs.begin(); it != sstrs.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,70 +27,70 @@ using namespace ebusd;
|
||||
static bool error = false;
|
||||
|
||||
void verify(bool expectFailMatch, string type, string input,
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
bool match, string expectStr, string gotStr) {
|
||||
match = match && expectStr == gotStr;
|
||||
if (expectFailMatch) {
|
||||
if (match) {
|
||||
cout << " failed " << type << " match >" << input
|
||||
<< "< error: unexpectedly succeeded" << endl;
|
||||
error = true;
|
||||
} else {
|
||||
cout << " failed " << type << " match >" << input << "< OK" << endl;
|
||||
}
|
||||
} else if (match) {
|
||||
cout << " " << type << " match >" << input << "< OK" << endl;
|
||||
} else {
|
||||
cout << " " << type << " match >" << input << "< error: got >"
|
||||
<< gotStr << "<, expected >" << expectStr << "<" << endl;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SymbolString sstr(true);
|
||||
SymbolString sstr(true);
|
||||
|
||||
if (argc > 1) {
|
||||
result_t result = sstr.parseHex(argv[1], true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
unsigned char gotCrc = sstr.getCRC();
|
||||
cout << "calculated CRC: 0x"
|
||||
<< nouppercase << setw(2) << hex << setfill('0')
|
||||
<< static_cast<unsigned>(gotCrc) << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
string gotStr, expectStr;
|
||||
result_t result = sstr.parseHex("10feb5050427a915aa", false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177";
|
||||
verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77;
|
||||
ostringstream ostr;
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc);
|
||||
expectStr = ostr.str();
|
||||
ostr.str("");
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc);
|
||||
gotStr = ostr.str();
|
||||
verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr);
|
||||
if (argc > 1) {
|
||||
result_t result = sstr.parseHex(argv[1], true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
} else {
|
||||
unsigned char gotCrc = sstr.getCRC();
|
||||
cout << "calculated CRC: 0x"
|
||||
<< nouppercase << setw(2) << hex << setfill('0')
|
||||
<< static_cast<unsigned>(gotCrc) << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
string gotStr, expectStr;
|
||||
result_t result = sstr.parseHex("10feb5050427a915aa", false);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse escaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(false, false), expectStr = "10feb5050427a90015a90177";
|
||||
verify(false, "parse escaped", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
unsigned char gotCrc = sstr.getCRC(), expectCrc = 0x77;
|
||||
ostringstream ostr;
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(expectCrc);
|
||||
expectStr = ostr.str();
|
||||
ostr.str("");
|
||||
ostr << nouppercase << setw(2) << hex << setfill('0') << static_cast<unsigned>(gotCrc);
|
||||
gotStr = ostr.str();
|
||||
verify(false, "CRC", "10feb5050427a915aa", gotCrc == expectCrc, expectStr, gotStr);
|
||||
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
}
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "unescape", "10feb5050427a915aa", true, expectStr, gotStr);
|
||||
}
|
||||
|
||||
sstr = SymbolString(false);
|
||||
result = sstr.parseHex("10feb5050427a90015a90177", true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse unescaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr);
|
||||
}
|
||||
sstr = SymbolString(false);
|
||||
result = sstr.parseHex("10feb5050427a90015a90177", true);
|
||||
if (result != RESULT_OK) {
|
||||
cout << "parse unescaped error: " << getResultCode(result) << endl;
|
||||
error = true;
|
||||
} else {
|
||||
gotStr = sstr.getDataStr(true, false), expectStr = "10feb5050427a915aa77";
|
||||
verify(false, "parse unescaped", "10feb5050427a90015a90177", true, expectStr, gotStr);
|
||||
}
|
||||
|
||||
return error ? 1 : 0;
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user