Skip to content

Commit b09ba9e

Browse files
geduxasPolarGoose
authored andcommitted
* Add OBIS which was missing for Lithuania meters
* Add negative value support
1 parent 62481cf commit b09ba9e

File tree

5 files changed

+98
-43
lines changed

5 files changed

+98
-43
lines changed

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ target_include_directories(bearssl SYSTEM PUBLIC "${bearsll_SOURCE_DIR}/inc" PRI
3434
# cmake_template for warnings and sanitizers
3535
FetchContent_Populate(
3636
cmake_template
37-
URL https://github.com/cpp-best-practices/cmake_template/archive/1015c6b88410df411c0cc0413e3e64c33d7a8331.zip
38-
URL_HASH SHA512=b7ad5b05eb21e5cb8159f3bd8b05615ee55489e5ab6a543bfe50ba05c8c8935106a6870b438c97ca3894108650a4a542a810707954cbe6b3beb9c6de82f1bde7)
37+
URL https://github.com/cpp-best-practices/cmake_template/archive/0a32e3ff530534f04d7a73817d563bb26eea1b5a.zip
38+
URL_HASH SHA512=4cab8e5f75054b46c131447e0b9b4f36239eed7c8e5c3a01a09f1454126ea4fc24a5426a4662966cd925673f25383220a56644cab7551cb70aa40870e4577c0b)
3939
include(${cmake_template_SOURCE_DIR}/cmake/CompilerWarnings.cmake)
4040
include(${cmake_template_SOURCE_DIR}/cmake/Sanitizers.cmake)
4141

572 KB
Binary file not shown.

src/dsmr_parser/fields.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ struct TimestampField : StringField<T, 13, 13> {};
5656
struct FixedValue {
5757
operator float() const noexcept { return val(); }
5858
float val() const noexcept { return static_cast<float>(_value) / 1000.0f; }
59-
uint32_t int_val() const noexcept { return _value; }
59+
int32_t int_val() const noexcept { return _value; }
6060

61-
uint32_t _value;
61+
int32_t _value;
6262
};
6363

