allow mixing references to base type and templates in message definition

This commit is contained in:
john30
2015-03-13 22:12:11 +01:00
parent d226818b7e
commit 0f93369bee
3 changed files with 128 additions and 124 deletions
+107 -123
View File
@@ -274,132 +274,33 @@ result_t DataField::create(vector<string>::iterator& it,
comment = str;
}
string typeName;
size_t pos = typeStr.find(LENGTH_SEPARATOR);
unsigned char length;
if (pos == string::npos) {
length = 0;
// check for reference(s) to templates
if (templates != NULL) {
istringstream stream(typeStr);
bool found = false;
while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR) != 0) {
DataField* templ = templates->get(token);
if (templ == NULL) {
if (!found)
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);
}
bool firstType = true;
istringstream stream(typeStr);
while (result == RESULT_OK && getline(stream, token, VALUE_SEPARATOR) != 0) {
DataField* templ = templates->get(token);
unsigned char length;
if (templ == NULL) {
size_t pos = token.find(LENGTH_SEPARATOR);
if (pos == string::npos)
length = 0; // no length specified
else {
length = (unsigned char)parseInt(token.substr(pos+1).c_str(), 10, 1, MAX_POS, result);
if (result != RESULT_OK)
break;
}
if (result != RESULT_OK)
break;
if (found)
continue; // go to next definition
string typeName = token.substr(0, pos);
SingleDataField* add = NULL;
result = SingleDataField::create(typeName.c_str(), length, firstType ? name : "", firstType ? comment : "", firstType ? unit : "", partType, divisor, values, add);
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);
@@ -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)
{
+20 -1
View File
@@ -180,7 +180,7 @@ public:
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 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).
@@ -287,6 +287,25 @@ public:
*/
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.
* @return the value unit.
+1
View File
@@ -227,6 +227,7 @@ int main()
{"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,,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();
DataField* fields = NULL;