kopia lustrzana https://github.com/weetmuts/wmbusmeters
Add dynaic driver add_combinable and storage_nr.
rodzic
dbd99698b8
commit
097f91fac0
|
@ -1,5 +1,7 @@
|
||||||
FROM alpine AS build
|
FROM alpine AS build
|
||||||
RUN apk add --no-cache alpine-sdk gcc linux-headers libxml2-dev cmake libusb-dev bash samurai
|
RUN apk add --no-cache alpine-sdk gcc linux-headers libxml2-dev cmake libusb-dev bash samurai
|
||||||
|
|
||||||
|
ADD https://api.github.com/repos/wmbusmeters/wmbusmeters/git/refs/heads/master version.json
|
||||||
RUN git clone https://github.com/steve-m/librtlsdr.git && \
|
RUN git clone https://github.com/steve-m/librtlsdr.git && \
|
||||||
git clone https://github.com/wmbusmeters/wmbusmeters.git && \
|
git clone https://github.com/wmbusmeters/wmbusmeters.git && \
|
||||||
git clone https://github.com/weetmuts/rtl-wmbus.git && \
|
git clone https://github.com/weetmuts/rtl-wmbus.git && \
|
||||||
|
|
|
@ -35,8 +35,12 @@ string get_translation(XMQDoc *doc, XMQNode *node, string name, string lang);
|
||||||
string check_calculate(const char *formula, DriverDynamic *dd);
|
string check_calculate(const char *formula, DriverDynamic *dd);
|
||||||
Unit check_display_unit(const char *display_unit, DriverDynamic *dd);
|
Unit check_display_unit(const char *display_unit, DriverDynamic *dd);
|
||||||
|
|
||||||
void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd);
|
void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
void checked_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
|
void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
|
void checked_set_tariffnr_range(const char *tariffnr_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
|
void checked_set_subunitnr_range(const char *subunitnr_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
|
void checked_add_vif_combinable(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd);
|
||||||
|
|
||||||
const char *line = "-------------------------------------------------------------------------------";
|
const char *line = "-------------------------------------------------------------------------------";
|
||||||
|
|
||||||
|
@ -107,26 +111,19 @@ bool DriverDynamic::load(DriverInfo *di, const string &file_name, const char *co
|
||||||
DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
|
DriverDynamic::DriverDynamic(MeterInfo &mi, DriverInfo &di) :
|
||||||
MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName())
|
MeterCommonImplementation(mi, di), file_name_(di.getDynamicFileName())
|
||||||
{
|
{
|
||||||
XMQDoc *doc = di.getDynamicDriver();
|
|
||||||
assert(doc);
|
|
||||||
|
|
||||||
verbose("(driver) constructing driver %s from already loaded file %s\n",
|
|
||||||
di.name().str().c_str(),
|
|
||||||
fileName().c_str());
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
XMQDoc *doc = di.getDynamicDriver();
|
||||||
|
assert(doc);
|
||||||
|
|
||||||
|
verbose("(driver) constructing driver %s from already loaded file %s\n",
|
||||||
|
di.name().str().c_str(),
|
||||||
|
fileName().c_str());
|
||||||
|
|
||||||
xmqForeach(doc, NULL, "/driver/use", (XMQNodeCallback)add_use, this);
|
xmqForeach(doc, NULL, "/driver/use", (XMQNodeCallback)add_use, this);
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
xmqForeach(doc, NULL, "/driver/field", (XMQNodeCallback)add_field, this);
|
xmqForeach(doc, NULL, "/driver/field", (XMQNodeCallback)add_field, this);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch(...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,9 +160,9 @@ XMQProceed DriverDynamic::add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *d
|
||||||
char a = mfct[0];
|
char a = mfct[0];
|
||||||
char b = mfct[1];
|
char b = mfct[1];
|
||||||
char c = mfct[2];
|
char c = mfct[2];
|
||||||
if (!(a >= 'A' && a < 'Z' &&
|
if (!(a >= 'A' && a <= 'Z' &&
|
||||||
b >= 'A' && b < 'Z' &&
|
b >= 'A' && b <= 'Z' &&
|
||||||
c >= 'A' && c < 'Z'))
|
c >= 'A' && c <= 'Z'))
|
||||||
{
|
{
|
||||||
warning("(driver) error in %s, bad manufacturer in mvt triplet: %s\n"
|
warning("(driver) error in %s, bad manufacturer in mvt triplet: %s\n"
|
||||||
"%s\n"
|
"%s\n"
|
||||||
|
@ -339,22 +336,22 @@ XMQProceed DriverDynamic::add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *
|
||||||
{
|
{
|
||||||
FieldMatcher *fm = dd->tmp_matcher_;
|
FieldMatcher *fm = dd->tmp_matcher_;
|
||||||
|
|
||||||
check_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd);
|
checked_set_measurement_type(xmqGetString(doc, match, "measurement_type"), fm, dd);
|
||||||
|
|
||||||
check_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd);
|
checked_set_vif_range(xmqGetString(doc, match, "vif_range"), fm, dd);
|
||||||
|
|
||||||
const char *vif_range_s = xmqGetString(doc, match, "vif_range");
|
checked_set_storagenr_range(xmqGetString(doc, match, "storage_nr"), fm, dd);
|
||||||
|
|
||||||
if (vif_range_s)
|
xmqForeach(doc, match, "add_combinable", (XMQNodeCallback)add_combinable, dd);
|
||||||
{
|
|
||||||
VIFRange vif_range = toVIFRange(vif_range_s);
|
return XMQ_CONTINUE;
|
||||||
if (vif_range == VIFRange::None)
|
}
|
||||||
{
|
|
||||||
warning("(driver) error unknown measurement type %s\n", vif_range_s);
|
XMQProceed DriverDynamic::add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd)
|
||||||
throw 1;
|
{
|
||||||
}
|
FieldMatcher *fm = dd->tmp_matcher_;
|
||||||
fm->set(vif_range);
|
|
||||||
}
|
checked_add_vif_combinable(xmqGetString(doc, match, "."), fm, dd);
|
||||||
|
|
||||||
return XMQ_CONTINUE;
|
return XMQ_CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -622,7 +619,7 @@ Unit check_display_unit(const char *display_unit_s, DriverDynamic *dd)
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd)
|
void checked_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm, DriverDynamic *dd)
|
||||||
{
|
{
|
||||||
if (!measurement_type_s)
|
if (!measurement_type_s)
|
||||||
{
|
{
|
||||||
|
@ -665,7 +662,7 @@ void check_set_measurement_type(const char *measurement_type_s, FieldMatcher *fm
|
||||||
fm->set(measurement_type);
|
fm->set(measurement_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd)
|
void checked_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynamic *dd)
|
||||||
{
|
{
|
||||||
if (!vif_range_s)
|
if (!vif_range_s)
|
||||||
{
|
{
|
||||||
|
@ -701,3 +698,58 @@ void check_set_vif_range(const char *vif_range_s, FieldMatcher *fm, DriverDynami
|
||||||
|
|
||||||
fm->set(vif_range);
|
fm->set(vif_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checked_set_storagenr_range(const char *storagenr_range_s, FieldMatcher *fm, DriverDynamic *dd)
|
||||||
|
{
|
||||||
|
if (!storagenr_range_s) return;
|
||||||
|
|
||||||
|
auto fields = splitString(storagenr_range_s, ',');
|
||||||
|
bool ok = isNumber(fields[0]);
|
||||||
|
if (fields.size() > 1)
|
||||||
|
{
|
||||||
|
ok &= isNumber(fields[1]);
|
||||||
|
}
|
||||||
|
if (!ok || fields.size() > 2)
|
||||||
|
{
|
||||||
|
warning("(driver) error in %s, bad storagenr_range: %s\n"
|
||||||
|
"%s\n",
|
||||||
|
dd->fileName().c_str(),
|
||||||
|
storagenr_range_s,
|
||||||
|
line);
|
||||||
|
throw 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields.size() == 1)
|
||||||
|
{
|
||||||
|
fm->set(StorageNr(atoi(fields[0].c_str())));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fm->set(StorageNr(atoi(fields[0].c_str())),
|
||||||
|
StorageNr(atoi(fields[1].c_str())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checked_add_vif_combinable(const char *vif_combinable_s, FieldMatcher *fm, DriverDynamic *dd)
|
||||||
|
{
|
||||||
|
if (!vif_combinable_s) return;
|
||||||
|
|
||||||
|
VIFCombinable vif_combinable = toVIFCombinable(vif_combinable_s);
|
||||||
|
|
||||||
|
if (vif_combinable == VIFCombinable::None)
|
||||||
|
{
|
||||||
|
warning("(driver) error in %s, bad vif_combinable: %s\n"
|
||||||
|
"%s\n"
|
||||||
|
"Available vif combinables:\n"
|
||||||
|
"%s\n"
|
||||||
|
"%s\n",
|
||||||
|
dd->fileName().c_str(),
|
||||||
|
vif_combinable_s,
|
||||||
|
line,
|
||||||
|
availableVIFCombinables().c_str(),
|
||||||
|
line);
|
||||||
|
throw 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fm->add(vif_combinable);
|
||||||
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ struct DriverDynamic : public virtual MeterCommonImplementation
|
||||||
static XMQProceed add_use(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
static XMQProceed add_use(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
||||||
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
||||||
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
||||||
|
static XMQProceed add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
||||||
|
|
||||||
const string &fileName() { return file_name_; }
|
const string &fileName() { return file_name_; }
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,17 @@ LIST_OF_VIF_RANGES
|
||||||
return VIFRange::None;
|
return VIFRange::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VIFCombinable toVIFCombinable(const char *s)
|
||||||
|
{
|
||||||
|
if (!strcmp(s, "None")) return VIFCombinable::None;
|
||||||
|
if (!strcmp(s, "Any")) return VIFCombinable::Any;
|
||||||
|
#define X(name,from,to) if (!strcmp(s, #name)) return VIFCombinable::name;
|
||||||
|
LIST_OF_VIF_COMBINABLES
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
return VIFCombinable::None;
|
||||||
|
}
|
||||||
|
|
||||||
const char *toString(VIFCombinable v)
|
const char *toString(VIFCombinable v)
|
||||||
{
|
{
|
||||||
switch (v) {
|
switch (v) {
|
||||||
|
@ -1299,13 +1310,16 @@ bool FieldMatcher::matches(DVEntry &dv_entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test ranges and types.
|
// Test ranges and types.
|
||||||
bool b =
|
bool range = (!match_vif_range || isInsideVIFRange(dv_entry.vif, vif_range));
|
||||||
(!match_vif_range || isInsideVIFRange(dv_entry.vif, vif_range)) &&
|
bool raw = (!match_vif_raw || dv_entry.vif == vif_raw);
|
||||||
(!match_vif_raw || dv_entry.vif == vif_raw) &&
|
bool type = (!match_measurement_type || dv_entry.measurement_type == measurement_type);
|
||||||
(!match_measurement_type || dv_entry.measurement_type == measurement_type) &&
|
bool storage = (!match_storage_nr || (dv_entry.storage_nr >= storage_nr_from && dv_entry.storage_nr <= storage_nr_to));
|
||||||
(!match_storage_nr || (dv_entry.storage_nr >= storage_nr_from && dv_entry.storage_nr <= storage_nr_to)) &&
|
bool tariff = (!match_tariff_nr || (dv_entry.tariff_nr >= tariff_nr_from && dv_entry.tariff_nr <= tariff_nr_to));
|
||||||
(!match_tariff_nr || (dv_entry.tariff_nr >= tariff_nr_from && dv_entry.tariff_nr <= tariff_nr_to)) &&
|
bool subunit = (!match_subunit_nr || (dv_entry.subunit_nr >= subunit_nr_from && dv_entry.subunit_nr <= subunit_nr_to));
|
||||||
(!match_subunit_nr || (dv_entry.subunit_nr >= subunit_nr_from && dv_entry.subunit_nr <= subunit_nr_to));
|
|
||||||
|
//printf("Match? range=%d raw=%d type=%d storage=%d tariff=%d subunit=%d \n", range, raw, type, storage, tariff, subunit);
|
||||||
|
|
||||||
|
bool b = range & raw & type & storage & tariff & subunit;
|
||||||
|
|
||||||
if (!b) return false;
|
if (!b) return false;
|
||||||
|
|
||||||
|
@ -1501,3 +1515,18 @@ LIST_OF_VIF_RANGES
|
||||||
available_vif_ranges_.pop_back();
|
available_vif_ranges_.pop_back();
|
||||||
return available_vif_ranges_;
|
return available_vif_ranges_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string available_vif_combinables_;
|
||||||
|
|
||||||
|
const string &availableVIFCombinables()
|
||||||
|
{
|
||||||
|
if (available_vif_combinables_ != "") return available_vif_combinables_;
|
||||||
|
|
||||||
|
#define X(n,from,to) available_vif_combinables_ += string(#n) + "\n";
|
||||||
|
LIST_OF_VIF_COMBINABLES
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
// Remove last newline
|
||||||
|
available_vif_combinables_.pop_back();
|
||||||
|
return available_vif_combinables_;
|
||||||
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
X(ExternalTemperature,0x64,0x67, Quantity::Temperature, Unit::C) \
|
X(ExternalTemperature,0x64,0x67, Quantity::Temperature, Unit::C) \
|
||||||
X(Pressure,0x68,0x6B, Quantity::Pressure, Unit::BAR) \
|
X(Pressure,0x68,0x6B, Quantity::Pressure, Unit::BAR) \
|
||||||
X(HeatCostAllocation,0x6E,0x6E, Quantity::HCA, Unit::HCA) \
|
X(HeatCostAllocation,0x6E,0x6E, Quantity::HCA, Unit::HCA) \
|
||||||
X(Date,0x6C,0x6C, Quantity::PointInTime, Unit::DateTimeLT) \
|
X(Date,0x6C,0x6C, Quantity::PointInTime, Unit::DateLT) \
|
||||||
X(DateTime,0x6D,0x6D, Quantity::PointInTime, Unit::DateTimeLT) \
|
X(DateTime,0x6D,0x6D, Quantity::PointInTime, Unit::DateTimeLT) \
|
||||||
X(EnergyMJ,0x08,0x0F, Quantity::Energy, Unit::MJ) \
|
X(EnergyMJ,0x08,0x0F, Quantity::Energy, Unit::MJ) \
|
||||||
X(EnergyWh,0x00,0x07, Quantity::Energy, Unit::KWH) \
|
X(EnergyWh,0x00,0x07, Quantity::Energy, Unit::KWH) \
|
||||||
|
@ -208,6 +208,7 @@ struct VIFCombinableRaw {
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VIFCombinable toVIFCombinable(const char *s);
|
||||||
VIFCombinable toVIFCombinable(int i);
|
VIFCombinable toVIFCombinable(int i);
|
||||||
const char *toString(VIFCombinable v);
|
const char *toString(VIFCombinable v);
|
||||||
|
|
||||||
|
@ -588,5 +589,6 @@ bool extractDVdate(std::map<std::string,std::pair<int,DVEntry>> *values,
|
||||||
|
|
||||||
|
|
||||||
const std::string &availableVIFRanges();
|
const std::string &availableVIFRanges();
|
||||||
|
const std::string &availableVIFCombinables();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1313,7 +1313,6 @@ void MeterCommonImplementation::processFieldExtractors(Telegram *t)
|
||||||
if (fi.hasMatcher() && fi.matches(dve))
|
if (fi.hasMatcher() && fi.matches(dve))
|
||||||
{
|
{
|
||||||
current_match_nr++;
|
current_match_nr++;
|
||||||
|
|
||||||
if (fi.matcher().index_nr != IndexNr(current_match_nr) &&
|
if (fi.matcher().index_nr != IndexNr(current_match_nr) &&
|
||||||
!fi.matcher().expectedToMatchAgainstMultipleEntries())
|
!fi.matcher().expectedToMatchAgainstMultipleEntries())
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,7 @@ using namespace std;
|
||||||
bool verbose_ = false;
|
bool verbose_ = false;
|
||||||
|
|
||||||
#define LIST_OF_TESTS \
|
#define LIST_OF_TESTS \
|
||||||
|
X(dynamic_loading)\
|
||||||
X(crc) \
|
X(crc) \
|
||||||
X(dvparser) \
|
X(dvparser) \
|
||||||
X(devices) \
|
X(devices) \
|
||||||
|
@ -2028,6 +2029,8 @@ LIST_OF_QUANTITIES
|
||||||
test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
||||||
test_si_convert(2211717, 2211717, Unit::NUMBER, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
test_si_convert(2211717, 2211717, Unit::NUMBER, "counter", Unit::COUNTER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
||||||
test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::NUMBER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
test_si_convert(2211717, 2211717, Unit::FACTOR, "counter", Unit::NUMBER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
||||||
|
test_si_convert(2211717, 2211717, Unit::PERCENTAGE, "counter", Unit::NUMBER, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
||||||
|
test_si_convert(2211717, 2211717, Unit::NUMBER, "counter", Unit::PERCENTAGE, "counter", Quantity::Dimensionless, &from_set, &to_set);
|
||||||
|
|
||||||
check_units_tested(from_set, to_set, Quantity::Dimensionless);
|
check_units_tested(from_set, to_set, Quantity::Dimensionless);
|
||||||
|
|
||||||
|
@ -2588,3 +2591,22 @@ void test_formulas_stringinterpolation()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_dynamic_loading()
|
||||||
|
{
|
||||||
|
VIFRange vr = toVIFRange("Date");
|
||||||
|
|
||||||
|
if (vr != VIFRange::Date)
|
||||||
|
{
|
||||||
|
printf("ERROR in dynamic loading got %s but expected %s!\n",
|
||||||
|
toString(vr), toString(VIFRange::Date));
|
||||||
|
}
|
||||||
|
|
||||||
|
vr = toVIFRange("DateTime");
|
||||||
|
|
||||||
|
if (vr != VIFRange::DateTime)
|
||||||
|
{
|
||||||
|
printf("ERROR in dynamic loading got %s but expected %s!\n",
|
||||||
|
toString(vr), toString(VIFRange::DateTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ using namespace std;
|
||||||
X(NUMBER, COUNTER, {vto=vfrom;}) \
|
X(NUMBER, COUNTER, {vto=vfrom;}) \
|
||||||
X(FACTOR, NUMBER, {vto=vfrom;}) \
|
X(FACTOR, NUMBER, {vto=vfrom;}) \
|
||||||
X(NUMBER, FACTOR, {vto=vfrom;}) \
|
X(NUMBER, FACTOR, {vto=vfrom;}) \
|
||||||
|
X(PERCENTAGE, NUMBER, {vto=vfrom;}) \
|
||||||
|
X(NUMBER, PERCENTAGE, {vto=vfrom;}) \
|
||||||
X(UnixTimestamp,DateTimeLT, {vto=vfrom; }) \
|
X(UnixTimestamp,DateTimeLT, {vto=vfrom; }) \
|
||||||
X(DateTimeLT,UnixTimestamp, {vto=vfrom; }) \
|
X(DateTimeLT,UnixTimestamp, {vto=vfrom; }) \
|
||||||
X(DateLT,UnixTimestamp, {vto=vfrom; }) \
|
X(DateLT,UnixTimestamp, {vto=vfrom; }) \
|
||||||
|
@ -122,6 +124,7 @@ using namespace std;
|
||||||
X(COUNTER, 1.0, SIExp()) \
|
X(COUNTER, 1.0, SIExp()) \
|
||||||
X(FACTOR, 1.0, SIExp()) \
|
X(FACTOR, 1.0, SIExp()) \
|
||||||
X(NUMBER, 1.0, SIExp()) \
|
X(NUMBER, 1.0, SIExp()) \
|
||||||
|
X(PERCENTAGE, 1.0, SIExp()) \
|
||||||
X(TXT, 1.0, SIExp()) \
|
X(TXT, 1.0, SIExp()) \
|
||||||
|
|
||||||
|
|
||||||
|
@ -830,7 +833,7 @@ const char *availableUnits()
|
||||||
if (available_units_[0]) return available_units_;
|
if (available_units_[0]) return available_units_;
|
||||||
|
|
||||||
#define X(n,suffix,sn,q,ln) if (Unit::n != Unit::Unknown) { \
|
#define X(n,suffix,sn,q,ln) if (Unit::n != Unit::Unknown) { \
|
||||||
strcat(available_units_, #suffix); strcat(available_units_, "\n"); \
|
strcat(available_units_, #suffix); strcat(available_units_, " "); \
|
||||||
assert(strlen(available_units_) < 1024); }
|
assert(strlen(available_units_) < 1024); }
|
||||||
LIST_OF_UNITS
|
LIST_OF_UNITS
|
||||||
#undef X
|
#undef X
|
||||||
|
|
Ładowanie…
Reference in New Issue