allow mixing references to base type and templates in message definition
This commit is contained in:
+107
-123
@@ -274,132 +274,33 @@ result_t DataField::create(vector<string>::iterator& it,
|
|||||||
comment = str;
|
comment = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
string typeName;
|
bool firstType = true;
|
||||||
size_t pos = typeStr.find(LENGTH_SEPARATOR);
|
istringstream stream(typeStr);
|
||||||
unsigned char length;
|
while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR) != 0) {
|
||||||
if (pos == string::npos) {
|
DataField* templ = templates->get(token);
|
||||||
length = 0;
|
unsigned char length;
|
||||||
// check for reference(s) to templates
|
if (templ == NULL) {
|
||||||
if (templates != NULL) {
|
size_t pos = token.find(LENGTH_SEPARATOR);
|
||||||
istringstream stream(typeStr);
|
if (pos == string::npos)
|
||||||
bool found = false;
|
length = 0; // no length specified
|
||||||
while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR) != 0) {
|
else {
|
||||||
DataField* templ = templates->get(token);
|
length = (unsigned char)parseInt(token.substr(pos+1).c_str(), 10, 1, MAX_POS, result);
|
||||||
if (templ == NULL) {
|
if (result != RESULT_OK)
|
||||||
if (!found)
|
break;
|
||||||
break; // fallback to direct definition
|
|
||||||
result = RESULT_ERR_NOTFOUND; // cannot mix reference and direct definition
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
found = true;
|
|
||||||
result = templ->derive("", "", "", partType, divisor, values, fields);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (result != RESULT_OK)
|
string typeName = token.substr(0, pos);
|
||||||
break;
|
SingleDataField* add = NULL;
|
||||||
if (found)
|
result = SingleDataField::create(typeName.c_str(), length, firstType ? name : "", firstType ? comment : "", firstType ? unit : "", partType, divisor, values, add);
|
||||||
continue; // go to next definition
|
if (add != NULL)
|
||||||
|
fields.push_back(add);
|
||||||
|
else if (result == RESULT_OK)
|
||||||
|
result = RESULT_ERR_NOTFOUND; // type not found
|
||||||
}
|
}
|
||||||
typeName = typeStr;
|
else
|
||||||
|
result = templ->derive(firstType ? name : "", firstType ? comment : "", firstType ? unit : "", partType, divisor, values, fields);
|
||||||
|
|
||||||
|
firstType = false;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
length = (unsigned char)parseInt(typeStr.substr(pos+1).c_str(), 10, 1, MAX_POS, result);
|
|
||||||
if (result != RESULT_OK)
|
|
||||||
break;
|
|
||||||
typeName = typeStr.substr(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
SingleDataField* add = NULL;
|
|
||||||
const char* typeNameStr = typeName.c_str();
|
|
||||||
for (size_t i = 0; result == RESULT_OK && add == NULL && i < sizeof(dataTypes) / sizeof(dataType_t); i++) {
|
|
||||||
const dataType_t* dataType = &dataTypes[i];
|
|
||||||
if (strcasecmp(typeNameStr, dataType->name) == 0) {
|
|
||||||
unsigned char bitCount = dataType->bitCount;
|
|
||||||
unsigned char byteCount = (unsigned char)((bitCount + 7) / 8);
|
|
||||||
if ((dataType->flags & ADJ) != 0) { // adjustable length
|
|
||||||
if ((bitCount % 8) != 0) {
|
|
||||||
if (length == 0)
|
|
||||||
bitCount = 1; // default bit count: 1 bit
|
|
||||||
else if (length <= bitCount)
|
|
||||||
bitCount = length;
|
|
||||||
else {
|
|
||||||
result = RESULT_ERR_OUT_OF_RANGE; // invalid length
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
byteCount = (unsigned char)((bitCount + 7) / 8);
|
|
||||||
}
|
|
||||||
else if (length == 0)
|
|
||||||
byteCount = 1; //default byte count: 1 byte
|
|
||||||
else if (length <= byteCount)
|
|
||||||
byteCount = length;
|
|
||||||
else {
|
|
||||||
result = RESULT_ERR_OUT_OF_RANGE; // invalid length
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (length > 0 && length != byteCount)
|
|
||||||
continue; // check for another one with same name but different length
|
|
||||||
|
|
||||||
switch (dataType->type)
|
|
||||||
{
|
|
||||||
case bt_str:
|
|
||||||
case bt_hexstr:
|
|
||||||
case bt_dat:
|
|
||||||
case bt_tim:
|
|
||||||
add = new StringDataField(name, comment, unit, *dataType, partType, byteCount);
|
|
||||||
break;
|
|
||||||
case bt_num:
|
|
||||||
if (values.empty() && (dataType->flags & DAY) != 0) {
|
|
||||||
for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++)
|
|
||||||
values[dataType->minValueOrLength + i] = dayNames[i];
|
|
||||||
}
|
|
||||||
if (values.empty() || (dataType->flags & LST) == 0) {
|
|
||||||
if (divisor == 0)
|
|
||||||
divisor = 1;
|
|
||||||
|
|
||||||
if ((dataType->bitCount % 8) == 0) {
|
|
||||||
if (divisor < 0) {
|
|
||||||
if (dataType->divisorOrFirstBit > 1) {
|
|
||||||
result = RESULT_ERR_INVALID_ARG;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (dataType->divisorOrFirstBit < 0)
|
|
||||||
divisor *= -dataType->divisorOrFirstBit;
|
|
||||||
} else if (dataType->divisorOrFirstBit < 0) {
|
|
||||||
if (divisor > 1) {
|
|
||||||
result = RESULT_ERR_INVALID_ARG;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (divisor < 0)
|
|
||||||
divisor *= -dataType->divisorOrFirstBit;
|
|
||||||
} else
|
|
||||||
divisor *= dataType->divisorOrFirstBit;
|
|
||||||
|
|
||||||
if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) {
|
|
||||||
result = RESULT_ERR_OUT_OF_RANGE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add = new NumberDataField(name, comment, unit, *dataType, partType, byteCount, bitCount, divisor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (values.begin()->first < dataType->minValueOrLength
|
|
||||||
|| values.rbegin()->first > dataType->maxValueOrLength) {
|
|
||||||
result = RESULT_ERR_OUT_OF_RANGE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//TODO add special field for fixed values (exactly one value in the list of values)
|
|
||||||
add = new ValueListDataField(name, comment, unit, *dataType, partType, byteCount, bitCount, values);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (add != NULL)
|
|
||||||
fields.push_back(add);
|
|
||||||
else if (result == RESULT_OK)
|
|
||||||
result = RESULT_ERR_NOTFOUND; // type not found
|
|
||||||
|
|
||||||
} while (it != end && result == RESULT_OK);
|
} while (it != end && result == RESULT_OK);
|
||||||
|
|
||||||
@@ -430,6 +331,89 @@ void DataField::dumpString(ostream& output, const string str, const bool prepend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result_t SingleDataField::create(const char* typeNameStr, const unsigned char length,
|
||||||
|
const string name, const string comment, const string unit,
|
||||||
|
const PartType partType, int divisor, map<unsigned int, string> values,
|
||||||
|
SingleDataField* &returnField)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(dataTypes) / sizeof(dataType_t); i++) {
|
||||||
|
const dataType_t* dataType = &dataTypes[i];
|
||||||
|
if (strcasecmp(typeNameStr, dataType->name) == 0) {
|
||||||
|
unsigned char bitCount = dataType->bitCount;
|
||||||
|
unsigned char byteCount = (unsigned char)((bitCount + 7) / 8);
|
||||||
|
if ((dataType->flags & ADJ) != 0) { // adjustable length
|
||||||
|
if ((bitCount % 8) != 0) {
|
||||||
|
if (length == 0)
|
||||||
|
bitCount = 1; // default bit count: 1 bit
|
||||||
|
else if (length <= bitCount)
|
||||||
|
bitCount = length;
|
||||||
|
else
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid length
|
||||||
|
|
||||||
|
byteCount = (unsigned char)((bitCount + 7) / 8);
|
||||||
|
}
|
||||||
|
else if (length == 0)
|
||||||
|
byteCount = 1; //default byte count: 1 byte
|
||||||
|
else if (length <= byteCount)
|
||||||
|
byteCount = length;
|
||||||
|
else
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE; // invalid length
|
||||||
|
}
|
||||||
|
else if (length > 0 && length != byteCount)
|
||||||
|
continue; // check for another one with same name but different length
|
||||||
|
|
||||||
|
switch (dataType->type)
|
||||||
|
{
|
||||||
|
case bt_str:
|
||||||
|
case bt_hexstr:
|
||||||
|
case bt_dat:
|
||||||
|
case bt_tim:
|
||||||
|
returnField = new StringDataField(name, comment, unit, *dataType, partType, byteCount);
|
||||||
|
return RESULT_OK;
|
||||||
|
case bt_num:
|
||||||
|
if (values.empty() && (dataType->flags & DAY) != 0) {
|
||||||
|
for (unsigned int i = 0; i < sizeof(dayNames) / sizeof(dayNames[0]); i++)
|
||||||
|
values[dataType->minValueOrLength + i] = dayNames[i];
|
||||||
|
}
|
||||||
|
if (values.empty() || (dataType->flags & LST) == 0) {
|
||||||
|
if (divisor == 0)
|
||||||
|
divisor = 1;
|
||||||
|
|
||||||
|
if ((dataType->bitCount % 8) == 0) {
|
||||||
|
if (divisor < 0) {
|
||||||
|
if (dataType->divisorOrFirstBit > 1)
|
||||||
|
return RESULT_ERR_INVALID_ARG;
|
||||||
|
|
||||||
|
if (dataType->divisorOrFirstBit < 0)
|
||||||
|
divisor *= -dataType->divisorOrFirstBit;
|
||||||
|
} else if (dataType->divisorOrFirstBit < 0) {
|
||||||
|
if (divisor > 1)
|
||||||
|
return RESULT_ERR_INVALID_ARG;
|
||||||
|
|
||||||
|
if (divisor < 0)
|
||||||
|
divisor *= -dataType->divisorOrFirstBit;
|
||||||
|
} else
|
||||||
|
divisor *= dataType->divisorOrFirstBit;
|
||||||
|
|
||||||
|
if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnField = new NumberDataField(name, comment, unit, *dataType, partType, byteCount, bitCount, divisor);
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
if (values.begin()->first < dataType->minValueOrLength
|
||||||
|
|| values.rbegin()->first > dataType->maxValueOrLength)
|
||||||
|
return RESULT_ERR_OUT_OF_RANGE;
|
||||||
|
|
||||||
|
//TODO add special field for fixed values (exactly one value in the list of values)
|
||||||
|
returnField = new ValueListDataField(name, comment, unit, *dataType, partType, byteCount, bitCount, values);
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RESULT_ERR_NOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
void SingleDataField::dump(ostream& output)
|
void SingleDataField::dump(ostream& output)
|
||||||
{
|
{
|
||||||
|
|||||||
+20
-1
@@ -180,7 +180,7 @@ public:
|
|||||||
virtual unsigned char getLength(PartType partType) = 0;
|
virtual unsigned char getLength(PartType partType) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a new DataField from this field.
|
* Derive a new @a DataField from this field.
|
||||||
* @param name the field name.
|
* @param name the field name.
|
||||||
* @param comment the field comment, or empty to use this fields comment.
|
* @param comment the field comment, or empty to use this fields comment.
|
||||||
* @param unit the value unit, or empty to use this fields unit (if applicable).
|
* @param unit the value unit, or empty to use this fields unit (if applicable).
|
||||||
@@ -287,6 +287,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual ~SingleDataField() {}
|
virtual ~SingleDataField() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method for creating a new @a SingleDataField instance derived from a base type.
|
||||||
|
* @param typeNameStr the base type name string.
|
||||||
|
* @param length the base type length, or 0 for default.
|
||||||
|
* @param name the field name.
|
||||||
|
* @param comment the field comment.
|
||||||
|
* @param unit the value unit.
|
||||||
|
* @param partType the message part in which the field is stored.
|
||||||
|
* @param divisor the extra divisor (negative for reciprocal) to apply on the value, or 1 for none (if applicable).
|
||||||
|
* @param values the value=text assignments.
|
||||||
|
* @param returnField the variable in which the created @a SingleDataField instance shall be stored.
|
||||||
|
* @return @a RESULT_OK on success, or an error code.
|
||||||
|
* Note: the caller needs to free the created instance.
|
||||||
|
*/
|
||||||
|
static result_t create(const char* typeNameStr, const unsigned char length,
|
||||||
|
const string name, const string comment, const string unit,
|
||||||
|
const PartType partType, int divisor, map<unsigned int, string> values,
|
||||||
|
SingleDataField* &returnField);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value unit.
|
* Get the value unit.
|
||||||
* @return the value unit.
|
* @return the value unit.
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ int main()
|
|||||||
{"trelrel,temp;temp","","", "", "t"}, // template struct with relative pos and ref to templates
|
{"trelrel,temp;temp","","", "", "t"}, // template struct with relative pos and ref to templates
|
||||||
{"x,,trelrel","18.004;19.008","10fe07000401120213", "00", ""}, // reference to template struct
|
{"x,,trelrel","18.004;19.008","10fe07000401120213", "00", ""}, // reference to template struct
|
||||||
{"x,,temp,,,,y,,d1c","18.004;9.5","10fe070003011213", "00", ""}, // reference to template, normal def
|
{"x,,temp,,,,y,,d1c","18.004;9.5","10fe070003011213", "00", ""}, // reference to template, normal def
|
||||||
|
{"x,,temp;HEX:2","18.004;13 14","10fe07000401121314", "00", ""}, // reference to template and base type
|
||||||
};
|
};
|
||||||
DataFieldTemplates* templates = new DataFieldTemplates();
|
DataFieldTemplates* templates = new DataFieldTemplates();
|
||||||
DataField* fields = NULL;
|
DataField* fields = NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user