6464
// Floating point numbers in the message never have more than 3 decimal
@@ -132,7 +132,7 @@ struct LastFixedField : public FixedField<T, _unit, _int_unit> {
132132
template <typename T, const char* _unit>
133133
struct IntField : ParsedField<T> {
134134
ParseResult<void> parse(const char* str, const char* end) {
135-
ParseResult<uint32_t> res = NumParser::parse(0, _unit, str, end);
135+
ParseResult<int32_t> res = NumParser::parse(0, _unit, str, end);
136136
if (!res.err) {
137137
auto& dst = static_cast<T*>(this)->val();
138138
using Dst = std::remove_reference_t<decltype(dst)>;
@@ -174,10 +174,10 @@ struct AveragedFixedField : public FixedField<T, _unit, _int_unit> {
174174
if (res.err)
175175
return res;
176176

177-
ParseResult<uint32_t> average;
178-
average.succeed(0u);
177+
ParseResult<int32_t> average;
178+
average.succeed(0);
179179
average.next = res.next;
180-
for (uint32_t i = 0; i < numberOfValues.result; i++) {
180+
for (int32_t i = 0; i < numberOfValues.result; i++) {
181181
// skip date (230201000000W)
182182
res = StringParser::parse_string(1, 20, average.next, end);
183183
if (res.err)
@@ -482,8 +482,7 @@ DEFINE_FIELD(apparent_return_power_l3, FixedValue, ObisId(1, 0, 70, 7, 0), Fixed
482482
// Active Demand Avg3 Plus in W resolution
483483
DEFINE_FIELD(active_demand_power, FixedValue, ObisId(1, 0, 1, 24, 0), FixedField, units::kW, units::W);
484484
// Active Demand Avg3 Net in W resolution
485-
// TODO: 1-0.16.24.0.255 can have negative value, this library is not ready for negative numbers.
486-
// DEFINE_FIELD(active_demand_net, int32_t, ObisId(1, 0, 16, 24, 0), IntField, units::kW);
485+
DEFINE_FIELD(active_demand_net, FixedValue, ObisId(1, 0, 16, 24, 0), FixedField, units::kW, units::W);
487486
// Active Demand Avg3 Absolute in W resolution
488487
DEFINE_FIELD(active_demand_abs, FixedValue, ObisId(1, 0, 15, 24, 0), FixedField, units::kW, units::W);
489488

@@ -572,5 +571,17 @@ DEFINE_FIELD(fw_core_checksum, std::string, ObisId(1, 0, 0, 2, 8), StringField,
572571
DEFINE_FIELD(fw_module_version, std::string, ObisId(1, 1, 0, 2, 0), StringField, 0, 96);
573572
DEFINE_FIELD(fw_module_checksum, std::string, ObisId(1, 1, 0, 2, 8), StringField, 0, 96);
574573

574+
// Instantaneous power factor
575+
DEFINE_FIELD(power_factor, FixedValue, ObisId(1, 0, 13, 7, 0), FixedField, units::none, units::none);
576+
// Instantaneous power factor in phase L1, L2, L3
577+
DEFINE_FIELD(power_factor_l1, FixedValue, ObisId(1, 0, 33, 7, 0), FixedField, units::none, units::none);
578+
DEFINE_FIELD(power_factor_l2, FixedValue, ObisId(1, 0, 53, 7, 0), FixedField, units::none, units::none);
579+
DEFINE_FIELD(power_factor_l3, FixedValue, ObisId(1, 0, 73, 7, 0), FixedField, units::none, units::none);
580+
// Minimum Power factor
581+
DEFINE_FIELD(min_power_factor, FixedValue, ObisId(1, 0, 13, 3, 0), FixedField, units::none, units::none);
582+
583+
// Measurement Period 3 for Instantaneous values
584+
DEFINE_FIELD(period_3_for_instantaneous_values, uint32_t, ObisId(1, 0, 0, 8, 2), IntField, units::s);
585+
575586
}
576587
}

src/dsmr_parser/parser.h

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ static constexpr char INVALID_NUMBER[] = "Invalid number";
105105
static constexpr char INVALID_UNIT[] = "Invalid unit";
106106

107107
struct NumParser final {
108-
static ParseResult<uint32_t> parse_float_or_int(const size_t max_decimals, const char* float_unit, const char* int_unit, const char* str, const char* end) {
108+
static ParseResult<int32_t> parse_float_or_int(const size_t max_decimals, const char* float_unit, const char* int_unit, const char* str, const char* end) {
109109
auto float_res = NumParser::parse(max_decimals, float_unit, str, end);
110110
if (!float_res.err)
111111
return float_res;
@@ -117,36 +117,41 @@ struct NumParser final {
117117
return float_res;
118118
}
119119

120-
static ParseResult<uint32_t> parse(size_t max_decimals, const char* unit, const char* str, const char* end) {
121-
ParseResult<uint32_t> res;
120+
static ParseResult<int32_t> parse(size_t max_decimals, const char* unit, const char* str, const char* end) {
121+
ParseResult<int32_t> res;
122122
if (str >= end || *str != '(')
123123
return res.fail("Missing (", str);
124124

125-
const char* num_start = str + 1; // Skip (
126-
const char* num_end = num_start;
125+
const char* cur_symbol = str + 1; // Skip (
127126

128-
uint32_t value = 0;
127+
bool negative = false;
128+
if (cur_symbol < end && *cur_symbol == '-') {
129+
negative = true;
130+
++cur_symbol;
131+
}
132+
133+
int32_t value = 0;
129134

130135
// Parse integer part
131-
while (num_end < end && !strchr("*.)", *num_end)) {
132-
if (*num_end < '0' || *num_end > '9')
133-
return res.fail(INVALID_NUMBER, num_end);
136+
while (cur_symbol < end && !strchr("*.)", *cur_symbol)) {
137+
if (*cur_symbol < '0' || *cur_symbol > '9')
138+
return res.fail(INVALID_NUMBER, cur_symbol);
134139
value *= 10;
135-
value += static_cast<uint32_t>(*num_end - '0');
136-
++num_end;
140+
value += (*cur_symbol - '0');
141+
++cur_symbol;
137142
}
138143

139144
// Parse decimal part, if any
140-
if (max_decimals && num_end < end && *num_end == '.') {
141-
++num_end;
145+
if (max_decimals && cur_symbol < end && *cur_symbol == '.') {
146+
++cur_symbol;
142147

143-
while (num_end < end && !strchr("*)", *num_end) && max_decimals) {
148+
while (cur_symbol < end && !strchr("*)", *cur_symbol) && max_decimals) {
144149
max_decimals--;
145-
if (*num_end < '0' || *num_end > '9')
146-
return res.fail(INVALID_NUMBER, num_end);
150+
if (*cur_symbol < '0' || *cur_symbol > '9')
151+
return res.fail(INVALID_NUMBER, cur_symbol);
147152
value *= 10;
148-
value += static_cast<uint32_t>(*num_end - '0');
149-
++num_end;
153+
value += (*cur_symbol - '0');
154+
++cur_symbol;
150155
}
151156
}
152157

@@ -156,30 +161,32 @@ struct NumParser final {
156161

157162
// Workaround for https://github.com/matthijskooijman/arduino-dsmr/issues/50
158163
// If value is 0, then we allow missing unit.
159-
if (unit && *unit && (num_end >= end || (*num_end != '*' && *num_end != '.')) && value == 0) {
160-
num_end = std::find(num_end, end, ')');
164+
if (unit && *unit && (cur_symbol >= end || (*cur_symbol != '*' && *cur_symbol != '.')) && value == 0) {
165+
cur_symbol = std::find(cur_symbol, end, ')');
161166
}
162167

163-
// If a unit was passed, check that the unit in the messages
164-
// messages the unit passed.
168+
// If a unit was passed, check that the unit in the message matches the unit passed.
165169
else if (unit && *unit) {
166-
if (num_end >= end || *num_end != '*')
167-
return res.fail("Missing unit", num_end);
168-
const char* unit_start = ++num_end; // skip *
169-
while (num_end < end && *num_end != ')' && *unit) {
170+
if (cur_symbol >= end || *cur_symbol != '*')
171+
return res.fail("Missing unit", cur_symbol);
172+
const char* unit_start = ++cur_symbol; // skip *
173+
while (cur_symbol < end && *cur_symbol != ')' && *unit) {
170174
// Next character in units do not match?
171-
if (std::tolower(static_cast<unsigned char>(*num_end++)) != std::tolower(static_cast<unsigned char>(*unit++)))
175+
if (std::tolower(static_cast<unsigned char>(*cur_symbol++)) != std::tolower(static_cast<unsigned char>(*unit++)))
172176
return res.fail(INVALID_UNIT, unit_start);
173177
}
174178
// At the end of the message unit, but not the passed unit?
175179
if (*unit)
176180
return res.fail(INVALID_UNIT, unit_start);
177181
}
178182

179-
if (num_end >= end || *num_end != ')')
180-
return res.fail("Extra data", num_end);
183+
if (cur_symbol >= end || *cur_symbol != ')')
184+
return res.fail("Extra data", cur_symbol);
185+
186+
if (negative)
187+
value = -value;
181188

182-
return res.succeed(value).until(num_end + 1); // Skip )
189+
return res.succeed(value).until(cur_symbol + 1); // Skip )
183190
}
184191
};
185192

tests/parser_test.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,14 @@ TEST_CASE("Should parse all fields in the DSMR message correctly") {
4949
"1-1:0.2.0((ER12)\r\n"
5050
"1-1:0.2.8(ER13))\r\n"
5151
"0-1:24.4.0(1)\r\n"
52-
"!96c9\r\n";
52+
"1-0:16.24.0(-03.618*kW)\r\n"
53+
"1-0:13.7.0(0.998)\r\n"
54+
"1-0:33.7.0(0.975)\r\n"
55+
"1-0:53.7.0(0.963)\r\n"
56+
"1-0:73.7.0(0.987)\r\n"
57+
"1-0:13.3.0(0.000)\r\n"
58+
"1-0:0.8.2(00900*s)\r\n"
59+
"!\r\n";
5360

5461
ParsedData<
5562
/* String */ identification,
@@ -104,10 +111,17 @@ TEST_CASE("Should parse all fields in the DSMR message correctly") {
104111
/* String */ fw_core_version,
105112
/* String */ fw_core_checksum,
106113
/* String */ fw_module_version,
107-
/* String */ fw_module_checksum>
114+
/* String */ fw_module_checksum,
115+
/* FixedValue */ active_demand_net,
116+
/* FixedValue */ power_factor,
117+
/* FixedValue */ power_factor_l1,
118+
/* FixedValue */ power_factor_l2,
119+
/* FixedValue */ power_factor_l3,
120+
/* FixedValue */ min_power_factor,
121+
/* FixedValue */ period_3_for_instantaneous_values>
108122
data;
109123

110-
auto res = P1Parser::parse(data, msg, std::size(msg), true);
124+
auto res = P1Parser::parse(data, msg, std::size(msg), /* unknown_error */ true, /* check_crc */ false);
111125
REQUIRE(res.err == nullptr);
112126

113127
// Print all values
@@ -146,6 +160,29 @@ TEST_CASE("Should parse all fields in the DSMR message correctly") {
146160
REQUIRE(data.fw_core_checksum == "1.0.smth smth-123");
147161
REQUIRE(data.fw_module_version == "(ER12");
148162
REQUIRE(data.fw_module_checksum == "ER13)");
163+
REQUIRE(data.active_demand_net == -3.618f);
164+
REQUIRE(data.power_factor == 0.998f);
165+
REQUIRE(data.power_factor_l1 == 0.975f);
166+
REQUIRE(data.power_factor_l2 == 0.963f);
167+
REQUIRE(data.power_factor_l3 == 0.987f);
168+
REQUIRE(data.min_power_factor == 0.0f);
169+
REQUIRE(data.period_3_for_instantaneous_values == 900);
170+
}
171+
172+
TEST_CASE("Should calculate CRC correctly") {
173+
const auto& msg = "/KFM5KAIFA-METER\r\n"
174+
"\r\n"
175+
"1-0:1.8.1(000671.578*kWh)\r\n"
176+
"1-0:1.7.0(00.318*kW)\r\n"
177+
"!1e1D\r\n";
178+
179+
ParsedData<
180+
/* String */ identification,
181+
/* FixedValue */ power_delivered>
182+
data;
183+
184+
auto res = P1Parser::parse(data, msg, std::size(msg), /* unknown_error */ false, /* check_crc */ true);
185+
REQUIRE(res.err == nullptr);
149186
}
150187

151188
TEST_CASE("Should report an error if the crc has incorrect format") {

0 commit comments

Comments
 (0)