From e4d3ec1e7e5a90e31004e3bf761b3a8bb6d7810c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 1 Aug 2021 23:12:52 +0200 Subject: [PATCH] Add field_ prefix which is equivalent to json_ prefix. --- CHANGES | 3 ++ README.md | 6 ++-- simulations/simulation_additional_json.txt | 2 +- src/cmdline.cc | 18 ++++++---- src/config.cc | 40 ++++++++++++++++------ src/config.h | 5 ++- src/main.cc | 13 ++++--- src/meters.cc | 18 ++++++++-- src/meters.h | 3 +- src/meters_common_implementation.h | 3 +- src/printer.cc | 5 +-- src/printer.h | 2 +- tests/config6/etc/wmbusmeters.conf | 6 ++-- tests/config6/etc/wmbusmeters.d/Water | 1 + tests/test_additional_json.sh | 12 +++++-- 15 files changed, 100 insertions(+), 37 deletions(-) diff --git a/CHANGES b/CHANGES index 37126fb..102f086 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,7 @@ +Added the field timestamp_ut to print the existing timestamp field +using unix timestamp seconds since 1970-01-01. + Daniel Glaser added support for the Aventies water meter! Thanks Daniel! Mblnk added support for Qundis QWater5.5. Thanks Mblnk! diff --git a/README.md b/README.md index e2751ea..bbb8a7f 100644 --- a/README.md +++ b/README.md @@ -126,11 +126,11 @@ You can add the static json data `"address":"RoadenRd 456","city":"Stockholm"` t wmbusmeters.conf setting: ```ini -json_address=RoadenRd 456 -json_city=Stockholm +field_address=RoadenRd 456 +field_city=Stockholm ``` -If you add `json_floor=5` to the meter file `MyTapWater`, then you can have the meter tailored static json `"floor":"5"` added to telegrams handled by that particular meter. +If you add `field_floor=5` to the meter file `MyTapWater`, then you can have the meter tailored static json `"floor":"5"` added to telegrams handled by that particular meter. (The old prefix json_ still works.) If you are running on a Raspberry PI with flash storage and you relay the data to another computer using a shell command (`mosquitto_pub` or `curl` or similar) then you might want to remove `meterfiles` and `meterfilesaction` to minimize the writes to the local flash file system. diff --git a/simulations/simulation_additional_json.txt b/simulations/simulation_additional_json.txt index b0e4bce..732d6fa 100644 --- a/simulations/simulation_additional_json.txt +++ b/simulations/simulation_additional_json.txt @@ -1,2 +1,2 @@ telegram=|2A442D2C998734761B168D2091D37CAC21576C78|02FF207100041308190000441308190000615B7F616713| -{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z","floor":"5","address":"RoodRd 42"} +{"media":"cold water","meter":"multical21","name":"MyTapWater","id":"76348799","total_m3":6.408,"target_m3":6.408,"max_flow_m3h":0,"flow_temperature_c":127,"external_temperature_c":19,"current_status":"DRY","time_dry":"22-31 days","time_reversed":"","time_leaking":"","time_bursting":"","timestamp":"1111-11-11T11:11:11Z","floor":"5","address":"RoodRd 42","city":"Stockholm"} diff --git a/src/cmdline.cc b/src/cmdline.cc index 6b09b63..0756a0a 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -440,16 +440,22 @@ shared_ptr parseCommandLine(int argc, char **argv) { i++; continue; } - if (!strncmp(argv[i], "--json_", 7)) + if (!strncmp(argv[i], "--json_", 7) || + !strncmp(argv[i], "--field_", 8)) { // For example: --json_floor=42 - string json = string(argv[i]+7); - if (json == "") { - error("The json command cannot be empty.\n"); + // or --field_floor=42 + // they are equivalent. + int off = 7; + if (!strncmp(argv[i], "--field_", 8)) { off = 8; } + + string extra_constant_field = string(argv[i]+off); + if (extra_constant_field == "") { + error("The extra constant field command cannot be empty.\n"); } // The extra "floor"="42" will be pushed to the json. - debug("Added json %s\n", json.c_str()); - c->jsons.push_back(json); + debug("Added extra constant field %s\n", extra_constant_field.c_str()); + c->extra_constant_fields.push_back(extra_constant_field); i++; continue; } diff --git a/src/config.cc b/src/config.cc index f780f21..7821edb 100644 --- a/src/config.cc +++ b/src/config.cc @@ -58,7 +58,7 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) int bps {}; vector telegram_shells; vector alarm_shells; - vector jsons; + vector extra_constant_fields; debug("(config) loading meter file %s\n", file.c_str()); for (;;) { @@ -118,10 +118,13 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) alarm_shells.push_back(p.second); } else - if (startsWith(p.first, "json_")) + if (startsWith(p.first, "json_") || + startsWith(p.first, "field_")) { - string keyvalue = p.first.substr(5)+"="+p.second; - jsons.push_back(keyvalue); + int off = 5; + if (startsWith(p.first, "field_")) { off = 6; } + string keyvalue = p.first.substr(off)+"="+p.second; + extra_constant_fields.push_back(keyvalue); } else warning("Found invalid key \"%s\" in meter config file\n", p.first.c_str()); @@ -182,7 +185,7 @@ void parseMeterConfig(Configuration *c, vector &buf, string file) } if (use) { vector ids = splitMatchExpressions(id); - c->meters.push_back(MeterInfo(bus, name, mt, "", ids, key, modes, bps, telegram_shells, jsons)); + c->meters.push_back(MeterInfo(bus, name, mt, "", ids, key, modes, bps, telegram_shells, extra_constant_fields)); } return; @@ -562,6 +565,19 @@ void handleSelectedFields(Configuration *c, string s) } } +void handleAddedFields(Configuration *c, string s) +{ + char buf[s.length()+1]; + strcpy(buf, s.c_str()); + char *saveptr {}; + const char *tok = strtok_r(buf, ",", &saveptr); + while (tok != NULL) + { + c->added_fields.push_back(tok); + tok = strtok_r(NULL, ",", &saveptr); + } +} + void handleShell(Configuration *c, string cmdline) { c->telegram_shells.push_back(cmdline); @@ -572,9 +588,9 @@ void handleAlarmShell(Configuration *c, string cmdline) c->alarm_shells.push_back(cmdline); } -void handleJson(Configuration *c, string json) +void handleExtraConstantField(Configuration *c, string field) { - c->jsons.push_back(json); + c->extra_constant_fields.push_back(field); } shared_ptr loadConfiguration(string root, string device_override, string listento_override) @@ -620,14 +636,18 @@ shared_ptr loadConfiguration(string root, string device_override, else if (p.first == "addconversions") handleConversions(c, p.second); else if (p.first == "logtimestamps") handleLogTimestamps(c, p.second); else if (p.first == "selectfields") handleSelectedFields(c, p.second); + else if (p.first == "addfields") handleAddedFields(c, p.second); else if (p.first == "shell") handleShell(c, p.second); else if (p.first == "resetafter") handleResetAfter(c, p.second); else if (p.first == "alarmshell") handleAlarmShell(c, p.second); - else if (startsWith(p.first, "json_")) + else if (startsWith(p.first, "json_") || + startsWith(p.first, "field_")) { - string s = p.first.substr(5); + int off = 5; + if (startsWith(p.first, "field_")) { off = 6; } + string s = p.first.substr(off); string keyvalue = s+"="+p.second; - handleJson(c, keyvalue); + handleExtraConstantField(c, keyvalue); } else { diff --git a/src/config.h b/src/config.h index 45b1413..024b2ef 100644 --- a/src/config.h +++ b/src/config.h @@ -110,8 +110,10 @@ struct Configuration bool no_init {}; std::vector conversions; std::vector selected_fields; + std::vector added_fields; std::vector meters; - std::vector jsons; // Additional jsons to always add. + std::vector extra_constant_fields; // Additional constant fields to always add to json. + // These extra constant fields can also be part of selected with selectfields or added with addfields. ~Configuration() = default; }; @@ -120,6 +122,7 @@ shared_ptr loadConfiguration(string root, string device_override, void handleConversions(Configuration *c, string s); void handleSelectedFields(Configuration *c, string s); +void handleAddedFields(Configuration *c, string s); bool handleDevice(Configuration *c, string devicefile); enum class LinkModeCalculationResultType diff --git a/src/main.cc b/src/main.cc index 459b07a..429b04a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -187,8 +187,9 @@ void list_shell_envs(Configuration *config, string meter_driver) &ignore2, config->separator, &ignore3, &envs, - &config->jsons, - &config->selected_fields); + &config->extra_constant_fields, + &config->selected_fields, + &config->added_fields); for (auto &e : envs) { @@ -219,7 +220,11 @@ void list_fields(Configuration *config, string meter_driver) string meterr = padLeft("meter", width); printf("%s Meter driver.\n", meterr.c_str()); string timestamp = padLeft("timestamp", width); - printf("%s Timestamp when wmbusmeters received the telegram.\n", timestamp.c_str()); + printf("%s Timestamp when wmbusmeters received the telegram. Local time for hr/fields UTC for json.\n", timestamp.c_str()); + string timestamp_ut = padLeft("timestamp_ut", width); + printf("%s Unix timestamp when wmbusmeters received the telegram.\n", timestamp_ut.c_str()); + string timestamp_lt = padLeft("timestamp_l", width); + printf("%s Unix timestamp when wmbusmeters received the telegram.\n", timestamp_ut.c_str()); string device = padLeft("device", width); printf("%s The wmbus device that received the telegram.\n", device.c_str()); string rssi = padLeft("rssi_dbm", width); @@ -401,7 +406,7 @@ bool start(Configuration *config) meter_manager_->whenMeterUpdated( [&](Telegram *t,Meter *meter) { - printer_->print(t, meter, &config->jsons, &config->selected_fields); + printer_->print(t, meter, &config->extra_constant_fields, &config->selected_fields, &config->added_fields); oneshot_check(config, t, meter); } ); diff --git a/src/meters.cc b/src/meters.cc index ba54f2a..82fc069 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -404,7 +404,7 @@ string MeterCommonImplementation::unixTimestampOfUpdate() { char ut[40]; memset(ut, 0, sizeof(ut)); - sprintf(ut, "%zu", datetime_of_update_); + snprintf(ut, sizeof(ut)-1, "%zu", datetime_of_update_); return string(ut); } @@ -650,6 +650,16 @@ string concatFields(Meter *m, Telegram *t, char c, vector &prints, vector s += m->datetimeOfUpdateHumanReadable() + c; continue; } + if (field == "timestamp_lt") + { + s += m->datetimeOfUpdateHumanReadable() + c; + continue; + } + if (field == "timestamp_utc") + { + s += m->datetimeOfUpdateRobot() + c; + continue; + } if (field == "timestamp_ut") { s += m->unixTimestampOfUpdate() + c; @@ -767,7 +777,8 @@ void MeterCommonImplementation::printMeter(Telegram *t, string *json, vector *envs, vector *more_json, - vector *selected_fields) + vector *selected_fields, + vector *added_fields) { *human_readable = concatFields(this, t, '\t', prints_, conversions_, true, selected_fields); *fields = concatFields(this, t, separator, prints_, conversions_, false, selected_fields); @@ -853,6 +864,9 @@ void MeterCommonImplementation::printMeter(Telegram *t, envs->push_back(string("METER_MEDIA=")+media); envs->push_back(string("METER_TYPE=")+meterDriver()); envs->push_back(string("METER_TIMESTAMP=")+datetimeOfUpdateRobot()); + envs->push_back(string("METER_TIMESTAMP_UTC=")+datetimeOfUpdateRobot()); + envs->push_back(string("METER_TIMESTAMP_UT=")+unixTimestampOfUpdate()); + envs->push_back(string("METER_TIMESTAMP_LT=")+datetimeOfUpdateHumanReadable()); if (t->about.device != "") { envs->push_back(string("METER_DEVICE=")+t->about.device); diff --git a/src/meters.h b/src/meters.h index 575ea41..38c2f6a 100644 --- a/src/meters.h +++ b/src/meters.h @@ -230,7 +230,8 @@ struct Meter string *json, vector *envs, vector *more_json, - vector *selected_fields) = 0; + vector *selected_fields, + vector *added_fields) = 0; // The handleTelegram expects an input_frame where the DLL crcs have been removed. // Returns true of this meter handled this telegram! diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index 1cf01ab..e3fe748 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -89,7 +89,8 @@ protected: string *json, vector *envs, vector *more_json, // Add this json "key"="value" strings. - vector *selected_fields); // Only print these fields. Json always everything. + vector *selected_fields, // Only print these fields. Json always everything. + vector *added_fields); // Added these fields for printing. virtual void processContent(Telegram *t) = 0; diff --git a/src/printer.cc b/src/printer.cc index ffde9d1..0988f08 100644 --- a/src/printer.cc +++ b/src/printer.cc @@ -42,13 +42,14 @@ Printer::Printer(bool json, bool fields, char separator, void Printer::print(Telegram *t, Meter *meter, vector *more_json, - vector *selected_fields) + vector *selected_fields, + vector *added_fields) { string human_readable, fields, json; vector envs; bool printed = false; - meter->printMeter(t, &human_readable, &fields, separator_, &json, &envs, more_json, selected_fields); + meter->printMeter(t, &human_readable, &fields, separator_, &json, &envs, more_json, selected_fields, added_fields); if (shell_cmdlines_.size() > 0 || meter->shellCmdlines().size() > 0) { printShells(meter, envs); diff --git a/src/printer.h b/src/printer.h index fa1ec99..4c69a75 100644 --- a/src/printer.h +++ b/src/printer.h @@ -32,7 +32,7 @@ struct Printer { MeterFileNaming naming, MeterFileTimestamp timestamp); - void print(Telegram *t, Meter *meter, vector *more_json, vector *selected_fields); + void print(Telegram *t, Meter *meter, vector *more_json, vector *selected_fields, vector *added_fields); private: diff --git a/tests/config6/etc/wmbusmeters.conf b/tests/config6/etc/wmbusmeters.conf index ec8b1ae..16dfe21 100644 --- a/tests/config6/etc/wmbusmeters.conf +++ b/tests/config6/etc/wmbusmeters.conf @@ -2,5 +2,7 @@ loglevel=debug device=must_be_overriden logtelegrams=false format=json -shell=echo METER =="$METER_JSON"== =="$METER_address"== =="$METER_floor"== > /tmp/wmbusmeters_meter_additional_json_test -json_address=RoodRd 42 \ No newline at end of file +shell=echo METER =="$METER_JSON"== =="$METER_address"== =="$METER_city"== =="$METER_floor"== =="$METER_elevator"== > /tmp/wmbusmeters_meter_additional_json_test +# The json_ prefix and field_ prefix are equivalent. +json_address=RoodRd 42 +field_city=Stockholm \ No newline at end of file diff --git a/tests/config6/etc/wmbusmeters.d/Water b/tests/config6/etc/wmbusmeters.d/Water index 980de39..1cbd4d9 100644 --- a/tests/config6/etc/wmbusmeters.d/Water +++ b/tests/config6/etc/wmbusmeters.d/Water @@ -3,3 +3,4 @@ type=supercom587 id=12345678 key= json_floor=5 +field_elevator=ABC diff --git a/tests/test_additional_json.sh b/tests/test_additional_json.sh index 936e6bb..fb2b5ce 100755 --- a/tests/test_additional_json.sh +++ b/tests/test_additional_json.sh @@ -8,7 +8,7 @@ TESTNAME="Test additional json from cmdline" TESTRESULT="ERROR" cat simulations/simulation_additional_json.txt | grep '^{' > $TEST/test_expected.txt -$PROG --format=json --json_floor=5 --json_address="RoodRd 42" simulations/simulation_additional_json.txt \ +$PROG --format=json --json_floor=5 --json_address="RoodRd 42" --field_city="Stockholm" simulations/simulation_additional_json.txt \ MyTapWater multical21 76348799 "" \ > $TEST/test_output.txt 2> $TEST/test_stderr.txt @@ -41,6 +41,9 @@ METER_NAME METER_MEDIA METER_TYPE METER_TIMESTAMP +METER_TIMESTAMP_UTC +METER_TIMESTAMP_UT +METER_TIMESTAMP_LT METER_DEVICE METER_RSSI_DBM METER_TOTAL_M3 @@ -69,16 +72,19 @@ if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi TESTNAME="Test additional json from wmbusmeters.conf and from meter file" TESTRESULT="ERROR" -$PROG --useconfig=tests/config6 --device=simulations/simulation_shell.txt > $TEST/test_output.txt 2> $TEST/test_stderr.txt +$PROG --debug --useconfig=tests/config6 --device=simulations/simulation_shell.txt > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then INFO=$(cat /tmp/wmbusmeters_meter_additional_json_test | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/') - EXPECTED=$(echo 'METER =={"media":"warm water","meter":"supercom587","name":"Water","id":"12345678","total_m3":5.548,"timestamp":"1111-11-11T11:11:11Z","floor":"5","address":"RoodRd 42"}== ==RoodRd 42== ==5==') + EXPECTED=$(echo 'METER =={"media":"warm water","meter":"supercom587","name":"Water","id":"12345678","total_m3":5.548,"timestamp":"1111-11-11T11:11:11Z","floor":"5","elevator":"ABC","address":"RoodRd 42","city":"Stockholm"}== ==RoodRd 42== ==Stockholm== ==5== ==ABC==') if [ "$INFO" = "$EXPECTED" ] then echo "OK: $TESTNAME" TESTRESULT="OK" + else + echo "INFO =>$INFO<" + echo "EXPECTED=>$EXPECTED<" fi fi