Skip to content

Make the parser compile for Windows and Linux machines #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/build/
.idea/
/*build*/
33 changes: 33 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
cmake_minimum_required (VERSION 3.20)
project(arduino-dsmr-test LANGUAGES CXX)

## Download Catch2 test framework
file(DOWNLOAD
https://github.com/catchorg/Catch2/releases/download/v3.4.0/catch_amalgamated.hpp
${CMAKE_BINARY_DIR}/catch2/catch_amalgamated.hpp
EXPECTED_MD5 b9e33e9a8198294a87b64dcf641dee16)
file(DOWNLOAD
https://github.com/catchorg/Catch2/releases/download/v3.4.0/catch_amalgamated.cpp
${CMAKE_BINARY_DIR}/catch2/catch_amalgamated.cpp
EXPECTED_MD5 b4ee03064bf6be8f41313df1649ff7a9)

add_library(catch2
${CMAKE_BINARY_DIR}/catch2/catch_amalgamated.hpp
${CMAKE_BINARY_DIR}/catch2/catch_amalgamated.cpp)
target_compile_features(catch2 PRIVATE cxx_std_23)
target_include_directories(catch2 INTERFACE ${CMAKE_BINARY_DIR}/catch2)
target_compile_options(catch2 PRIVATE -w)

# Configure arduino_dsmr_test project
file(GLOB_RECURSE arduino_dsmr_test_src_files CONFIGURE_DEPENDS "test/*.h" "test/*.cpp")
add_executable(arduino_dsmr_test
${arduino_dsmr_test_src_files}
${CMAKE_SOURCE_DIR}/src/dsmr/util.h
${CMAKE_SOURCE_DIR}/src/dsmr/fields.h
${CMAKE_SOURCE_DIR}/src/dsmr/parser.h
${CMAKE_SOURCE_DIR}/src/dsmr/crc16.h
${CMAKE_SOURCE_DIR}/src/dsmr/fields.cpp)
target_include_directories(arduino_dsmr_test PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_precompile_headers(arduino_dsmr_test PRIVATE ${CMAKE_SOURCE_DIR}/test/precompiled_header.h)
target_link_libraries(arduino_dsmr_test catch2)
target_compile_options(arduino_dsmr_test PRIVATE -Wall -Wextra)
34 changes: 17 additions & 17 deletions src/dsmr/fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct ParsedField {
template <typename T, size_t minlen, size_t maxlen>
struct StringField : ParsedField<T> {
ParseResult<void> parse(const char *str, const char *end) {
ParseResult<String> res = StringParser::parse_string(minlen, maxlen, str, end);
ParseResult<std::string> res = StringParser::parse_string(minlen, maxlen, str, end);
if (!res.err)
static_cast<T*>(this)->val() = res.result;
return res;
Expand Down Expand Up @@ -102,7 +102,7 @@ struct FixedField : ParsedField<T> {
};

struct TimestampedFixedValue : public FixedValue {
String timestamp;
std::string timestamp;
};

// Some numerical values are prefixed with a timestamp. This is simply
Expand All @@ -111,7 +111,7 @@ template <typename T, const char *_unit, const char *_int_unit>
struct TimestampedFixedField : public FixedField<T, _unit, _int_unit> {
ParseResult<void> parse(const char *str, const char *end) {
// First, parse timestamp
ParseResult<String> res = StringParser::parse_string(13, 13, str, end);
ParseResult<std::string> res = StringParser::parse_string(13, 13, str, end);
if (res.err)
return res;

Expand Down Expand Up @@ -182,8 +182,8 @@ struct NameConverter {
operator const __FlashStringHelper*() const { return reinterpret_cast<const __FlashStringHelper*>(&FieldT::name_progmem); }
};

#define DEFINE_FIELD(fieldname, value_t, obis, field_t, field_args...) \
struct fieldname : field_t<fieldname, ##field_args> { \
#define DEFINE_FIELD(fieldname, value_t, obis, field_t, ...) \
struct fieldname : field_t<fieldname, ##__VA_ARGS__> { \
value_t fieldname; \
bool fieldname ## _present = false; \
static constexpr ObisId id = obis; \
Expand All @@ -201,16 +201,16 @@ struct NameConverter {

/* Meter identification. This is not a normal field, but a
* specially-formatted first line of the message */
DEFINE_FIELD(identification, String, ObisId(255, 255, 255, 255, 255, 255), RawField);
DEFINE_FIELD(identification, std::string, ObisId(255, 255, 255, 255, 255, 255), RawField);

/* Version information for P1 output */
DEFINE_FIELD(p1_version, String, ObisId(1, 3, 0, 2, 8), StringField, 2, 2);
DEFINE_FIELD(p1_version, std::string, ObisId(1, 3, 0, 2, 8), StringField, 2, 2);

/* Date-time stamp of the P1 message */
DEFINE_FIELD(timestamp, String, ObisId(0, 0, 1, 0, 0), TimestampField);
DEFINE_FIELD(timestamp, std::string, ObisId(0, 0, 1, 0, 0), TimestampField);

/* Equipment identifier */
DEFINE_FIELD(equipment_id, String, ObisId(0, 0, 96, 1, 1), StringField, 0, 96);
DEFINE_FIELD(equipment_id, std::string, ObisId(0, 0, 96, 1, 1), StringField, 0, 96);

/* Meter Reading electricity delivered to client (Tariff 1) in 0,001 kWh */
DEFINE_FIELD(energy_delivered_tariff1, FixedValue, ObisId(1, 0, 1, 8, 1), FixedField, units::kWh, units::Wh);
Expand All @@ -224,7 +224,7 @@ DEFINE_FIELD(energy_returned_tariff2, FixedValue, ObisId(1, 0, 2, 8, 2), FixedFi
/* Tariff indicator electricity. The tariff indicator can also be used
* to switch tariff dependent loads e.g boilers. This is the
* responsibility of the P1 user */
DEFINE_FIELD(electricity_tariff, String, ObisId(0, 0, 96, 14, 0), StringField, 4, 4);
DEFINE_FIELD(electricity_tariff, std::string, ObisId(0, 0, 96, 14, 0), StringField, 4, 4);

/* Actual electricity power delivered (+P) in 1 Watt resolution */
DEFINE_FIELD(power_delivered, FixedValue, ObisId(1, 0, 1, 7, 0), FixedField, units::kW, units::W);
Expand All @@ -243,7 +243,7 @@ DEFINE_FIELD(electricity_failures, uint32_t, ObisId(0, 0, 96, 7, 21), IntField,
DEFINE_FIELD(electricity_long_failures, uint32_t, ObisId(0, 0, 96, 7, 9), IntField, units::none);

/* Power Failure Event Log (long power failures) */
DEFINE_FIELD(electricity_failure_log, String, ObisId(1, 0, 99, 97, 0), RawField);
DEFINE_FIELD(electricity_failure_log, std::string, ObisId(1, 0, 99, 97, 0), RawField);

/* Number of voltage sags in phase L1 */
DEFINE_FIELD(electricity_sags_l1, uint32_t, ObisId(1, 0, 32, 32, 0), IntField, units::none);
Expand All @@ -261,10 +261,10 @@ DEFINE_FIELD(electricity_swells_l3, uint32_t, ObisId(1, 0, 72, 36, 0), IntField,

/* Text message codes: numeric 8 digits (Note: Missing from 5.0 spec)
* */
DEFINE_FIELD(message_short, String, ObisId(0, 0, 96, 13, 1), StringField, 0, 16);
DEFINE_FIELD(message_short, std::string, ObisId(0, 0, 96, 13, 1), StringField, 0, 16);
/* Text message max 2048 characters (Note: Spec says 1024 in comment and
* 2048 in format spec, so we stick to 2048). */
DEFINE_FIELD(message_long, String, ObisId(0, 0, 96, 13, 0), StringField, 0, 2048);
DEFINE_FIELD(message_long, std::string, ObisId(0, 0, 96, 13, 0), StringField, 0, 2048);

/* Instantaneous voltage L1 in 0.1V resolution (Note: Spec says V
* resolution in comment, but 0.1V resolution in format spec. Added in
Expand Down Expand Up @@ -305,7 +305,7 @@ DEFINE_FIELD(power_returned_l3, FixedValue, ObisId(1, 0, 62, 7, 0), FixedField,
DEFINE_FIELD(gas_device_type, uint16_t, ObisId(0, GAS_MBUS_ID, 24, 1, 0), IntField, units::none);

/* Equipment identifier (Gas) */
DEFINE_FIELD(gas_equipment_id, String, ObisId(0, GAS_MBUS_ID, 96, 1, 0), StringField, 0, 96);
DEFINE_FIELD(gas_equipment_id, std::string, ObisId(0, GAS_MBUS_ID, 96, 1, 0), StringField, 0, 96);

/* Valve position Gas (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
DEFINE_FIELD(gas_valve_position, uint8_t, ObisId(0, GAS_MBUS_ID, 24, 4, 0), IntField, units::none);
Expand All @@ -320,7 +320,7 @@ DEFINE_FIELD(gas_delivered, TimestampedFixedValue, ObisId(0, GAS_MBUS_ID, 24, 2,
DEFINE_FIELD(thermal_device_type, uint16_t, ObisId(0, THERMAL_MBUS_ID, 24, 1, 0), IntField, units::none);

/* Equipment identifier (Thermal: heat or cold) */
DEFINE_FIELD(thermal_equipment_id, String, ObisId(0, THERMAL_MBUS_ID, 96, 1, 0), StringField, 0, 96);
DEFINE_FIELD(thermal_equipment_id, std::string, ObisId(0, THERMAL_MBUS_ID, 96, 1, 0), StringField, 0, 96);

/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
DEFINE_FIELD(thermal_valve_position, uint8_t, ObisId(0, THERMAL_MBUS_ID, 24, 4, 0), IntField, units::none);
Expand All @@ -334,7 +334,7 @@ DEFINE_FIELD(thermal_delivered, TimestampedFixedValue, ObisId(0, THERMAL_MBUS_ID
DEFINE_FIELD(water_device_type, uint16_t, ObisId(0, WATER_MBUS_ID, 24, 1, 0), IntField, units::none);

/* Equipment identifier (Thermal: heat or cold) */
DEFINE_FIELD(water_equipment_id, String, ObisId(0, WATER_MBUS_ID, 96, 1, 0), StringField, 0, 96);
DEFINE_FIELD(water_equipment_id, std::string, ObisId(0, WATER_MBUS_ID, 96, 1, 0), StringField, 0, 96);

/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
DEFINE_FIELD(water_valve_position, uint8_t, ObisId(0, WATER_MBUS_ID, 24, 4, 0), IntField, units::none);
Expand All @@ -348,7 +348,7 @@ DEFINE_FIELD(water_delivered, TimestampedFixedValue, ObisId(0, WATER_MBUS_ID, 24
DEFINE_FIELD(slave_device_type, uint16_t, ObisId(0, SLAVE_MBUS_ID, 24, 1, 0), IntField, units::none);

/* Equipment identifier (Thermal: heat or cold) */
DEFINE_FIELD(slave_equipment_id, String, ObisId(0, SLAVE_MBUS_ID, 96, 1, 0), StringField, 0, 96);
DEFINE_FIELD(slave_equipment_id, std::string, ObisId(0, SLAVE_MBUS_ID, 96, 1, 0), StringField, 0, 96);

/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */
DEFINE_FIELD(slave_valve_position, uint8_t, ObisId(0, SLAVE_MBUS_ID, 24, 4, 0), IntField, units::none);
Expand Down
4 changes: 2 additions & 2 deletions src/dsmr/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ struct ParsedData<T, Ts...> : public T, ParsedData<Ts...> {


struct StringParser {
static ParseResult<String> parse_string(size_t min, size_t max, const char *str, const char *end) {
ParseResult<String> res;
static ParseResult<std::string> parse_string(size_t min, size_t max, const char *str, const char *end) {
ParseResult<std::string> res;
if (str >= end || *str != '(')
return res.fail(F("Missing ("), str);

Expand Down
22 changes: 8 additions & 14 deletions src/dsmr/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@
#ifndef DSMR_INCLUDE_UTIL_H
#define DSMR_INCLUDE_UTIL_H

#ifdef ARDUINO_ARCH_ESP8266
#define DSMR_PROGMEM
#else
#define DSMR_PROGMEM PROGMEM
#endif
#include <string>

#include <Arduino.h>
#define DSMR_PROGMEM
#define F(str) str
using __FlashStringHelper = char;

namespace dsmr {

Expand All @@ -51,12 +49,8 @@ inline unsigned int lengthof(const T (&)[sz]) { return sz; }
// This appends the given number of bytes from the given C string to the
// given Arduino string, without requiring a trailing NUL.
// Requires that there _is_ room for nul-termination
static void concat_hack(String& s, const char *append, size_t n) {
// Add null termination. Inefficient, but it works...
char buf[n + 1];
memcpy(buf, append, n);
buf[n] = 0;
s.concat(buf);
static void concat_hack(std::string& s, const char *append, size_t n) {
s.append(append, n);
}

/**
Expand Down Expand Up @@ -137,8 +131,8 @@ struct ParseResult : public _ParseResult<ParseResult<T>, T> {
* characters in the total parsed string. These are needed to properly
* limit the context output.
*/
String fullError(const char* start, const char* end) const {
String res;
std::string fullError(const char* start, const char* end) const {
std::string res;
if (this->ctx && start && end) {
// Find the entire line surrounding the context
const char *line_end = this->ctx;
Expand Down
Loading