From 69df7f022a5933525361c7680f3da73b8caa0c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 30 Aug 2020 21:33:48 +0200 Subject: [PATCH 01/61] Detect and start wmbus dongles from within wmbusmeters. --- Makefile | 4 +- install.sh | 123 ++----- src/admin.cc | 459 ++++++------------------- src/cmdline.cc | 31 +- src/config.cc | 12 +- src/config.h | 4 +- src/main.cc | 529 ++++++++++++++++++----------- src/meter_amiplus.cc | 10 +- src/meter_apator08.cc | 10 +- src/meter_apator162.cc | 10 +- src/meter_cma12w.cc | 10 +- src/meter_ebzwmbe.cc | 10 +- src/meter_ehzp.cc | 10 +- src/meter_esyswm.cc | 10 +- src/meter_eurisii.cc | 10 +- src/meter_fhkvdataiii.cc | 10 +- src/meter_hydrodigit.cc | 10 +- src/meter_hydrus.cc | 10 +- src/meter_iperl.cc | 10 +- src/meter_izar.cc | 10 +- src/meter_lansendw.cc | 10 +- src/meter_lansenpu.cc | 10 +- src/meter_lansensm.cc | 10 +- src/meter_lansenth.cc | 10 +- src/meter_mkradio3.cc | 10 +- src/meter_multical21.cc | 18 +- src/meter_multical302.cc | 10 +- src/meter_multical403.cc | 10 +- src/meter_omnipower.cc | 10 +- src/meter_q400.cc | 10 +- src/meter_qcaloric.cc | 10 +- src/meter_rfmamb.cc | 10 +- src/meter_rfmtx1.cc | 10 +- src/meter_supercom587.cc | 10 +- src/meter_vario451.cc | 10 +- src/meter_waterstarm.cc | 10 +- src/meters.cc | 18 +- src/meters.h | 63 ++-- src/meters_common_implementation.h | 4 +- src/serial.cc | 165 +++++---- src/serial.h | 13 +- src/testinternals.cc | 27 ++ src/timings.h | 6 +- src/util.cc | 7 + src/util.h | 8 +- src/wmbus.cc | 182 ++++++---- src/wmbus.h | 106 +++--- src/wmbus_amb8465.cc | 25 +- src/wmbus_cul.cc | 7 +- src/wmbus_im871a.cc | 31 +- src/wmbus_rawtty.cc | 5 +- src/wmbus_rtlwmbus.cc | 11 +- src/wmbus_utils.h | 4 + src/wmbus_wmb13u.cc | 9 +- 54 files changed, 1088 insertions(+), 1063 deletions(-) diff --git a/Makefile b/Makefile index 7ff33c3..a023d34 100644 --- a/Makefile +++ b/Makefile @@ -192,8 +192,8 @@ $(BUILD)/main.o: $(BUILD)/short_manual.h $(BUILD)/version.h $(BUILD)/wmbusmeters: $(METER_OBJS) $(BUILD)/main.o $(BUILD)/short_manual.h $(CXX) -o $(BUILD)/wmbusmeters $(METER_OBJS) $(BUILD)/main.o $(LDFLAGS) -lpthread -$(BUILD)/wmbusmeters-admin: $(METER_OBJS) $(BUILD)/admin.o $(BUILD)/short_manual.h - $(CXX) -o $(BUILD)/wmbusmeters-admin $(METER_OBJS) $(BUILD)/admin.o $(LDFLAGS) -lmenu -lncurses -lpthread +$(BUILD)/wmbusmeters-admin: $(METER_OBJS) $(BUILD)/admin.o $(BUILD)/ui.o $(BUILD)/short_manual.h + $(CXX) -o $(BUILD)/wmbusmeters-admin $(METER_OBJS) $(BUILD)/admin.o $(BUILD)/ui.o $(LDFLAGS) -lmenu -lform -lncurses -lpthread $(BUILD)/short_manual.h: README.md echo 'R"MANUAL(' > $(BUILD)/short_manual.h diff --git a/install.sh b/install.sh index 3c65c80..94c8cba 100755 --- a/install.sh +++ b/install.sh @@ -7,7 +7,6 @@ then Options: --no-adduser Do not add wmbusmeters user - --no-udev-rules Do not add udev rules " exit 0 fi @@ -33,7 +32,6 @@ fi SRC=$1 ROOT="${2%/}" ADDUSER=true -ADDUDEVRULES=true while [ $# -ne 0 ] do @@ -43,10 +41,6 @@ do --no-adduser) ADDUSER=false ;; - --no-udev-rules) - ADDUDEVRULES=false - shift - ;; esac done @@ -94,7 +88,7 @@ fi if [ "$ADDUSER" = "true" ] then - if [ "$ID" = "" ] + if [ -z "$ID" ] then # Create the wmbusmeters user useradd --system --shell $USERSHELL --groups dialout wmbusmeters @@ -102,6 +96,15 @@ then else echo user: wmbusmeters unmodified fi + + if [ "$(groups wmbusmeters | grep -o dialout)" = "" ] + then + # Add the wmbusmeters user to dialout + usermod -a -G dialout wmbusmeters + echo user: added wmbusmeters to dialout group + else + echo user: wmbusmeters already added to dialout + fi if [ ! -z "$SUDO_USER" ] then if [ "$(groups $SUDO_USER | grep -o wmbusmeters)" = "" ] @@ -110,7 +113,15 @@ then usermod -a -G wmbusmeters $SUDO_USER echo user: added $SUDO_USER to group wmbusmeters else - echo user: user $SUDO_USER already added group wmbusmeters + echo user: $SUDO_USER already added group wmbusmeters + fi + if [ "$(groups $SUDO_USER | grep -o dialout)" = "" ] + then + # Add user to the dialout group. + usermod -a -G dialout $SUDO_USER + echo user: added $SUDO_USER to group dialout + else + echo user: $SUDO_USER already added group dialout fi fi fi @@ -268,88 +279,26 @@ OLD_WMBAS=~/old.wmbusmeters@.service.backup CURR_WMBAS="$ROOT"/lib/systemd/system/wmbusmeters@.service if [ -f $CURR_WMBAS ] then + echo systemd: removing $CURR_WMBAS echo systemd: backing up $CURR_WMBAS to here: $OLD_WMBAS cp $CURR_WMBAS $OLD_WMBAS 2>/dev/null -fi - -# Create service file that needs an argument eg. -# sudo systemctl start wmbusmeters@/dev/im871a_1.service -cat <<'EOF' > "$ROOT"/lib/systemd/system/wmbusmeters@.service -[Unit] -Description="wmbusmeters service (udev triggered by %I)" -Documentation=https://github.com/weetmuts/wmbusmeters -Documentation=man:wmbusmeters(1) -After=network.target -StopWhenUnneeded=true -StartLimitIntervalSec=10 -StartLimitInterval=10 -StartLimitBurst=3 - -[Service] -Type=forking -PrivateTmp=yes -User=wmbusmeters -Group=wmbusmeters -Restart=always -RestartSec=1 - -# Run ExecStartPre with root-permissions - -PermissionsStartOnly=true -ExecStartPre=-/bin/mkdir -p /var/log/wmbusmeters/meter_readings -ExecStartPre=/bin/chown -R wmbusmeters:wmbusmeters /var/log/wmbusmeters -ExecStartPre=-/bin/mkdir -p /run/wmbusmeters -ExecStartPre=/bin/chown -R wmbusmeters:wmbusmeters /run/wmbusmeters - -ExecStart=/usr/sbin/wmbusmetersd --device='%I' /run/wmbusmeters/wmbusmeters-%i.pid -ExecReload=/bin/kill -HUP $MAINPID -PIDFile=/run/wmbusmeters/wmbusmeters-%i.pid - -[Install] -WantedBy=multi-user.target -EOF - -if diff $OLD_WMBAS $CURR_WMBAS 1>/dev/null -then - echo systemd: no changes to $CURR_WMBAS -else - echo systemd: updated $CURR_WMBAS + rm $CURR_WMBAS SYSTEMD_NEEDS_RELOAD=true fi #################################################################### ## -## Create /etc/udev/rules.d/99-wmbus-usb-serial.rules +## Remove any existing /etc/udev/rules.d/99-wmbus-usb-serial.rules +## The new wmbuseters daemon is not going to use these. ## -UDEV_NEEDS_RELOAD=false - - -if [ "$ADDUDEVRULES" = "true" ] +if [ -f "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ] then - if [ -f "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ] - then - echo udev: removing "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules - echo udev: backup stored here: ~/old.wmbusmeters-wmbus-usb-serial.rules.backup - cp "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ~/old.wmbusmeters-wmbus-usb-serial.rules.backup - rm "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules - UDEV_NEEDS_RELOAD=true - fi - - if [ ! -f "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ] - then - mkdir -p "$ROOT"/etc/udev/rules.d - # Create service file - cat < "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules -SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",SYMLINK+="im871a_%n",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters@/dev/im871a_%n.service" -SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001",SYMLINK+="amb8465_%n",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters@/dev/amb8465_%n.service" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838",SYMLINK+="rtlsdr_%n",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters@/dev/rtlsdr_%n.service" -SUBSYSTEM=="usb", ATTRS{idVendor}=="2047", ATTRS{idProduct}=="0863",SYMLINK+="rfmrx2_%n",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters@/dev/rfmrx2_%n.service" -EOF - echo udev: installed "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules - else - echo udev: "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules unchanged - fi + echo udev: removing "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules + echo udev: backup stored here: ~/old.wmbusmeters-wmbus-usb-serial.rules.backup + cp "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ~/old.wmbusmeters-wmbus-usb-serial.rules.backup + rm "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules + UDEV_NEEDS_RELOAD=true fi if [ "$SYSTEMD_NEEDS_RELOAD" = "true" ] @@ -362,13 +311,9 @@ fi if [ "$UDEV_NEEDS_RELOAD" = "true" ] then - D=$(diff "$ROOT"/etc/udev/rules.d/99-wmbus-usb-serial.rules ~/old.wmbusmeters-wmbus-usb-serial.rules.backup) - if [ "$D" != "" ] - then - echo - echo - echo You need to reload udev configuration! Please do: - echo "sudo udevadm control --reload-rules" - echo "sudo udevadm trigger" - fi + echo + echo + echo You need to reload udev configuration! Please do: + echo "sudo udevadm control --reload-rules" + echo "sudo udevadm trigger" fi diff --git a/src/admin.cc b/src/admin.cc index ad74aec..4a607e2 100644 --- a/src/admin.cc +++ b/src/admin.cc @@ -15,24 +15,16 @@ along with this program. If not, see . */ -#include -#include #include #include #include -#include +#include #include"serial.h" #include"shell.h" +#include"ui.h" #include"wmbus.h" -#define BG_PAIR 1 -#define WIN_PAIR 2 -#define TITLE_PAIR 3 -#define HILIGHT_PAIR 4 - -#include - bool running_as_root_ = false; bool member_of_dialout_ = false; @@ -78,30 +70,24 @@ LIST_OF_WMBUS_RECEIVERS }; bool detectIfRoot(); +string userName(); bool detectIfMemberOfGroup(string group); void detectProcesses(string cmd, vector *pids); void detectWMBUSReceiver(); void resetWMBUSReceiver(); -void probeFor(string type, AccessCheck(*func)(string,SerialCommunicationManager*)); +void probeFor(string type, AccessCheck(*func)(string,Detected*,SerialCommunicationManager*)); -void printAt(WINDOW *win, int y, int x, const char *str, chtype color); -void printMiddle(WINDOW *win, int y, int width, const char *str, chtype color); +void stopDaemon(); +void startDaemon(); -void alwaysOnScreen(); -int selectFromMenu(const char *title, const char *menu[]); -int selectFromMenu(const char *title, vector menu); -void displayInformationAndWait(string title, vector entries, int px=-1, int py=-1); -void displayInformationNoWait(WINDOW **win, string title, vector entries, int px=-1, int py=-1); - -void notImplementedYet(string msg); - -int screen_width, screen_height; unique_ptr handler; WINDOW *status_window; WINDOW *serial_ports_window; WINDOW *processes_window; +void alwaysOnScreen(); + int main(int argc, char **argv) { if (argc == 2 && (!strcmp(argv[1], "--debug") || !strcmp(argv[1], "--trace"))) @@ -112,27 +98,33 @@ int main(int argc, char **argv) enableSyslog(); } + initUI(); + clear(); + + refresh(); + int x=0; + int y=0; + for (int i=0;i<10; ++i) { + printAt(stdscr, y, x, "HEJSAN", COLOR_PAIR(BG_PAIR)); + y++; + x++; + }; + refresh(); + for(;;) {} + endwin(); + return 0; + + /* running_as_root_ = detectIfRoot(); member_of_dialout_ = detectIfMemberOfGroup("dialout"); handler = createSerialCommunicationManager(0, 0, false); - initscr(); - getmaxyx(stdscr, screen_height, screen_width); - start_color(); - cbreak(); - curs_set(0); - noecho(); - keypad(stdscr, TRUE); - - init_pair(BG_PAIR, COLOR_WHITE, COLOR_BLUE); - init_pair(WIN_PAIR, COLOR_BLACK, COLOR_WHITE); - init_pair(TITLE_PAIR, COLOR_WHITE, COLOR_CYAN); - init_pair(HILIGHT_PAIR, COLOR_WHITE, COLOR_RED); - wbkgd(stdscr, COLOR_PAIR(BG_PAIR)); + initUI(); bool running = true; + registerUpdateCB(alwaysOnScreen); alwaysOnScreen(); do @@ -157,84 +149,23 @@ int main(int argc, char **argv) notImplementedYet("Edit meters"); break; case MainMenuType::STOP_DAEMON: - notImplementedYet("Stop daemon"); + stopDaemon(); break; case MainMenuType::START_DAEMON: - notImplementedYet("Start daemon"); + startDaemon(); break; case MainMenuType::EXIT_ADMIN: running = false; break; } } while (running); - - - endwin(); -} - -void printAt(WINDOW *win, int y, int x, const char *str, chtype color) -{ - wattron(win, color); - mvwprintw(win, y, x, "%s", str); - wattroff(win, color); - refresh(); -} - -void printMiddle(WINDOW *win, int y, int width, const char *str, chtype color) -{ - int len = strlen(str); - int wh, ww; - - getyx(win, wh, ww); - ((void)wh); - - int x = (width-len)/2; - wattron(win, color); - mvwprintw(win, y, x, "%s", str); - wattroff(win, color); - refresh(); -} - -int countEntries(const char *entries[]) -{ - int i = 0; - for (; entries[i] != 0; ++i); - return i+1; -} - -int maxWidth(const char *entries[]) -{ - int max = 0; - for (int i=0; entries[i] != 0; ++i) - { - int n = strlen(entries[i]); - if (max < n) max = n; - } - return max; -} - -int maxWidth(vector entries) -{ - int max = 0; - for (string& s : entries) - { - int n = s.length(); - if (max < n) max = n; - } - return max; + */ } void alwaysOnScreen() { - static uchar ticktock = 0; - vector info; - ticktock++; - if (running_as_root_ == false) - { - info.push_back("Not running as root!"); - } if (member_of_dialout_ == false) { info.push_back("Not member of dialout!"); @@ -268,7 +199,17 @@ void alwaysOnScreen() } } - displayInformationNoWait(&status_window, (ticktock%2==0)?"Status ":"Status.", info, 1, 1); + vector status; + time_t now = time(NULL); + struct tm nowt {}; + localtime_r(&now, &nowt); + status.push_back("wmbusmeters-admin"); + status.push_back(strdatetimesec(&nowt)); + string name = "["+userName()+"]"; + status.push_back(name); + displayStatusLineNoWait(&status_window, status, 0, 0); + + displayInformationNoWait(&status_window, "Problems", info, 2, 2); vector devices = handler->listSerialDevices(); if (devices.size() == 0) @@ -280,252 +221,8 @@ void alwaysOnScreen() displayInformationNoWait(&serial_ports_window, "Serial ports", devices, 1, 15); erase(); - redrawwin(status_window); - redrawwin(serial_ports_window); -} - -int selectFromMenu(const char *title, const char *entries[]) -{ - vector menu; - int n_choices = countEntries(entries); - - for (int i=0; i entries) -{ - int selected = -1; - ITEM **menu_items; - int c; - MENU *menu; - WINDOW *frame_window, *menu_window; - int n_choices, i; - - n_choices = entries.size()+1; - menu_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); - for(i = 0; i < n_choices-1; ++i) - { - menu_items[i] = new_item(entries[i].c_str(), NULL); - } - menu_items[n_choices-1] = NULL; - - menu = new_menu(menu_items); - int mw = 0; - int mh = 0; - scale_menu(menu, &mh, &mw); - int w = mw+2; - int h = mh+4; - if (w-2 < (int)strlen(title)) - { - w = (int)strlen(title)+2; - } - int x = screen_width/2-w/2; - int y = screen_height/2-h/2; - frame_window = newwin(h, w, y, x); - - int mx = (w-mw)/2; - int my = 3; - menu_window = derwin(frame_window, mh, mw, my, mx); - - set_menu_fore(menu, COLOR_PAIR(HILIGHT_PAIR)); - set_menu_back(menu, COLOR_PAIR(WIN_PAIR)); - set_menu_grey(menu, COLOR_PAIR(3)); - - keypad(frame_window, TRUE); - - set_menu_win(menu, frame_window); - set_menu_sub(menu, menu_window); - - set_menu_mark(menu, ">"); - - box(frame_window, 0, 0); - wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); - - printMiddle(frame_window, 1, w, title, COLOR_PAIR(WIN_PAIR)); - mvwaddch(frame_window, 2, 0, ACS_LTEE); - mvwhline(frame_window, 2, 1, ACS_HLINE, 38); - mvwaddch(frame_window, 2, w-1, ACS_RTEE); - refresh(); - - post_menu(menu); - wrefresh(frame_window); - - alwaysOnScreen(); - - wtimeout(frame_window, 1000); - - bool running = true; - do - { - c = wgetch(frame_window); - ITEM *cur = current_item(menu); - selected = item_index(cur); - switch(c) - { - case ERR: - alwaysOnScreen(); - redrawwin(frame_window); - break; - case KEY_DOWN: - if (selected < n_choices-2) - { - menu_driver(menu, REQ_DOWN_ITEM); - } - else - { - menu_driver(menu, REQ_FIRST_ITEM); - } - break; - case KEY_UP: - if (selected > 0) - { - menu_driver(menu, REQ_UP_ITEM); - } - else - { - menu_driver(menu, REQ_LAST_ITEM); - } - break; - case '\n': - running = false; - break; - } - wrefresh(frame_window); - } while (running); - - unpost_menu(menu); - free_menu(menu); - delwin(frame_window); - erase(); - refresh(); - for(i = 0; i < n_choices; ++i) - { - free_item(menu_items[i]); - } - - return selected; -} - -void displayInformationAndWait(string title, vector entries, int px, int py) -{ - WINDOW *frame_window; - - alwaysOnScreen(); - - int mw = maxWidth(entries)+1; - int mh = entries.size(); - int w = mw+2; - int h = mh+4; - if (w-2 < (int)title.length()) - { - w = (int)title.length()+2; - } - int x = screen_width/2-w/2; - int y = screen_height/2-h/2; - if (px != -1) - { - x = px; - } - if (py != -1) - { - y = py; - } - frame_window = newwin(h, w, y, x); - - keypad(frame_window, TRUE); - - box(frame_window, 0, 0); - wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); - - printMiddle(frame_window, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); - mvwaddch(frame_window, 2, 0, ACS_LTEE); - mvwhline(frame_window, 2, 1, ACS_HLINE, 38); - mvwaddch(frame_window, 2, w-1, ACS_RTEE); - //refresh(); - - int r = 3; - for (string e : entries) - { - printAt(frame_window, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); - r++; - } - wrefresh(frame_window); - wtimeout(frame_window, 1000); - - bool running = true; - do - { - int c = wgetch(frame_window); - switch(c) - { - case ERR: - alwaysOnScreen(); - redrawwin(frame_window); - break; - case 27: - case '\n': - running = false; - break; - } - wrefresh(frame_window); - } while (running); - - delwin(frame_window); - erase(); - refresh(); -} - -void displayInformationNoWait(WINDOW **winp, string title, vector entries, int px, int py) -{ - WINDOW *win = *winp; - - if (win != NULL) - { - delwin(win); - *winp = NULL; - } - int mw = maxWidth(entries)+1; - int mh = entries.size(); - int w = mw+2; - int h = mh+4; - if (w-2 < (int)title.length()) - { - w = (int)title.length()+2; - } - int x = screen_width/2-w/2; - int y = screen_height/2-h/2; - if (px != -1) - { - x = px; - } - if (py != -1) - { - y = py; - } - win = newwin(h, w, y, x); - *winp = win; - - box(win, 0, 0); - wbkgd(win, COLOR_PAIR(WIN_PAIR)); - - printMiddle(win, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); - mvwaddch(win, 2, 0, ACS_LTEE); - mvwhline(win, 2, 1, ACS_HLINE, 38); - mvwaddch(win, 2, w-1, ACS_RTEE); -// refresh(); - - int r = 3; - for (string e : entries) - { - printAt(win, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); - r++; - } - wrefresh(win); + wrefresh(status_window); + wrefresh(serial_ports_window); } void detectWMBUSReceiver() @@ -586,15 +283,9 @@ void resetWMBUSReceiver() } } -void notImplementedYet(string msg) -{ - vector entries; - entries.push_back(msg); - displayInformationAndWait("Not implemented yet", entries); -} - -void probeFor(string type, AccessCheck (*check)(string,SerialCommunicationManager*)) +void probeFor(string type, AccessCheck (*check)(string,Detected*,SerialCommunicationManager*)) { + Detected detected {}; vector devices = handler->listSerialDevices(); vector entries; for (string& device : devices) @@ -602,7 +293,7 @@ void probeFor(string type, AccessCheck (*check)(string,SerialCommunicationManage string tty = "?"; AccessCheck ac = checkAccessAndDetect( handler.get(), - [=](string d, SerialCommunicationManager* m){ return check(d, m);}, + [&](string d, SerialCommunicationManager* m){ return check(d, &detected, m);}, type, device); @@ -638,6 +329,18 @@ bool detectIfRoot() return out == "0\n"; } +string userName() +{ + vector args; + vector envs; + args.push_back("-u"); + args.push_back("-n"); + string out; + invokeShellCaptureOutput("/usr/bin/id", args, envs, &out, true); + + return out; +} + bool detectIfMemberOfGroup(string group) { vector args; @@ -674,3 +377,49 @@ void detectProcesses(string cmd, vector *pids) pch = strtok (NULL, " \n"); } } + +void stopDaemon() +{ + vector info; + info.push_back("Enter sudo password to execute:"); + info.push_back("systemctl stop wmbusmeters"); + + debug("(passowrd) calling inputfield\n"); + string pwd = inputField("Stop daemon", info, "Password"); + debug("(passowrd) GOT %s\n", pwd.c_str()); + //string pwd = displayInformationAndInput("Stop daemon", info, 1, 1); + //vector args; + //vector envs; + //args.push_back("gurka"); + // string out; +// invokeShellCaptureOutput("systemctl stop wmbusmeters", args, envs, &out, true); +} + +void startDaemon() +{ +} + +/* +static char* trim_whitespaces(char *str) +{ + char *end; + + // trim leading space + while(isspace(*str)) + str++; + + if(*str == 0) // all spaces? + return str; + + // trim trailing space + end = str + strnlen(str, 128) - 1; + + while(end > str && isspace(*end)) + end--; + + // write new null terminator + *(end+1) = '\0'; + + return str; +} +*/ diff --git a/src/cmdline.cc b/src/cmdline.cc index 5fcfbaf..2b0da5f 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -16,7 +16,6 @@ */ #include"cmdline.h" -#include"config.h" #include"meters.h" #include"util.h" @@ -68,7 +67,8 @@ unique_ptr parseCommandLine(int argc, char **argv) { c->need_help = true; return unique_ptr(c); } - while (argv[i] && argv[i][0] == '-') { + while (argv[i] && argv[i][0] == '-') + { if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) { c->need_help = true; return unique_ptr(c); @@ -96,6 +96,12 @@ unique_ptr parseCommandLine(int argc, char **argv) { i++; continue; } + if (!strcmp(argv[i], "--trace")) { + c->debug = true; + c->trace = true; + i++; + continue; + } if (!strcmp(argv[i], "--internaltesting")) { c->internaltesting = true; i++; @@ -438,18 +444,17 @@ unique_ptr parseCommandLine(int argc, char **argv) { error("Unknown option \"%s\"\n", argv[i]); } - char *extra = argv[i] ? strchr(argv[i], ':') : NULL ; - if (extra) { - *extra = 0; - extra++; - c->device_extra = extra; + while (argv[i]) + { + Device device; + bool ok = isPossibleDevice(argv[i], &device); + if (!ok) break; + c->wmbus_devices.push_back(device); + i++; } - if (argv[i]) { - c->device = argv[i]; - } - i++; - if (c->device.length() == 0) { - error("You must supply the usb device to which the wmbus dongle is connected.\n"); + + if (c->wmbus_devices.size() == 0) { + error("You must supply at least one device to receive wmbus telegrams.\n"); } if ((argc-i) % 4 != 0) { diff --git a/src/config.cc b/src/config.cc index c2f1744..a07a525 100644 --- a/src/config.cc +++ b/src/config.cc @@ -189,20 +189,18 @@ void handleInternalTesting(Configuration *c, string value) } } -void handleDevice(Configuration *c, string device) +void handleDevice(Configuration *c, string devicefile) { // device can be: // /dev/ttyUSB00 // auto // rtlwmbus:/usr/bin/rtl_sdr -f 868.9M -s 1600000 - | /usr/bin/rtl_wmbus // simulation....txt (read telegrams from file) - size_t p = device.find (':'); - if (p != string::npos) + Device device; + bool ok = isPossibleDevice(devicefile, &device); + if (ok) { - c->device_extra = device.substr(p+1); - c->device = device.substr(0,p); - } else { - c->device = device; + c->wmbus_devices.push_back(device); } } diff --git a/src/config.h b/src/config.h index 585e111..f56e545 100644 --- a/src/config.h +++ b/src/config.h @@ -79,8 +79,8 @@ struct Configuration bool oneshot {}; int exitafter {}; // Seconds to exit. int reopenafter {}; // Re-open the serial device repeatedly. Silly dongle. - string device; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus - string device_extra; // The frequency or the command line that will start rtlwmbus + std::vector wmbus_devices; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600 + std::vector mbus_devices; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600 string telegram_reader; // A set of all link modes (union) that the user requests the wmbus dongle to listen to. LinkModeSet listen_to_link_modes; diff --git a/src/main.cc b/src/main.cc index 4880271..2680a5f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,8 +24,7 @@ #include"version.h" #include"wmbus.h" -#include - +#include #include #include #include @@ -38,21 +37,49 @@ using namespace std; -void oneshotCheck(Configuration *cmdline, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> &meters); -bool startUsingCommandline(Configuration *cmdline); +void oneshotCheck(Configuration *config, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> *meters); +void setupLogFile(Configuration *config); +void setupMeters(Configuration *config, vector> *meters); +void attachMetersToPrinter(Configuration *config, vector> *meters, Printer *printer); +void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationManager *manager, vector> *devices); +unique_ptr createPrinter(Configuration *config); +void logStartInformation(Configuration *config); +bool start(Configuration *config); void startUsingConfigFiles(string root, bool is_daemon, string device_override, string listento_override); void startDaemon(string pid_file, string device_override, string listento_override); // Will use config files. +// The serial communication manager takes care of +// monitoring the file descrtiptors for the ttys, +// background shells. It also invokes regular callbacks +// used for monitoring alarms and detecting new devices. +unique_ptr manager_; + +// Registered meters to decode and relay. +// This does not change during runtime. +vector> meters_; + +// Current active set of wmbus devices that can receive telegrams. +// This can change during runtime, plugging/unplugging wmbus dongles. +vector> devices_; +pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER; + +// Rendering the telegrams to json,fields or shell calls is +// done by the printer. +unique_ptr printer_; + +// Set as true when the warning for no detected wmbus devices has been printed. +bool printed_warning_ = false; + int main(int argc, char **argv) { - auto cmdline = parseCommandLine(argc, argv); + auto config = parseCommandLine(argc, argv); - if (cmdline->version) { + if (config->version) { printf("wmbusmeters: " VERSION "\n"); printf(COMMIT "\n"); exit(0); } - if (cmdline->license) { + if (config->license) { const char * license = R"LICENSE( Copyright (C) 2017-2020 Fredrik Öhrström @@ -77,129 +104,79 @@ provided you with this binary. Read the full license for all details. puts(license); exit(0); } - if (cmdline->need_help) { + if (config->need_help) { printf("wmbusmeters version: " VERSION "\n"); const char *short_manual = #include"short_manual.h" puts(short_manual); } else - if (cmdline->daemon) { - startDaemon(cmdline->pid_file, cmdline->device_override, cmdline->listento_override); + if (config->daemon) { + startDaemon(config->pid_file, config->device_override, config->listento_override); exit(0); } else - if (cmdline->useconfig) { - startUsingConfigFiles(cmdline->config_root, false, cmdline->device_override, cmdline->listento_override); + if (config->useconfig) { + startUsingConfigFiles(config->config_root, false, config->device_override, config->listento_override); exit(0); } else { // We want the data visible in the log file asap! setbuf(stdout, NULL); - startUsingCommandline(cmdline.get()); + start(config.get()); exit(0); } error("(main) internal error\n"); } -bool startUsingCommandline(Configuration *config) +unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *config, SerialCommunicationManager *manager) { - if (config->use_logfile) - { - verbose("(wmbusmeters) using log file %s\n", config->logfile.c_str()); - bool ok = enableLogfile(config->logfile, config->daemon); - if (!ok) { - if (config->daemon) { - warning("Could not open log file, will use syslog instead.\n"); - } else { - error("Could not open log file.\n"); - } - } - } - else - { - disableLogfile(); - } - - warningSilenced(config->silence); - verboseEnabled(config->verbose); - logTelegramsEnabled(config->logtelegrams); - debugEnabled(config->debug); - internalTestingEnabled(config->internaltesting); - traceEnabled(config->trace); - stderrEnabled(config->use_stderr); - setAlarmShells(config->alarm_shells); - - debug("(wmbusmeters) version: " VERSION "\n"); - - if (config->exitafter != 0) { - verbose("(config) wmbusmeters will exit after %d seconds\n", config->exitafter); - } - - if (config->reopenafter != 0) { - verbose("(config) wmbusmeters close/open the wmbus dongle fd after every %d seconds\n", config->reopenafter); - } - - if (config->meterfiles) { - verbose("(config) store meter files in: \"%s\"\n", config->meterfiles_dir.c_str()); - } - verbose("(config) using device: %s\n", config->device.c_str()); - if (config->device_extra.length() > 0) { - verbose("(config) with: %s\n", config->device_extra.c_str()); - } - verbose("(config) number of meters: %d\n", config->meters.size()); - - auto manager = createSerialCommunicationManager(config->exitafter, config->reopenafter); - onExit(call(manager.get(),stop)); - - Detected settings = detectWMBusDeviceSetting(config->device, config->device_extra, manager.get()); + unique_ptr wmbus; unique_ptr serial_override; bool link_modes_matter = true; - if (settings.override_tty) + if (detected->override_tty) { - serial_override = manager->createSerialDeviceFile(settings.devicefile); - verbose("(serial) override with devicefile: %s\n", settings.devicefile.c_str()); + serial_override = manager->createSerialDeviceFile(detected->device.file); + verbose("(serial) override with devicefile: %s\n", detected->device.file.c_str()); link_modes_matter = false; } - unique_ptr wmbus; - - switch (settings.type) + switch (detected->type) { case DEVICE_IM871A: - verbose("(im871a) on %s\n", settings.devicefile.c_str()); - wmbus = openIM871A(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(im871a) on %s\n", detected->device.file.c_str()); + wmbus = openIM871A(detected->device.file, manager, std::move(serial_override)); break; case DEVICE_AMB8465: - verbose("(amb8465) on %s\n", settings.devicefile.c_str()); - wmbus = openAMB8465(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(amb8465) on %s\n", detected->device.file.c_str()); + wmbus = openAMB8465(detected->device.file, manager, std::move(serial_override)); break; case DEVICE_SIMULATOR: - verbose("(simulator) in %s\n", settings.devicefile.c_str()); - wmbus = openSimulator(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(simulator) in %s\n", detected->device.file.c_str()); + wmbus = openSimulator(detected->device.file, manager, std::move(serial_override)); link_modes_matter = false; break; case DEVICE_RAWTTY: - verbose("(rawtty) on %s\n", settings.devicefile.c_str()); - wmbus = openRawTTY(settings.devicefile, settings.baudrate, manager.get(), std::move(serial_override)); + verbose("(rawtty) on %s\n", detected->device.file.c_str()); + wmbus = openRawTTY(detected->device.file, detected->baudrate, manager, std::move(serial_override)); link_modes_matter = false; break; case DEVICE_RFMRX2: - verbose("(rfmrx2) on %s\n", settings.devicefile.c_str()); + verbose("(rfmrx2) on %s\n", detected->device.file.c_str()); if (config->reopenafter == 0) { manager->setReopenAfter(600); // Close and reopen the fd, because of some bug in the device. } - wmbus = openRawTTY(settings.devicefile, 38400, manager.get(), std::move(serial_override)); + wmbus = openRawTTY(detected->device.file, 38400, manager, std::move(serial_override)); break; case DEVICE_RTLWMBUS: { string command; - if (!settings.override_tty) + if (!detected->override_tty) { - command = config->device_extra; + command = detected->device.suffix; string freq = "868.95M"; string prefix = ""; if (isFrequency(command)) { @@ -226,7 +203,7 @@ bool startUsingCommandline(Configuration *config) } verbose("(rtlwmbus) using command: %s\n", command.c_str()); } - wmbus = openRTLWMBUS(command, manager.get(), + wmbus = openRTLWMBUS(command, manager, [command](){ warning("(rtlwmbus) child process exited! " "Command was: \"%s\"\n", command.c_str()); @@ -237,9 +214,9 @@ bool startUsingCommandline(Configuration *config) case DEVICE_RTL433: { string command; - if (!settings.override_tty) + if (!detected->override_tty) { - command = config->device_extra; + command = detected->device.suffix; string freq = "868.95M"; string prefix = ""; if (isFrequency(command)) { @@ -262,7 +239,7 @@ bool startUsingCommandline(Configuration *config) } verbose("(rtl433) using command: %s\n", command.c_str()); } - wmbus = openRTL433(command, manager.get(), + wmbus = openRTL433(command, manager, [command](){ warning("(rtl433) child process exited! " "Command was: \"%s\"\n", command.c_str()); @@ -272,20 +249,20 @@ bool startUsingCommandline(Configuration *config) } case DEVICE_CUL: { - verbose("(cul) on %s\n", settings.devicefile.c_str()); - wmbus = openCUL(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(cul) on %s\n", detected->device.file.c_str()); + wmbus = openCUL(detected->device.file, manager, std::move(serial_override)); break; } case DEVICE_D1TC: { - verbose("(d1tc) on %s\n", settings.devicefile.c_str()); - wmbus = openD1TC(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(d1tc) on %s\n", detected->device.file.c_str()); + wmbus = openD1TC(detected->device.file, manager, std::move(serial_override)); break; } case DEVICE_WMB13U: { - verbose("(wmb13u) on %s\n", settings.devicefile.c_str()); - wmbus = openWMB13U(settings.devicefile, manager.get(), std::move(serial_override)); + verbose("(wmb13u) on %s\n", detected->device.file.c_str()); + wmbus = openWMB13U(detected->device.file, manager, std::move(serial_override)); break; } case DEVICE_UNKNOWN: @@ -302,127 +279,301 @@ bool startUsingCommandline(Configuration *config) if (lmcr.type != LinkModeCalculationResultType::Success) { error("%s\n", lmcr.msg.c_str()); } + return wmbus; +} - auto output = unique_ptr(new Printer(config->json, config->fields, - config->separator, config->meterfiles, config->meterfiles_dir, - config->use_logfile, config->logfile, - config->telegram_shells, - config->meterfiles_action == MeterFileType::Overwrite, - config->meterfiles_naming, - config->meterfiles_timestamp)); - vector> meters; - - if (config->meters.size() > 0) +void setupLogFile(Configuration *config) +{ + if (config->use_logfile) { - for (auto &m : config->meters) - { - const char *keymsg = (m.key[0] == 0) ? "not-encrypted" : "encrypted"; - switch (toMeterType(m.type)) - { -#define X(mname,link,info,type,cname) \ - case MeterType::type: \ - meters.push_back(create##cname(wmbus.get(), m)); \ - verbose("(wmbusmeters) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ - m.name.c_str(), m.id.c_str(), keymsg); \ - meters.back()->addConversions(config->conversions); \ - break; -LIST_OF_METERS -#undef X - case MeterType::UNKNOWN: - error("No such meter type \"%s\"\n", m.type.c_str()); - break; + verbose("(wmbusmeters) using log file %s\n", config->logfile.c_str()); + bool ok = enableLogfile(config->logfile, config->daemon); + if (!ok) { + if (config->daemon) { + warning("Could not open log file, will use syslog instead.\n"); + } else { + error("Could not open log file.\n"); } - - if (config->list_shell_envs) - { - string ignore1, ignore2, ignore3; - vector envs; - Telegram t; - meters.back()->printMeter(&t, - &ignore1, - &ignore2, config->separator, - &ignore3, - &envs, - &config->jsons, - &config->selected_fields); - printf("Environment variables provided to shell for meter %s:\n", m.type.c_str()); - for (auto &e : envs) { - int p = e.find('='); - string key = e.substr(0,p); - printf("%s\n", key.c_str()); - } - exit(0); - } - - if (config->list_fields) - { - printf("Fields produced by meter %s:\n", m.type.c_str()); - printf("id\n"); - printf("name\n"); - printf("timestamp\n"); - for (auto &f : meters.back()->fields()) - { - printf("%s\n", f.c_str()); - } - exit(0); - } - - meters.back()->onUpdate([&](Telegram*t,Meter* meter) - { - output->print(t,meter,&config->jsons,&config->selected_fields); - }); - meters.back()->onUpdate([&](Telegram*t, Meter* meter) - { - oneshotCheck(config, manager.get(), t, meter, meters); - }); } } else { - notice("No meters configured. Printing id:s of all telegrams heard!\n\n"); + disableLogfile(); + } +} - wmbus->onTelegram([](vector frame){ - Telegram t; - MeterKeys mk; - t.parserNoWarnings(); // Try a best effort parse, do not print any warnings. - t.parse(frame, &mk); - t.print(); - t.explainParse("(wmbus)",0); - logTelegram("(wmbus)", t.frame, 0, 0); - return true; - }); +void setupMeters(Configuration *config, vector> *meters) +{ + for (auto &m : config->meters) + { + const char *keymsg = (m.key[0] == 0) ? "not-encrypted" : "encrypted"; + switch (toMeterType(m.type)) + { +#define X(mname,link,info,type,cname) \ + case MeterType::type: \ + meters->push_back(create##cname(m)); \ + verbose("(wmbusmeters) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ + m.name.c_str(), m.id.c_str(), keymsg); \ + meters->back()->addConversions(config->conversions); \ + break; +LIST_OF_METERS +#undef X + case MeterType::UNKNOWN: + error("No such meter type \"%s\"\n", m.type.c_str()); + break; + } + + if (config->list_shell_envs) + { + string ignore1, ignore2, ignore3; + vector envs; + Telegram t; + meters->back()->printMeter(&t, + &ignore1, + &ignore2, config->separator, + &ignore3, + &envs, + &config->jsons, + &config->selected_fields); + printf("Environment variables provided to shell for meter %s:\n", m.type.c_str()); + for (auto &e : envs) { + int p = e.find('='); + string key = e.substr(0,p); + printf("%s\n", key.c_str()); + } + exit(0); + } + + if (config->list_fields) + { + printf("Fields produced by meter %s:\n", m.type.c_str()); + printf("id\n"); + printf("name\n"); + printf("timestamp\n"); + for (auto &f : meters->back()->fields()) + { + printf("%s\n", f.c_str()); + } + exit(0); + } + } +} + +void attachMetersToPrinter(Configuration *config, vector> *meters, Printer *printer) +{ + for (auto &meter : *meters) + { + meter->onUpdate([&](Telegram*t,Meter* meter) + { + printer->print(t,meter,&config->jsons,&config->selected_fields); + }); + meter->onUpdate([&](Telegram*t, Meter* meter) + { + oneshotCheck(config, manager_.get(), t, meter, meters); + }); + } +} + +void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationManager *manager, vector> *devices) +{ + trace("(trace main) checking for dead wmbus devices...\n"); + + LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + vector not_working; + for (auto &w : devices_) + { + if (!w->isWorking()) + { + not_working.push_back(w.get()); + } } - wmbus->setMeters(&meters); - wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); - manager->startEventLoop(); - wmbus->setLinkModes(config->listen_to_link_modes); - string using_link_modes = wmbus->getLinkModes().hr(); - - verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - - if (settings.type == DEVICE_SIMULATOR) { - wmbus->simulate(); + for (auto w : not_working) + { + auto i = devices_.begin(); + while (i != devices_.end()) + { + if (w == (*i).get()) + { + // The erased unique_ptr will delete the WMBus object. + devices_.erase(i); + break; + } + i++; + } } - if (config->daemon) { + if (devices_.size() == 0) + { + if (!printed_warning_) + { + info("(main) no wmbus devices detected.\n"); + printed_warning_ = true; + } + } + else + { + printed_warning_ = false; + } + + UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + + trace("(trace main) checking for new wmbus devices...\n"); + + vector ds = manager->listSerialDevices(); + for (string& device : ds) + { + trace("(trace main) serial device %s\n", device.c_str()); + SerialDevice *sd = manager->lookup(device); + if (sd == NULL) + { + debug("(main) device %s not currently used, detect contents...\n", device.c_str()); + // This serial device is not in use. + Detected detected = detectImstAmberCul(device, "", manager); + if (detected.type != DEVICE_UNKNOWN) + { + info("(main) detected %s on %s\n", toString(detected.type), device.c_str()); + LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + unique_ptr w = createWMBusDeviceFrom(&detected, config, manager); + devices->push_back(std::move(w)); + WMBus *wmbus = devices->back().get(); + UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + wmbus->setLinkModes(config->listen_to_link_modes); + } + } + } +} + +unique_ptr createPrinter(Configuration *config) +{ + return unique_ptr(new Printer(config->json, config->fields, + config->separator, config->meterfiles, config->meterfiles_dir, + config->use_logfile, config->logfile, + config->telegram_shells, + config->meterfiles_action == MeterFileType::Overwrite, + config->meterfiles_naming, + config->meterfiles_timestamp)); +} + +void logStartInformation(Configuration *config) +{ + verbose("(wmbusmeters) version: " VERSION "\n"); + + if (config->exitafter != 0) { + verbose("(config) wmbusmeters will exit after %d seconds\n", config->exitafter); + } + + if (config->reopenafter != 0) { + verbose("(config) wmbusmeters close/open the wmbus dongle fd after every %d seconds\n", config->reopenafter); + } + + if (config->meterfiles) { + verbose("(config) store meter files in: \"%s\"\n", config->meterfiles_dir.c_str()); + } + + for (auto &device : config->wmbus_devices) + { + verbose("(config) using device: %s %s\n", device.file.c_str(), device.suffix.c_str()); + } + verbose("(config) number of meters: %d\n", config->meters.size()); +} + +bool start(Configuration *config) +{ + // Configure where the logging information should end up. + setupLogFile(config); + + // Configure settings. + warningSilenced(config->silence); + verboseEnabled(config->verbose); + logTelegramsEnabled(config->logtelegrams); + debugEnabled(config->debug); + internalTestingEnabled(config->internaltesting); + traceEnabled(config->trace); + stderrEnabled(config->use_stderr); + setAlarmShells(config->alarm_shells); + + logStartInformation(config); + + // Create the manager monitoring all filedescriptors and invoking callbacks. + manager_ = createSerialCommunicationManager(config->exitafter, config->reopenafter); + // If our software unexpectedly exits, then stop the manager, to try + // to achive a nice shutdown. + onExit(call(manager_.get(),stop)); + + // Create the printer object that knows how to translate + // telegrams into json, fields that are written into log files + // or sent to shell invocations. + printer_ = createPrinter(config); + + // Create the Meter objects from the configuration. + setupMeters(config, &meters_); + // Attach a received-telegram-callback from the meter and + // attach it to the printer. + attachMetersToPrinter(config, &meters_, printer_.get()); + + manager_->startEventLoop(); + + // Detect and initialize any devices. + // Future changes are triggered through this callback. + printed_warning_ = true; + detectAndConfigureWMBusDevices(config, manager_.get(), &devices_); + + if (devices_.size() == 0) + { + info("(main) no wmbus devices detected.\n"); + } + + // Every 2 seconds, perform the exact same call again and again. + manager_->startRegularCallback("device_detector", + 2, + [&](){ + detectAndConfigureWMBusDevices(config, manager_.get(), &devices_); + }); + + //wmbus->setMeters(&meters); + //wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); + //wmbus->setLinkModes(config->listen_to_link_modes); + //string using_link_modes = wmbus->getLinkModes().hr(); + + //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); + + //if (detected.type == DEVICE_SIMULATOR) { + //wmbus->simulate(); + //} + if (config->daemon) + { notice("(wmbusmeters) waiting for telegrams\n"); } - manager->waitForStop(); - if (config->daemon) { + // This thread now sleeps waiting for the serial communication manager to stop. + // The manager has already started one thread that performs select and then callbacks + // to decoding the telegrams, finally invoking the printer. + // The regular callback invoked to detect changes in the wmbus devices and + // the alarm checks, is started in a separate thread. + manager_->waitForStop(); + + if (config->daemon) + { notice("(wmbusmeters) shutting down\n"); } + // Destroy any remaining allocated objects. + devices_.clear(); + meters_.clear(); + printer_.reset(); + manager_.reset(); + restoreSignalHandlers(); return gotHupped(); } -void oneshotCheck(Configuration *config, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> &meters) +void oneshotCheck(Configuration *config, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> *meters) { if (!config->oneshot) return; - for (auto &m : meters) { + for (auto &m : *meters) + { if (m->numUpdates() == 0) return; } // All meters have received at least one update! Stop! @@ -507,7 +658,7 @@ void startUsingConfigFiles(string root, bool is_daemon, string device_override, { unique_ptr config = loadConfiguration(root, device_override, listento_override); config->daemon = is_daemon; - restart = startUsingCommandline(config.get()); + restart = start(config.get()); if (restart) { notice("(wmbusmeters) HUP received, restarting and reloading config files.\n"); diff --git a/src/meter_amiplus.cc b/src/meter_amiplus.cc index 5be3daf..823ead4 100644 --- a/src/meter_amiplus.cc +++ b/src/meter_amiplus.cc @@ -23,7 +23,7 @@ #include"util.h" struct MeterAmiplus : public virtual ElectricityMeter, public virtual MeterCommonImplementation { - MeterAmiplus(WMBus *bus, MeterInfo &mi); + MeterAmiplus(MeterInfo &mi); double totalEnergyConsumption(Unit u); double currentPowerConsumption(Unit u); @@ -41,8 +41,8 @@ private: string device_date_time_; }; -MeterAmiplus::MeterAmiplus(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::AMIPLUS, 0) +MeterAmiplus::MeterAmiplus(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::AMIPLUS, 0) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -86,9 +86,9 @@ MeterAmiplus::MeterAmiplus(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createAmiplus(WMBus *bus, MeterInfo &mi) +unique_ptr createAmiplus(MeterInfo &mi) { - return unique_ptr(new MeterAmiplus(bus, mi)); + return unique_ptr(new MeterAmiplus(mi)); } double MeterAmiplus::totalEnergyConsumption(Unit u) diff --git a/src/meter_apator08.cc b/src/meter_apator08.cc index 89add3b..a5237c3 100644 --- a/src/meter_apator08.cc +++ b/src/meter_apator08.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterApator08 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterApator08(WMBus *bus, MeterInfo &mi); + MeterApator08(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -38,13 +38,13 @@ private: double total_water_consumption_m3_ {}; }; -unique_ptr createApator08(WMBus *bus, MeterInfo &mi) +unique_ptr createApator08(MeterInfo &mi) { - return unique_ptr(new MeterApator08(bus, mi)); + return unique_ptr(new MeterApator08(mi)); } -MeterApator08::MeterApator08(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::APATOR08, 0x8614) // Not compliant! Will decode to APT. +MeterApator08::MeterApator08(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::APATOR08, 0x8614) // Not compliant! Will decode to APT. { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_apator162.cc b/src/meter_apator162.cc index a65d439..a52537d 100644 --- a/src/meter_apator162.cc +++ b/src/meter_apator162.cc @@ -24,7 +24,7 @@ using namespace std; struct MeterApator162 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterApator162(WMBus *bus, MeterInfo &mi); + MeterApator162(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -37,13 +37,13 @@ private: double total_water_consumption_m3_ {}; }; -unique_ptr createApator162(WMBus *bus, MeterInfo &mi) +unique_ptr createApator162(MeterInfo &mi) { - return unique_ptr(new MeterApator162(bus, mi)); + return unique_ptr(new MeterApator162(mi)); } -MeterApator162::MeterApator162(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::APATOR162, MANUFACTURER_APA) +MeterApator162::MeterApator162(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::APATOR162, MANUFACTURER_APA) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_cma12w.cc b/src/meter_cma12w.cc index 8963bd9..4b72a93 100644 --- a/src/meter_cma12w.cc +++ b/src/meter_cma12w.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterCMa12w : public virtual TempHygroMeter, public virtual MeterCommonImplementation { - MeterCMa12w(WMBus *bus, MeterInfo &mi); + MeterCMa12w(MeterInfo &mi); double currentTemperature(Unit u); double currentRelativeHumidity(); @@ -35,8 +35,8 @@ private: double average_temperature_1h_c_; }; -MeterCMa12w::MeterCMa12w(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::CMA12W, MANUFACTURER_ELV) +MeterCMa12w::MeterCMa12w(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::CMA12W, MANUFACTURER_ELV) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -57,9 +57,9 @@ MeterCMa12w::MeterCMa12w(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createCMa12w(WMBus *bus, MeterInfo &mi) +unique_ptr createCMa12w(MeterInfo &mi) { - return unique_ptr(new MeterCMa12w(bus, mi)); + return unique_ptr(new MeterCMa12w(mi)); } double MeterCMa12w::currentTemperature(Unit u) diff --git a/src/meter_ebzwmbe.cc b/src/meter_ebzwmbe.cc index 3ffada5..bd33c58 100644 --- a/src/meter_ebzwmbe.cc +++ b/src/meter_ebzwmbe.cc @@ -24,7 +24,7 @@ struct MeterEBZWMBE : public virtual ElectricityMeter, public virtual MeterCommonImplementation { - MeterEBZWMBE(WMBus *bus, MeterInfo &mi); + MeterEBZWMBE(MeterInfo &mi); double totalEnergyConsumption(Unit u); double currentPowerConsumption(Unit u); @@ -44,8 +44,8 @@ private: string customer_; }; -MeterEBZWMBE::MeterEBZWMBE(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::EBZWMBE, MANUFACTURER_EBZ) +MeterEBZWMBE::MeterEBZWMBE(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::EBZWMBE, MANUFACTURER_EBZ) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_NO_IV); @@ -87,9 +87,9 @@ MeterEBZWMBE::MeterEBZWMBE(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createEBZWMBE(WMBus *bus, MeterInfo &mi) +unique_ptr createEBZWMBE(MeterInfo &mi) { - return unique_ptr(new MeterEBZWMBE(bus, mi)); + return unique_ptr(new MeterEBZWMBE(mi)); } double MeterEBZWMBE::totalEnergyConsumption(Unit u) diff --git a/src/meter_ehzp.cc b/src/meter_ehzp.cc index f6564eb..b6deefa 100644 --- a/src/meter_ehzp.cc +++ b/src/meter_ehzp.cc @@ -24,7 +24,7 @@ struct MeterEHZP : public virtual ElectricityMeter, public virtual MeterCommonImplementation { - MeterEHZP(WMBus *bus, MeterInfo &mi); + MeterEHZP(MeterInfo &mi); double totalEnergyConsumption(Unit u); double currentPowerConsumption(Unit u); @@ -42,8 +42,8 @@ private: double on_time_h_ {}; }; -MeterEHZP::MeterEHZP(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::EHZP, MANUFACTURER_EMH) +MeterEHZP::MeterEHZP(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::EHZP, MANUFACTURER_EMH) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_NO_IV); @@ -75,9 +75,9 @@ MeterEHZP::MeterEHZP(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createEHZP(WMBus *bus, MeterInfo &mi) +unique_ptr createEHZP(MeterInfo &mi) { - return unique_ptr(new MeterEHZP(bus, mi)); + return unique_ptr(new MeterEHZP(mi)); } double MeterEHZP::totalEnergyConsumption(Unit u) diff --git a/src/meter_esyswm.cc b/src/meter_esyswm.cc index 1e5b102..4c46bac 100644 --- a/src/meter_esyswm.cc +++ b/src/meter_esyswm.cc @@ -26,7 +26,7 @@ struct MeterESYSWM : public virtual ElectricityMeter, public virtual MeterCommonImplementation { - MeterESYSWM(WMBus *bus, MeterInfo &mi); + MeterESYSWM(MeterInfo &mi); double totalEnergyConsumption(Unit u); double totalEnergyConsumptionTariff1(Unit u); @@ -61,8 +61,8 @@ private: string fabrication_no_; }; -MeterESYSWM::MeterESYSWM(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::ESYSWM, MANUFACTURER_ESY) +MeterESYSWM::MeterESYSWM(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::ESYSWM, MANUFACTURER_ESY) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_NO_IV); @@ -134,9 +134,9 @@ MeterESYSWM::MeterESYSWM(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createESYSWM(WMBus *bus, MeterInfo &mi) +unique_ptr createESYSWM(MeterInfo &mi) { - return unique_ptr(new MeterESYSWM(bus, mi)); + return unique_ptr(new MeterESYSWM(mi)); } double MeterESYSWM::totalEnergyConsumption(Unit u) diff --git a/src/meter_eurisii.cc b/src/meter_eurisii.cc index 51ae030..baa134e 100644 --- a/src/meter_eurisii.cc +++ b/src/meter_eurisii.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterEurisII : public virtual HeatCostMeter, public virtual MeterCommonImplementation { - MeterEurisII(WMBus *bus, MeterInfo &mi); + MeterEurisII(MeterInfo &mi); double currentConsumption(Unit u); string setDate(); @@ -41,8 +41,8 @@ private: uint16_t error_flags_; }; -MeterEurisII::MeterEurisII(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::EURISII, MANUFACTURER_INE) +MeterEurisII::MeterEurisII(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::EURISII, MANUFACTURER_INE) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -84,9 +84,9 @@ MeterEurisII::MeterEurisII(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createEurisII(WMBus *bus, MeterInfo &mi) +unique_ptr createEurisII(MeterInfo &mi) { - return unique_ptr(new MeterEurisII(bus, mi)); + return unique_ptr(new MeterEurisII(mi)); } double MeterEurisII::currentConsumption(Unit u) diff --git a/src/meter_fhkvdataiii.cc b/src/meter_fhkvdataiii.cc index 1bf60a0..7901d06 100644 --- a/src/meter_fhkvdataiii.cc +++ b/src/meter_fhkvdataiii.cc @@ -26,7 +26,7 @@ struct MeterFHKVDataIII : public virtual HeatCostMeter, public virtual MeterCommonImplementation { - MeterFHKVDataIII(WMBus *bus, MeterInfo &mi); + MeterFHKVDataIII(MeterInfo &mi); double currentPeriodEnergyConsumption(Unit u); string currentPeriodDate(); @@ -49,14 +49,14 @@ struct MeterFHKVDataIII : public virtual HeatCostMeter, public virtual MeterComm double temp_radiator_ {}; }; -unique_ptr createFHKVDataIII(WMBus *bus, MeterInfo &mi) +unique_ptr createFHKVDataIII(MeterInfo &mi) { - return unique_ptr(new MeterFHKVDataIII(bus, mi)); + return unique_ptr(new MeterFHKVDataIII(mi)); } -MeterFHKVDataIII::MeterFHKVDataIII(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::FHKVDATAIII, MANUFACTURER_TCH) +MeterFHKVDataIII::MeterFHKVDataIII(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::FHKVDATAIII, MANUFACTURER_TCH) { addMedia(0x80); // T telegrams addExpectedVersion(0x69); diff --git a/src/meter_hydrodigit.cc b/src/meter_hydrodigit.cc index 4ce07c0..9c020e0 100644 --- a/src/meter_hydrodigit.cc +++ b/src/meter_hydrodigit.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterHydrodigit : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterHydrodigit(WMBus *bus, MeterInfo &mi); + MeterHydrodigit(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -38,13 +38,13 @@ private: string meter_datetime_; }; -unique_ptr createHydrodigit(WMBus *bus, MeterInfo &mi) +unique_ptr createHydrodigit(MeterInfo &mi) { - return unique_ptr(new MeterHydrodigit(bus, mi)); + return unique_ptr(new MeterHydrodigit(mi)); } -MeterHydrodigit::MeterHydrodigit(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::HYDRODIGIT, MANUFACTURER_BMT) +MeterHydrodigit::MeterHydrodigit(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::HYDRODIGIT, MANUFACTURER_BMT) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_hydrus.cc b/src/meter_hydrus.cc index d1c1d7f..0519db6 100644 --- a/src/meter_hydrus.cc +++ b/src/meter_hydrus.cc @@ -24,7 +24,7 @@ using namespace std; struct MeterHydrus : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterHydrus(WMBus *bus, MeterInfo &mi); + MeterHydrus(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -44,8 +44,8 @@ private: double flow_temperature_c_ { 127 }; }; -MeterHydrus::MeterHydrus(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::HYDRUS, MANUFACTURER_DME) +MeterHydrus::MeterHydrus(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::HYDRUS, MANUFACTURER_DME) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -81,9 +81,9 @@ MeterHydrus::MeterHydrus(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createHydrus(WMBus *bus, MeterInfo &mi) +unique_ptr createHydrus(MeterInfo &mi) { - return unique_ptr(new MeterHydrus(bus, mi)); + return unique_ptr(new MeterHydrus(mi)); } void MeterHydrus::processContent(Telegram *t) diff --git a/src/meter_iperl.cc b/src/meter_iperl.cc index 4e8fbef..f0bedf3 100644 --- a/src/meter_iperl.cc +++ b/src/meter_iperl.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterIperl : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterIperl(WMBus *bus, MeterInfo &mi); + MeterIperl(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -40,8 +40,8 @@ private: double max_flow_m3h_ {}; }; -MeterIperl::MeterIperl(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::IPERL, MANUFACTURER_SEN) +MeterIperl::MeterIperl(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::IPERL, MANUFACTURER_SEN) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -64,9 +64,9 @@ MeterIperl::MeterIperl(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createIperl(WMBus *bus, MeterInfo &mi) +unique_ptr createIperl(MeterInfo &mi) { - return unique_ptr(new MeterIperl(bus, mi)); + return unique_ptr(new MeterIperl(mi)); } void MeterIperl::processContent(Telegram *t) diff --git a/src/meter_izar.cc b/src/meter_izar.cc index 06e6fe1..6c2fb68 100644 --- a/src/meter_izar.cc +++ b/src/meter_izar.cc @@ -45,7 +45,7 @@ typedef struct _izar_alarms { } izar_alarms; struct MeterIzar : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterIzar(WMBus *bus, MeterInfo &mi); + MeterIzar(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -75,13 +75,13 @@ private: vector keys; }; -unique_ptr createIzar(WMBus *bus, MeterInfo &mi) +unique_ptr createIzar(MeterInfo &mi) { - return unique_ptr(new MeterIzar(bus, mi)); + return unique_ptr(new MeterIzar(mi)); } -MeterIzar::MeterIzar(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::IZAR, MANUFACTURER_SAP) +MeterIzar::MeterIzar(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::IZAR, MANUFACTURER_SAP) { addManufacturer(MANUFACTURER_DME); diff --git a/src/meter_lansendw.cc b/src/meter_lansendw.cc index c4168a3..8786837 100644 --- a/src/meter_lansendw.cc +++ b/src/meter_lansendw.cc @@ -25,7 +25,7 @@ #define INFO_CODE_OPEN 0x0055 struct MeterLansenDW : public virtual DoorWindowDetector, public virtual MeterCommonImplementation { - MeterLansenDW(WMBus *bus, MeterInfo &mi); + MeterLansenDW(MeterInfo &mi); string status(); bool open(); @@ -40,8 +40,8 @@ private: }; -MeterLansenDW::MeterLansenDW(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::LANSENSM, MANUFACTURER_LAS) +MeterLansenDW::MeterLansenDW(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::LANSENSM, MANUFACTURER_LAS) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -57,9 +57,9 @@ MeterLansenDW::MeterLansenDW(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createLansenDW(WMBus *bus, MeterInfo &mi) +unique_ptr createLansenDW(MeterInfo &mi) { - return unique_ptr(new MeterLansenDW(bus, mi)); + return unique_ptr(new MeterLansenDW(mi)); } bool MeterLansenDW::open() diff --git a/src/meter_lansenpu.cc b/src/meter_lansenpu.cc index ac26123..191542b 100644 --- a/src/meter_lansenpu.cc +++ b/src/meter_lansenpu.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterLansenPU : public virtual PulseCounter, public virtual MeterCommonImplementation { - MeterLansenPU(WMBus *bus, MeterInfo &mi); + MeterLansenPU(MeterInfo &mi); double counterA(); double counterB(); @@ -38,8 +38,8 @@ private: }; -MeterLansenPU::MeterLansenPU(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::LANSENPU, MANUFACTURER_LAS) +MeterLansenPU::MeterLansenPU(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::LANSENPU, MANUFACTURER_LAS) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -60,9 +60,9 @@ MeterLansenPU::MeterLansenPU(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createLansenPU(WMBus *bus, MeterInfo &mi) +unique_ptr createLansenPU(MeterInfo &mi) { - return unique_ptr(new MeterLansenPU(bus, mi)); + return unique_ptr(new MeterLansenPU(mi)); } double MeterLansenPU::counterA() diff --git a/src/meter_lansensm.cc b/src/meter_lansensm.cc index 6a53ca8..42315b7 100644 --- a/src/meter_lansensm.cc +++ b/src/meter_lansensm.cc @@ -25,7 +25,7 @@ #define INFO_CODE_TEST 0x0008 struct MeterLansenSM : public virtual SmokeDetector, public virtual MeterCommonImplementation { - MeterLansenSM(WMBus *bus, MeterInfo &mi); + MeterLansenSM(MeterInfo &mi); string status(); bool smokeDetected(); @@ -40,8 +40,8 @@ private: }; -MeterLansenSM::MeterLansenSM(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::LANSENSM, MANUFACTURER_LAS) +MeterLansenSM::MeterLansenSM(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::LANSENSM, MANUFACTURER_LAS) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -57,9 +57,9 @@ MeterLansenSM::MeterLansenSM(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createLansenSM(WMBus *bus, MeterInfo &mi) +unique_ptr createLansenSM(MeterInfo &mi) { - return unique_ptr(new MeterLansenSM(bus, mi)); + return unique_ptr(new MeterLansenSM(mi)); } bool MeterLansenSM::smokeDetected() diff --git a/src/meter_lansenth.cc b/src/meter_lansenth.cc index 1769452..1ddbc78 100644 --- a/src/meter_lansenth.cc +++ b/src/meter_lansenth.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterLansenTH : public virtual TempHygroMeter, public virtual MeterCommonImplementation { - MeterLansenTH(WMBus *bus, MeterInfo &mi); + MeterLansenTH(MeterInfo &mi); double currentTemperature(Unit u); double currentRelativeHumidity(); @@ -39,8 +39,8 @@ private: double average_relative_humidity_24h_rh_ {}; }; -MeterLansenTH::MeterLansenTH(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::LANSENTH, MANUFACTURER_LAS) +MeterLansenTH::MeterLansenTH(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::LANSENTH, MANUFACTURER_LAS) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -81,9 +81,9 @@ MeterLansenTH::MeterLansenTH(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createLansenTH(WMBus *bus, MeterInfo &mi) +unique_ptr createLansenTH(MeterInfo &mi) { - return unique_ptr(new MeterLansenTH(bus, mi)); + return unique_ptr(new MeterLansenTH(mi)); } double MeterLansenTH::currentTemperature(Unit u) diff --git a/src/meter_mkradio3.cc b/src/meter_mkradio3.cc index b9e0d0e..7034cb9 100644 --- a/src/meter_mkradio3.cc +++ b/src/meter_mkradio3.cc @@ -26,7 +26,7 @@ using namespace std; struct MKRadio3 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MKRadio3(WMBus *bus, MeterInfo &mi); + MKRadio3(MeterInfo &mi); double totalWaterConsumption(Unit u); bool hasTotalWaterConsumption(); @@ -40,8 +40,8 @@ private: double target_water_consumption_m3_ {}; }; -MKRadio3::MKRadio3(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::MKRADIO3, MANUFACTURER_TCH) +MKRadio3::MKRadio3(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::MKRADIO3, MANUFACTURER_TCH) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -63,9 +63,9 @@ MKRadio3::MKRadio3(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createMKRadio3(WMBus *bus, MeterInfo &mi) +unique_ptr createMKRadio3(MeterInfo &mi) { - return unique_ptr(new MKRadio3(bus, mi)); + return unique_ptr(new MKRadio3(mi)); } void MKRadio3::processContent(Telegram *t) diff --git a/src/meter_multical21.cc b/src/meter_multical21.cc index 5e14967..1034ee7 100644 --- a/src/meter_multical21.cc +++ b/src/meter_multical21.cc @@ -39,7 +39,7 @@ using namespace std; #define INFO_CODE_BURST_SHIFT (4+9) struct MeterMultical21 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterMultical21(WMBus *bus, MeterInfo &mi, MeterType mt); + MeterMultical21(MeterInfo &mi, MeterType mt); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -91,8 +91,8 @@ private: int expected_version_ {}; // 0x1b for Multical21 and 0x1d for FlowIQ3100 }; -MeterMultical21::MeterMultical21(WMBus *bus, MeterInfo &mi, MeterType mt) : - MeterCommonImplementation(bus, mi, mt, MANUFACTURER_KAM) +MeterMultical21::MeterMultical21(MeterInfo &mi, MeterType mt) : + MeterCommonImplementation(mi, mt, MANUFACTURER_KAM) { setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); @@ -219,22 +219,22 @@ bool MeterMultical21::hasExternalTemperature() return has_external_temperature_; } -unique_ptr createMulticalWaterMeter(WMBus *bus, MeterInfo &mi, MeterType mt) +unique_ptr createMulticalWaterMeter(MeterInfo &mi, MeterType mt) { if (mt != MeterType::MULTICAL21 && mt != MeterType::FLOWIQ3100) { error("Internal error! Not a proper meter type when creating a multical21 style meter.\n"); } - return unique_ptr(new MeterMultical21(bus,mi,mt)); + return unique_ptr(new MeterMultical21(mi,mt)); } -unique_ptr createMultical21(WMBus *bus, MeterInfo &mi) +unique_ptr createMultical21(MeterInfo &mi) { - return createMulticalWaterMeter(bus, mi, MeterType::MULTICAL21); + return createMulticalWaterMeter(mi, MeterType::MULTICAL21); } -unique_ptr createFlowIQ3100(WMBus *bus, MeterInfo &mi) +unique_ptr createFlowIQ3100(MeterInfo &mi) { - return createMulticalWaterMeter(bus, mi, MeterType::FLOWIQ3100); + return createMulticalWaterMeter(mi, MeterType::FLOWIQ3100); } void MeterMultical21::processContent(Telegram *t) diff --git a/src/meter_multical302.cc b/src/meter_multical302.cc index 1339ea4..eca76a6 100644 --- a/src/meter_multical302.cc +++ b/src/meter_multical302.cc @@ -31,7 +31,7 @@ #define INFO_CODE_VOLTAGE_TOO_LOW 128 struct MeterMultical302 : public virtual HeatMeter, public virtual MeterCommonImplementation { - MeterMultical302(WMBus *bus, MeterInfo &mi); + MeterMultical302(MeterInfo &mi); double totalEnergyConsumption(Unit u); double targetEnergyConsumption(Unit u); @@ -51,8 +51,8 @@ private: string target_date_ {}; }; -MeterMultical302::MeterMultical302(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::MULTICAL302, MANUFACTURER_KAM) +MeterMultical302::MeterMultical302(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::MULTICAL302, MANUFACTURER_KAM) { setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); @@ -92,8 +92,8 @@ MeterMultical302::MeterMultical302(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createMultical302(WMBus *bus, MeterInfo &mi) { - return unique_ptr(new MeterMultical302(bus, mi)); +unique_ptr createMultical302(MeterInfo &mi) { + return unique_ptr(new MeterMultical302(mi)); } double MeterMultical302::totalEnergyConsumption(Unit u) diff --git a/src/meter_multical403.cc b/src/meter_multical403.cc index b281de2..d2a10df 100644 --- a/src/meter_multical403.cc +++ b/src/meter_multical403.cc @@ -33,7 +33,7 @@ #define INFO_CODE_TEMP_DIFF_WRONG_POLARITY 128 struct MeterMultical403 : public virtual HeatMeter, public virtual MeterCommonImplementation { - MeterMultical403(WMBus *bus, MeterInfo &mi); + MeterMultical403(MeterInfo &mi); double totalEnergyConsumption(Unit u); string status(); @@ -60,8 +60,8 @@ private: string target_date_ {}; }; -MeterMultical403::MeterMultical403(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::MULTICAL403, MANUFACTURER_KAM) +MeterMultical403::MeterMultical403(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::MULTICAL403, MANUFACTURER_KAM) { setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); @@ -108,8 +108,8 @@ MeterMultical403::MeterMultical403(WMBus *bus, MeterInfo &mi) : true, true); } -unique_ptr createMultical403(WMBus *bus, MeterInfo &mi) { - return unique_ptr(new MeterMultical403(bus, mi)); +unique_ptr createMultical403(MeterInfo &mi) { + return unique_ptr(new MeterMultical403(mi)); } double MeterMultical403::totalEnergyConsumption(Unit u) diff --git a/src/meter_omnipower.cc b/src/meter_omnipower.cc index c564e6a..2e3f5ee 100644 --- a/src/meter_omnipower.cc +++ b/src/meter_omnipower.cc @@ -23,7 +23,7 @@ #include"util.h" struct MeterOmnipower : public virtual ElectricityMeter, public virtual MeterCommonImplementation { - MeterOmnipower(WMBus *bus, MeterInfo &mi); + MeterOmnipower(MeterInfo &mi); double totalEnergyConsumption(Unit u); @@ -34,13 +34,13 @@ private: double total_energy_kwh_ {}; }; -unique_ptr createOmnipower(WMBus *bus, MeterInfo &mi) +unique_ptr createOmnipower(MeterInfo &mi) { - return unique_ptr(new MeterOmnipower(bus, mi)); + return unique_ptr(new MeterOmnipower(mi)); } -MeterOmnipower::MeterOmnipower(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::OMNIPOWER, MANUFACTURER_KAM) +MeterOmnipower::MeterOmnipower(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::OMNIPOWER, MANUFACTURER_KAM) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_q400.cc b/src/meter_q400.cc index d5e6ae1..2b85de9 100644 --- a/src/meter_q400.cc +++ b/src/meter_q400.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterQ400 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterQ400(WMBus *bus, MeterInfo &mi); + MeterQ400(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -42,13 +42,13 @@ private: double consumption_at_set_date_m3_ {}; }; -unique_ptr createQ400(WMBus *bus, MeterInfo &mi) +unique_ptr createQ400(MeterInfo &mi) { - return unique_ptr(new MeterQ400(bus, mi)); + return unique_ptr(new MeterQ400(mi)); } -MeterQ400::MeterQ400(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::Q400, MANUFACTURER_AXI) +MeterQ400::MeterQ400(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::Q400, MANUFACTURER_AXI) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_qcaloric.cc b/src/meter_qcaloric.cc index 05b2d2e..b421979 100644 --- a/src/meter_qcaloric.cc +++ b/src/meter_qcaloric.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterQCaloric : public virtual HeatCostMeter, public virtual MeterCommonImplementation { - MeterQCaloric(WMBus *bus, MeterInfo &mi); + MeterQCaloric(MeterInfo &mi); double currentConsumption(Unit u); string setDate(); @@ -45,8 +45,8 @@ private: string device_date_time_; }; -MeterQCaloric::MeterQCaloric(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::QCALORIC, MANUFACTURER_QDS) +MeterQCaloric::MeterQCaloric(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::QCALORIC, MANUFACTURER_QDS) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -102,9 +102,9 @@ MeterQCaloric::MeterQCaloric(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createQCaloric(WMBus *bus, MeterInfo &mi) +unique_ptr createQCaloric(MeterInfo &mi) { - return unique_ptr(new MeterQCaloric(bus, mi)); + return unique_ptr(new MeterQCaloric(mi)); } double MeterQCaloric::currentConsumption(Unit u) diff --git a/src/meter_rfmamb.cc b/src/meter_rfmamb.cc index 2fb0533..6ccf839 100644 --- a/src/meter_rfmamb.cc +++ b/src/meter_rfmamb.cc @@ -22,7 +22,7 @@ #include"wmbus_utils.h" struct MeterRfmAmb : public virtual TempHygroMeter, public virtual MeterCommonImplementation { - MeterRfmAmb(WMBus *bus, MeterInfo &mi); + MeterRfmAmb(MeterInfo &mi); double currentTemperature(Unit u); double maximumTemperature(Unit u); @@ -58,8 +58,8 @@ private: string device_date_time_; }; -MeterRfmAmb::MeterRfmAmb(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::RFMAMB, MANUFACTURER_BMT) +MeterRfmAmb::MeterRfmAmb(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::RFMAMB, MANUFACTURER_BMT) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); @@ -145,9 +145,9 @@ MeterRfmAmb::MeterRfmAmb(WMBus *bus, MeterInfo &mi) : false, true); } -unique_ptr createRfmAmb(WMBus *bus, MeterInfo &mi) +unique_ptr createRfmAmb(MeterInfo &mi) { - return unique_ptr(new MeterRfmAmb(bus, mi)); + return unique_ptr(new MeterRfmAmb(mi)); } double MeterRfmAmb::currentTemperature(Unit u) diff --git a/src/meter_rfmtx1.cc b/src/meter_rfmtx1.cc index 4168f17..6326246 100644 --- a/src/meter_rfmtx1.cc +++ b/src/meter_rfmtx1.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterRfmTX1 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterRfmTX1(WMBus *bus, MeterInfo &mi); + MeterRfmTX1(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -38,13 +38,13 @@ private: string meter_datetime_; }; -unique_ptr createRfmTX1(WMBus *bus, MeterInfo &mi) +unique_ptr createRfmTX1(MeterInfo &mi) { - return unique_ptr(new MeterRfmTX1(bus, mi)); + return unique_ptr(new MeterRfmTX1(mi)); } -MeterRfmTX1::MeterRfmTX1(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::RFMTX1, MANUFACTURER_BMT) +MeterRfmTX1::MeterRfmTX1(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::RFMTX1, MANUFACTURER_BMT) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_supercom587.cc b/src/meter_supercom587.cc index 87118ba..9ea68ef 100644 --- a/src/meter_supercom587.cc +++ b/src/meter_supercom587.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterSupercom587 : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterSupercom587(WMBus *bus, MeterInfo &mi); + MeterSupercom587(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -37,13 +37,13 @@ private: double total_water_consumption_m3_ {}; }; -unique_ptr createSupercom587(WMBus *bus, MeterInfo &mi) +unique_ptr createSupercom587(MeterInfo &mi) { - return unique_ptr(new MeterSupercom587(bus, mi)); + return unique_ptr(new MeterSupercom587(mi)); } -MeterSupercom587::MeterSupercom587(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::SUPERCOM587, MANUFACTURER_SON) +MeterSupercom587::MeterSupercom587(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::SUPERCOM587, MANUFACTURER_SON) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meter_vario451.cc b/src/meter_vario451.cc index c2ebfe1..591ebe3 100644 --- a/src/meter_vario451.cc +++ b/src/meter_vario451.cc @@ -25,7 +25,7 @@ struct MeterVario451 : public virtual HeatMeter, public virtual MeterCommonImplementation { - MeterVario451(WMBus *bus, MeterInfo &mi); + MeterVario451(MeterInfo &mi); double totalEnergyConsumption(Unit u); double currentPeriodEnergyConsumption(Unit u); @@ -40,13 +40,13 @@ struct MeterVario451 : public virtual HeatMeter, public virtual MeterCommonImple double prev_energy_gj_ {}; }; -unique_ptr createVario451(WMBus *bus, MeterInfo &mi) +unique_ptr createVario451(MeterInfo &mi) { - return unique_ptr(new MeterVario451(bus, mi)); + return unique_ptr(new MeterVario451(mi)); } -MeterVario451::MeterVario451(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::VARIO451, MANUFACTURER_TCH) +MeterVario451::MeterVario451(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::VARIO451, MANUFACTURER_TCH) { addMedia(0x04); // C telegrams addMedia(0xC3); // T telegrams diff --git a/src/meter_waterstarm.cc b/src/meter_waterstarm.cc index f4340f4..f11e607 100644 --- a/src/meter_waterstarm.cc +++ b/src/meter_waterstarm.cc @@ -25,7 +25,7 @@ using namespace std; struct MeterWaterstarM : public virtual WaterMeter, public virtual MeterCommonImplementation { - MeterWaterstarM(WMBus *bus, MeterInfo &mi); + MeterWaterstarM(MeterInfo &mi); // Total water counted through the meter double totalWaterConsumption(Unit u); @@ -44,13 +44,13 @@ private: string parameter_set_ {}; }; -unique_ptr createWaterstarM(WMBus *bus, MeterInfo &mi) +unique_ptr createWaterstarM(MeterInfo &mi) { - return unique_ptr(new MeterWaterstarM(bus, mi)); + return unique_ptr(new MeterWaterstarM(mi)); } -MeterWaterstarM::MeterWaterstarM(WMBus *bus, MeterInfo &mi) : - MeterCommonImplementation(bus, mi, MeterType::WATERSTARM, MANUFACTURER_DWZ) +MeterWaterstarM::MeterWaterstarM(MeterInfo &mi) : + MeterCommonImplementation(mi, MeterType::WATERSTARM, MANUFACTURER_DWZ) { setExpectedTPLSecurityMode(TPLSecurityMode::AES_CBC_IV); diff --git a/src/meters.cc b/src/meters.cc index 9835ba9..56b2096 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -25,19 +25,20 @@ #include #include -MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, MeterInfo &mi, - MeterType type, int manufacturer) : - type_(type), name_(mi.name), bus_(bus) +MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi, + MeterType type, + int manufacturer) : + type_(type), name_(mi.name) { ids_ = splitMatchExpressions(mi.id); if (mi.key.length() > 0) { hex2bin(mi.key, &meter_keys_.confidentiality_key); } - if (bus->type() == DEVICE_SIMULATOR) + /*if (bus->type() == DEVICE_SIMULATOR) { meter_keys_.simulation = true; - } + }*/ if (manufacturer) { manufacturers_.insert(manufacturer); } @@ -47,7 +48,7 @@ MeterCommonImplementation::MeterCommonImplementation(WMBus *bus, MeterInfo &mi, for (auto j : mi.jsons) { addJson(j); } - MeterCommonImplementation::bus()->onTelegram([this](vectorinput_frame){return this->handleTelegram(input_frame);}); + //MeterCommonImplementation::bus()->onTelegram([this](vectorinput_frame){return this->handleTelegram(input_frame);}); } void MeterCommonImplementation::addConversions(std::vector cs) @@ -139,11 +140,6 @@ string MeterCommonImplementation::name() return name_; } -WMBus *MeterCommonImplementation::bus() -{ - return bus_; -} - void MeterCommonImplementation::onUpdate(function cb) { on_update_.push_back(cb); diff --git a/src/meters.h b/src/meters.h index 1542b99..2328e1c 100644 --- a/src/meters.h +++ b/src/meters.h @@ -101,7 +101,6 @@ struct Meter virtual string name() = 0; virtual MeterType type() = 0; virtual vector media() = 0; - virtual WMBus *bus() = 0; virtual string datetimeOfUpdateHumanReadable() = 0; virtual string datetimeOfUpdateRobot() = 0; @@ -222,36 +221,36 @@ struct GenericMeter : public virtual Meter { string toMeterName(MeterType mt); MeterType toMeterType(string& type); LinkModeSet toMeterLinkModeSet(string& type); -unique_ptr createMultical21(WMBus *bus, MeterInfo &m); -unique_ptr createFlowIQ3100(WMBus *bus, MeterInfo &m); -unique_ptr createMultical302(WMBus *bus, MeterInfo &m); -unique_ptr createMultical403(WMBus *bus, MeterInfo &m); -unique_ptr createVario451(WMBus *bus, MeterInfo &m); -unique_ptr createWaterstarM(WMBus *bus, MeterInfo &m); -unique_ptr createOmnipower(WMBus *bus, MeterInfo &m); -unique_ptr createAmiplus(WMBus *bus, MeterInfo &m); -unique_ptr createSupercom587(WMBus *bus, MeterInfo &m); -unique_ptr createMKRadio3(WMBus *bus, MeterInfo &m); -unique_ptr createApator08(WMBus *bus, MeterInfo &m); -unique_ptr createApator162(WMBus *bus, MeterInfo &m); -unique_ptr createIperl(WMBus *bus, MeterInfo &m); -unique_ptr createHydrus(WMBus *bus, MeterInfo &m); -unique_ptr createHydrodigit(WMBus *bus, MeterInfo &m); -unique_ptr createIzar(WMBus *bus, MeterInfo &m); -unique_ptr createQ400(WMBus *bus, MeterInfo &m); -unique_ptr createQCaloric(WMBus *bus, MeterInfo &m); -unique_ptr createEurisII(WMBus *bus, MeterInfo &m); -unique_ptr createFHKVDataIII(WMBus *bus, MeterInfo &m); -unique_ptr createLansenTH(WMBus *bus, MeterInfo &m); -unique_ptr createLansenSM(WMBus *bus, MeterInfo &m); -unique_ptr createLansenPU(WMBus *bus, MeterInfo &m); -unique_ptr createLansenDW(WMBus *bus, MeterInfo &m); -unique_ptr createCMa12w(WMBus *bus, MeterInfo &m); -unique_ptr createRfmAmb(WMBus *bus, MeterInfo &m); -unique_ptr createRfmTX1(WMBus *bus, MeterInfo &m); -unique_ptr createEHZP(WMBus *bus, MeterInfo &m); -unique_ptr createESYSWM(WMBus *bus, MeterInfo &m); -unique_ptr createEBZWMBE(WMBus *bus, MeterInfo &m); -GenericMeter *createGeneric(WMBus *bus, MeterInfo &m); +unique_ptr createMultical21(MeterInfo &m); +unique_ptr createFlowIQ3100(MeterInfo &m); +unique_ptr createMultical302(MeterInfo &m); +unique_ptr createMultical403(MeterInfo &m); +unique_ptr createVario451(MeterInfo &m); +unique_ptr createWaterstarM(MeterInfo &m); +unique_ptr createOmnipower(MeterInfo &m); +unique_ptr createAmiplus(MeterInfo &m); +unique_ptr createSupercom587(MeterInfo &m); +unique_ptr createMKRadio3(MeterInfo &m); +unique_ptr createApator08(MeterInfo &m); +unique_ptr createApator162(MeterInfo &m); +unique_ptr createIperl(MeterInfo &m); +unique_ptr createHydrus(MeterInfo &m); +unique_ptr createHydrodigit(MeterInfo &m); +unique_ptr createIzar(MeterInfo &m); +unique_ptr createQ400(MeterInfo &m); +unique_ptr createQCaloric(MeterInfo &m); +unique_ptr createEurisII(MeterInfo &m); +unique_ptr createFHKVDataIII(MeterInfo &m); +unique_ptr createLansenTH(MeterInfo &m); +unique_ptr createLansenSM(MeterInfo &m); +unique_ptr createLansenPU(MeterInfo &m); +unique_ptr createLansenDW(MeterInfo &m); +unique_ptr createCMa12w(MeterInfo &m); +unique_ptr createRfmAmb(MeterInfo &m); +unique_ptr createRfmTX1(MeterInfo &m); +unique_ptr createEHZP(MeterInfo &m); +unique_ptr createESYSWM(MeterInfo &m); +unique_ptr createEBZWMBE(MeterInfo &m); +GenericMeter *createGeneric(MeterInfo &m); #endif diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index 1bbd530..b0b493f 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -43,7 +43,6 @@ struct MeterCommonImplementation : public virtual Meter string name(); MeterType type(); vector media(); - WMBus *bus(); ELLSecurityMode expectedELLSecurityMode(); TPLSecurityMode expectedTPLSecurityMode(); @@ -61,8 +60,7 @@ struct MeterCommonImplementation : public virtual Meter double getRecordAsDouble(std::string record); uint16_t getRecordAsUInt16(std::string record); - MeterCommonImplementation(WMBus *bus, MeterInfo &mi, - MeterType type, int manufacturer); + MeterCommonImplementation(MeterInfo &mi, MeterType type, int manufacturer); ~MeterCommonImplementation() = default; diff --git a/src/serial.cc b/src/serial.cc index 3cffc70..6e80d3b 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,6 +76,7 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager unique_ptr createSerialDeviceSimulator(); void listenTo(SerialDevice *sd, function cb); + void onDisappear(SerialDevice *sd, function cb); void stop(); void startEventLoop(); void waitForStop(); @@ -87,13 +88,13 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager void closeAll(); time_t reopenAfter() { return reopen_after_seconds_; } - int startRegularCallback(int seconds, function callback, string name); + int startRegularCallback(string name, int seconds, function callback); void stopRegularCallback(int id); void resetInitiated() { debug("(serial) initiate reset\n"); resetting_ = true; } void resetCompleted() { debug("(serial) reset completed\n"); resetting_ = false; } vector listSerialDevices(); - + SerialDevice *lookup(std::string device); private: @@ -118,8 +119,9 @@ private: pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER; vector devices_; vector timers_; - pthread_mutex_t timer_lock_ = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_t timers_lock_ = PTHREAD_MUTEX_INITIALIZER; bool calling_timers_ {}; + pthread_mutex_t timer_thread_lock_ = PTHREAD_MUTEX_INITIALIZER; }; SerialCommunicationManagerImp::~SerialCommunicationManagerImp() @@ -129,14 +131,15 @@ SerialCommunicationManagerImp::~SerialCommunicationManagerImp() // Stop the event loop. stop(); // Grab the event_loop_lock. This can only be done when the eventLoop has stopped running. - pthread_mutex_lock(&event_loop_lock_); + LOCK("(serial)", "destructor", event_loop_lock_); // Now we can be sure the eventLoop has stopped and it is safe to // free this Manager object. } struct SerialDeviceImp : public SerialDevice { - int fd() { return fd_; } + void doNotUseCallbacks() { no_callbacks_ = true; } + bool skippingCallbacks() { return no_callbacks_; } void fill(vector &data) {}; int receive(vector *data); bool working() { return fd_ != -1; } @@ -146,16 +149,19 @@ struct SerialDeviceImp : public SerialDevice void setIsFile() { is_file_ = true; } void setIsStdin() { is_stdin_ = true; } string device() { return ""; } + int fd() { return fd_; } protected: pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER; function on_data_; + function on_disappear_; int fd_ = -1; bool expecting_ascii_ {}; // If true, print using safeString instead if bin2hex bool is_file_ = false; bool is_stdin_ = false; + bool no_callbacks_ = false; friend struct SerialCommunicationManagerImp; @@ -166,7 +172,7 @@ int SerialDeviceImp::receive(vector *data) { bool close_me = false; - pthread_mutex_lock(&read_lock_); + LOCK("(serial)", "receive", read_lock_); data->clear(); int num_read = 0; @@ -216,7 +222,7 @@ int SerialDeviceImp::receive(vector *data) } } - pthread_mutex_unlock(&read_lock_); + UNLOCK("(serial)", "receive", read_lock_); if (close_me) close(); @@ -302,6 +308,11 @@ void SerialDeviceTTY::close() ::flock(fd_, LOCK_UN); ::close(fd_); fd_ = -1; + if (on_disappear_) + { + on_disappear_(); + on_disappear_ = NULL; + } manager_->closed(this); verbose("(serialtty) closed %s\n", device_.c_str()); } @@ -348,7 +359,7 @@ bool SerialDeviceTTY::send(vector &data) { if (data.size() == 0) return true; - pthread_mutex_lock(&write_lock_); + LOCK("(serial)", "send", write_lock_); bool rc = true; int n = data.size(); @@ -370,7 +381,7 @@ bool SerialDeviceTTY::send(vector &data) } end: - pthread_mutex_unlock(&write_lock_); + UNLOCK("(serial)", "send", write_lock_); return rc; } @@ -476,7 +487,7 @@ bool SerialDeviceCommand::send(vector &data) { if (data.size() == 0) return true; - pthread_mutex_lock(&write_lock_); + LOCK("(serial)", "sendcmd", write_lock_); bool rc = true; int n = data.size(); @@ -498,7 +509,7 @@ bool SerialDeviceCommand::send(vector &data) } end: - pthread_mutex_unlock(&write_lock_); + UNLOCK("(serial)", "sendcmd", write_lock_); return rc; } @@ -629,7 +640,7 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s // Block the event loop until everything is configured. if (start_event_loop) { - pthread_mutex_lock(&event_loop_lock_); + LOCK("(serial)", "constructor", event_loop_lock_); pthread_create(&select_thread_, NULL, startLoop, this); } wakeMeUpOnSigChld(select_thread_); @@ -649,7 +660,8 @@ void *SerialCommunicationManagerImp::runTimers(void *a) auto t = (SerialCommunicationManagerImp*)a; t->executeTimerCallbacks(); t->calling_timers_ = false; - pthread_mutex_unlock(&t->timer_lock_); + // Now unlock the previously trylocked mutex. + UNLOCK("(serial)", "runTimers", t->timer_thread_lock_); return NULL; } @@ -688,6 +700,17 @@ void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function si->on_data_ = cb; } +void SerialCommunicationManagerImp::onDisappear(SerialDevice *sd, function cb) +{ + if (sd == NULL) return; + SerialDeviceImp *si = dynamic_cast(sd); + if (!si) + { + error("Internal error: Invalid serial device passed to onDisappear.\n"); + } + si->on_disappear_ = cb; +} + void SerialCommunicationManagerImp::stop() { // Notify the main waitForStop thread that we are stopped! @@ -709,20 +732,20 @@ void SerialCommunicationManagerImp::stop() void SerialCommunicationManagerImp::startEventLoop() { // Release the event loop! - pthread_mutex_unlock(&event_loop_lock_); + UNLOCK("(serial)", "startEventLoop", event_loop_lock_); } void SerialCommunicationManagerImp::waitForStop() { debug("(serial) waiting for stop\n"); - expect_devices_to_work_ = true; + //expect_devices_to_work_ = true; main_thread_ = pthread_self(); while (running_) { - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "waitForStop", devices_lock_); size_t s = devices_.size(); - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "waitForStop", devices_lock_); if (s == 0) { break; @@ -751,19 +774,19 @@ void SerialCommunicationManagerImp::setReopenAfter(int seconds) void SerialCommunicationManagerImp::opened(SerialDeviceImp *sd) { - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "opened", devices_lock_); max_fd_ = max(sd->fd(), max_fd_); devices_.push_back(sd); if (signalsInstalled()) { if (select_thread_) pthread_kill(select_thread_, SIGUSR1); } - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "opened", devices_lock_); } void SerialCommunicationManagerImp::closed(SerialDeviceImp *sd) { - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "closed", devices_lock_); auto p = find(devices_.begin(), devices_.end(), sd); if (p != devices_.end()) { @@ -782,17 +805,23 @@ void SerialCommunicationManagerImp::closed(SerialDeviceImp *sd) debug("(serial) no devices working emergency exit!\n"); stop(); } - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "opened", devices_lock_); } void SerialCommunicationManagerImp::closeAll() { - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "closeAll", devices_lock_); vector copy = devices_; - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "closeAll", devices_lock_); for (SerialDeviceImp *d : copy) { + if (d->on_disappear_) + { + d->on_disappear_(); + d->on_disappear_ = NULL; + } + closed(d); } } @@ -800,7 +829,11 @@ void SerialCommunicationManagerImp::closeAll() void SerialCommunicationManagerImp::executeTimerCallbacks() { time_t curr = time(NULL); - for (Timer &t : timers_) + LOCK("(serial)", "executeTimerCallbacks", timers_lock_); + vector timers_copy = timers_; + UNLOCK("(serial)", "executeTimerCallbacks", timers_lock_); + + for (Timer &t : timers_copy) { if (t.isTime(curr)) { @@ -831,69 +864,57 @@ void *SerialCommunicationManagerImp::eventLoop() { fd_set readfds; - pthread_mutex_lock(&event_loop_lock_); + LOCK("(serial)", "eventLoop", event_loop_lock_); while (running_) { FD_ZERO(&readfds); bool all_working = true; - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "opened", devices_lock_); for (SerialDevice *d : devices_) { - FD_SET(d->fd(), &readfds); + if (!d->skippingCallbacks()) + { + FD_SET(d->fd(), &readfds); + } if (!d->working()) all_working = false; } - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "opened", devices_lock_); - if (!all_working && resetting_ == false) + if (!all_working && expect_devices_to_work_ && resetting_ == false) { debug("(serial) not all devices working, emergency exit!\n"); stop(); break; } - int default_timeout = isInternalTestingEnabled() ? CHECKSTATUS_TIMER_INTERNAL_TESTING : CHECKSTATUS_TIMER; - - struct timeval timeout { default_timeout, 0 }; + // Perform a select call every second. + struct timeval timeout { 1, 0 }; time_t curr = time(NULL); - // Default timeout is once every 10 seconds. See timings.h - // This means that we will poll the status of tty:s and commands - // once every 10 seconds. - - // However sometimes the timeout should be shorter. - // We might have an exit coming up... if (exit_after_seconds_ > 0) { time_t diff = curr-start_time_; - if (diff > exit_after_seconds_) { + if (diff > exit_after_seconds_) + { + // Running time limit hit, now stop. verbose("(serial) exit after %ld seconds\n", diff); stop(); break; } - timeout.tv_sec = exit_after_seconds_ - diff; - if (timeout.tv_sec < 0) timeout.tv_sec = 0; - } - - // We might have a regular timer callback coming up. - if (timers_.size() > 0 && !calling_timers_) - { - time_t remaining = calculateTimeToNearestTimerCallback(curr); - if (remaining < 0) remaining = 1; - if (timeout.tv_sec > remaining) timeout.tv_sec = remaining; } trace("(trace serial) select timeout %d s\n", timeout.tv_sec); bool num_devices = 0; - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "eventLoop2", devices_lock_); for (SerialDevice *d : devices_) { d->checkIfShouldReopen(); } num_devices = devices_.size(); - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "eventLoop2", devices_lock_); if (num_devices == 0 && expect_devices_to_work_ && resetting_ == false) { @@ -912,7 +933,7 @@ void *SerialCommunicationManagerImp::eventLoop() { // Something has happened that caused the sleeping select to wake up. vector to_be_notified; - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "eventLoop3", devices_lock_); for (SerialDevice *d : devices_) { if (FD_ISSET(d->fd(), &readfds)) @@ -921,7 +942,7 @@ void *SerialCommunicationManagerImp::eventLoop() to_be_notified.push_back(si); } } - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "eventLoop3", devices_lock_); for (SerialDeviceImp *si : to_be_notified) { @@ -933,12 +954,12 @@ void *SerialCommunicationManagerImp::eventLoop() } vector non_working; - pthread_mutex_lock(&devices_lock_); + LOCK("(serial)", "eventLoop4", devices_lock_); for (SerialDeviceImp *d : devices_) { if (!d->working()) non_working.push_back(d); } - pthread_mutex_unlock(&devices_lock_); + UNLOCK("(serial)", "eventLoop4", devices_lock_); for (SerialDeviceImp *d : non_working) { @@ -954,7 +975,7 @@ void *SerialCommunicationManagerImp::eventLoop() if (timer_found) { - int rc = pthread_mutex_trylock(&timer_lock_); + int rc = pthread_mutex_trylock(&timer_thread_lock_); // Only start timer thread if it is not running already. if (rc == 0) { @@ -969,7 +990,7 @@ void *SerialCommunicationManagerImp::eventLoop() } } - if (non_working.size() > 0 && resetting_ == false) + if (non_working.size() > 0 && expect_devices_to_work_ && resetting_ == false) { debug("(serial) non working devices found, exiting.\n"); stop(); @@ -977,7 +998,7 @@ void *SerialCommunicationManagerImp::eventLoop() } } verbose("(serial) event loop stopped!\n"); - pthread_mutex_unlock(&event_loop_lock_); + UNLOCK("(serial)", "eventLoop", event_loop_lock_); return NULL; } @@ -1077,26 +1098,42 @@ SerialCommunicationManager::~SerialCommunicationManager() { } -int SerialCommunicationManagerImp::startRegularCallback(int seconds, function callback, string name) +int SerialCommunicationManagerImp::startRegularCallback(string name, int seconds, function callback) { Timer t = { (int)timers_.size(), seconds, time(NULL), callback, name }; + LOCK("(serial)", "startRegularCallback", timers_lock_); timers_.push_back(t); + UNLOCK("(serial)", "startRegularCallback", timers_lock_); debug("(serial) registered regular callback %d %s every %d seconds\n", t.id, name.c_str(), seconds); return t.id; } void SerialCommunicationManagerImp::stopRegularCallback(int id) { + debug("(serial) stopping regular callback %d\n", id); + LOCK("(serial)", "stopRegularCallback", timers_lock_); for (auto i = timers_.begin(); i != timers_.end(); ++i) { if ((*i).id == id) { timers_.erase(i); - return; + break; } } + UNLOCK("(serial)", "startRegularCallback", timers_lock_); } + +SerialDevice *SerialCommunicationManagerImp::lookup(string device) +{ + for (auto sd : devices_) + { + if (sd->device() == device) return sd; + } + return NULL; +} + + #if defined(__APPLE__) vector SerialCommunicationManagerImp::listSerialDevices() { @@ -1201,7 +1238,11 @@ vector SerialCommunicationManagerImp::listSerialDevices() { string name = entries[i]->d_name; - if (name == ".." || name == ".") continue; + if (name == ".." || name == ".") + { + free(entries[i]); + continue; + } string tty = sysdir+name; check_if_serial(tty, &found_serials, &found_8250s); diff --git a/src/serial.h b/src/serial.h index fd4e8d5..dad9636 100644 --- a/src/serial.h +++ b/src/serial.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ struct SerialCommunicationManager; */ struct SerialDevice { + // If fail_if_not_ok then forcefully exit the program if cannot be opened. virtual AccessCheck open(bool fail_if_not_ok) = 0; virtual void close() = 0; // Send will return true only if sending on a tty. @@ -46,6 +47,9 @@ struct SerialDevice virtual bool working() = 0; // Used when connecting stdin to a tty driver for testing. virtual bool readonly() = 0; + // Mark this device so that it is ignored by the select/callback event loop. + virtual void doNotUseCallbacks() = 0; + virtual bool skippingCallbacks() = 0; // Return underlying device as string. virtual std::string device() = 0; @@ -70,7 +74,10 @@ struct SerialCommunicationManager // A serial device simulator used for internal testing. virtual unique_ptr createSerialDeviceSimulator() = 0; + // Invoke cb callback when data arrives on the serial device. virtual void listenTo(SerialDevice *sd, function cb) = 0; + // Invoke cb callback when the serial device has disappeared! + virtual void onDisappear(SerialDevice *sd, function cb) = 0; virtual void stop() = 0; virtual void startEventLoop() = 0; virtual void waitForStop() = 0; @@ -78,7 +85,7 @@ struct SerialCommunicationManager virtual void setReopenAfter(int seconds) = 0; // Register a new timer that regularly, every seconds, invokes the callback. // Returns an id for the timer. - virtual int startRegularCallback(int seconds, function callback, std::string name) = 0; + virtual int startRegularCallback(std::string name, int seconds, function callback) = 0; virtual void stopRegularCallback(int id) = 0; virtual void resetInitiated() = 0; @@ -86,6 +93,8 @@ struct SerialCommunicationManager // List all real serial devices. virtual std::vector listSerialDevices() = 0; + // Return a serial device for the given device, if it exists! Otherwise NULL. + virtual SerialDevice *lookup(std::string device) = 0; virtual ~SerialCommunicationManager(); }; diff --git a/src/testinternals.cc b/src/testinternals.cc index 3ac5574..8ca01d0 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -35,6 +35,7 @@ int test_linkmodes(); void test_ids(); void test_kdf(); void test_periods(); +void test_devices(); int main(int argc, char **argv) { @@ -50,6 +51,7 @@ int main(int argc, char **argv) test_ids(); test_kdf(); test_periods(); + test_devices(); return 0; } @@ -491,3 +493,28 @@ void test_periods() testp(t, "thu(00-00)", false); testp(t, "thu(01-01)", true); } + +void testd(string arg, string xf, string xs, string xl, bool xok) +{ + Device d; + bool ok = isPossibleDevice(arg, &d); + if (ok != xok) + { + printf("ERROR in device parsing \"%s\"\n", arg.c_str()); + return; + } + if (ok == false) return; + if (xf != d.file || + xs != d.suffix || + xl != d.linkmodes) + { + printf("ERROR in device parsing parts \"%s\"\n", arg.c_str()); + } +} + +void test_devices() +{ + testd("auto", "auto", "", "", true); + testd("/dev/ttyUSB0:9600", "/dev/ttyUSB0", "9600", "", true); + testd("auto:gurka", "", "", "", false); +} diff --git a/src/timings.h b/src/timings.h index 306f31b..86a4f51 100644 --- a/src/timings.h +++ b/src/timings.h @@ -18,10 +18,8 @@ #ifndef TIMINGS_H #define TIMINGS_H -// Default select timeout, every 10 seconds if no other timer/timeout cuts it short. -#define SELECT_TIMEOUT 10 -// When running internal tests on timeouts use 5 seconds instead. -#define SELECT_TIMEOUT_INTERNAL_TESTING 5 +// Default select timeout one second. +#define SELECT_TIMEOUT 1 // Default checkStatus callback frequency every 60 seconds, when an alarmtimeout has been set. #define CHECKSTATUS_TIMER 60 diff --git a/src/util.cc b/src/util.cc index 44a24ac..0f00b67 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1055,6 +1055,13 @@ string strdatetime(struct tm *datetime) return string(buf); } +string strdatetimesec(struct tm *datetime) +{ + char buf[256]; + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", datetime); + return string(buf); +} + AccessCheck checkIfExistsAndSameGroup(string device) { struct stat sb; diff --git a/src/util.h b/src/util.h index b8e37f9..843a9a6 100644 --- a/src/util.h +++ b/src/util.h @@ -49,8 +49,11 @@ std::string safeString(std::vector &target); void strprintf(std::string &s, const char* fmt, ...); // Return for example: 2010-03-21 std::string strdate(struct tm *date); -// Return for example: 2010-03-21 15:22:03 +// Return for example: 2010-03-21 15:22 std::string strdatetime(struct tm *date); +// Return for example: 2010-03-21 15:22:03 +std::string strdatetimesec(struct tm *date); + void xorit(uchar *srca, uchar *srcb, uchar *dest, int len); void shiftLeft(uchar *srca, uchar *srcb, int len); @@ -163,4 +166,7 @@ bool startsWith(std::string s, std::vector &data); // Sum the memory used by the heap and stack. size_t memoryUsage(); +#define LOCK(module,func,x) { trace("TRACE " module " " func " locking " #x "\n"); pthread_mutex_lock(&x); trace(module " " func " locked " #x "\n"); } +#define UNLOCK(module,func,x) { trace("TRACE " module " " func " unlocking " #x "\n"); pthread_mutex_unlock(&x); trace(module " " func " unlocked " #x "\n"); } + #endif diff --git a/src/wmbus.cc b/src/wmbus.cc index ff7b6cd..44773ad 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -512,6 +512,9 @@ Detected detectAuto(string devicefile, { assert(devicefile == "auto"); + Detected detected; + detected.device = { devicefile, suffix }; + if (suffix != "") { error("You cannot have a suffix appended to auto.\n"); @@ -520,68 +523,71 @@ Detected detectAuto(string devicefile, AccessCheck ac; ac = findAndDetect(handler, &devicefile, - [](string d, SerialCommunicationManager* m){ return detectIM871A(d, m);}, + [&](string d, SerialCommunicationManager* m){ return detectIM871A(d, &detected, m);}, "im871a", "/dev/im871a"); if (ac == AccessCheck::AccessOK) { - return { DEVICE_IM871A, devicefile, 0, false }; + return detected; } CHECK_SAME_GROUP ac = findAndDetect(handler, &devicefile, - [](string d, SerialCommunicationManager* m){ return detectAMB8465(d, m);}, + [&](string d, SerialCommunicationManager* m){ return detectAMB8465(d, &detected, m);}, "amb8465", "/dev/amb8465"); if (ac == AccessCheck::AccessOK) { - return { DEVICE_AMB8465, devicefile, false }; + return detected; } CHECK_SAME_GROUP ac = findAndDetect(handler, &devicefile, - [](string d, SerialCommunicationManager* m){ return detectRawTTY(d, 38400, m);}, + [&](string d, SerialCommunicationManager* m){ return detectRawTTY(d, 38400, &detected, m);}, "rfmrx2", "/dev/rfmrx2"); if (ac == AccessCheck::AccessOK) { - return { DEVICE_RFMRX2, devicefile, false }; + return detected; } CHECK_SAME_GROUP ac = findAndDetect(handler, &devicefile, - [](string d, SerialCommunicationManager* m){ return detectCUL(d, m);}, + [&](string d, SerialCommunicationManager* m){ return detectCUL(d, &detected, m);}, "cul", "/dev/ttyUSB0"); if (ac == AccessCheck::AccessOK) { - return { DEVICE_CUL, "/dev/ttyUSB0" }; + return detected; } CHECK_SAME_GROUP ac = findAndDetect(handler, &devicefile, - [](string d, SerialCommunicationManager* m){ return detectRTLSDR(d, m);}, + [&](string d, SerialCommunicationManager* m){ return detectRTLSDR(d, &detected, m);}, "rtlsdr", "/dev/rtlsdr"); if (ac == AccessCheck::AccessOK) { - return { DEVICE_RTLWMBUS, "rtlwmbus" }; + return detected; } CHECK_SAME_GROUP // We could not auto-detect any device. - return { DEVICE_UNKNOWN, "", false }; + return { { devicefile, "", ""}, DEVICE_UNKNOWN, 0, false }; } -Detected detectImstAmberCul(string devicefile, +Detected detectImstAmberCul(string file, string suffix, SerialCommunicationManager *handler) { + Detected detected {}; + detected.device = { file, suffix }; + // If im87a is tested first, a delay of 1s must be inserted // before amb8465 is tested, lest it will not respond properly. // It really should not matter, but perhaps is the uart of the amber @@ -591,35 +597,37 @@ Detected detectImstAmberCul(string devicefile, // Talk amb8465 with it... // assumes this device is configured for 9600 bps, which seems to be the default. - if (detectAMB8465(devicefile, handler) == AccessCheck::AccessOK) + if (detectAMB8465(file, &detected, handler) == AccessCheck::AccessOK) { - return { DEVICE_AMB8465, devicefile, false }; + return detected; } // Talk im871a with it... // assumes this device is configured for 57600 bps, which seems to be the default. - if (detectIM871A(devicefile, handler) == AccessCheck::AccessOK) + if (detectIM871A(file, &detected, handler) == AccessCheck::AccessOK) { - return { DEVICE_IM871A, devicefile, false }; + return detected; } // Talk CUL with it... // assumes this device is configured for 38400 bps, which seems to be the default. - if (detectCUL(devicefile, handler) == AccessCheck::AccessOK) + if (detectCUL(file, &detected, handler) == AccessCheck::AccessOK) { - return { DEVICE_CUL, devicefile, false }; + return detected; } // We could not auto-detect either. - return { DEVICE_UNKNOWN, "", false }; + return { { file, suffix }, DEVICE_UNKNOWN, 0, false }; } /** The devicefile can be: - auto (to autodetect the device) - /dev/ttyUSB0 (to use this character device) + auto (to autodetect the devices) + + /dev/ttyUSB0 (to use this serial device, probe for the exact device.) + /dev/ttyUSB0:9600 (listen to this serial device set to this baudrate N81, no probing.) /home/me/simulation.txt or /home/me/simulation_foo.txt (to use the wmbusmeters telegram=|....|+32 format) - /home/me/telegram.raw (to read bytes from this file) - stdin (to read bytes from stdin) + /home/me/telegram.raw (to read raw binary wmbus bytes from this file) + stdin (to read raw binary wmbus bytes from stdin) If a suffix the suffix can be: im871a @@ -632,69 +640,69 @@ Detected detectImstAmberCul(string devicefile, simulation: assume the devicefile produces telegram=|....|+xx lines. This can also pace the simulated telegrams in time. a baud rate like 38400: assume the devicefile is a raw tty character device. */ -Detected detectWMBusDeviceSetting(string devicefile, +Detected detectWMBusDeviceSetting(string file, string suffix, SerialCommunicationManager *handler) { - debug("(detect) \"%s\" \"%s\"\n", devicefile.c_str(), suffix.c_str()); + debug("(detect) \"%s\" \"%s\"\n", file.c_str(), suffix.c_str()); // Look for /dev/im871a /dev/amb8465 /dev/rfmrx2 /dev/rtlsdr - if (devicefile == "auto") + if (file == "auto") { debug("(detect) driver: auto\n"); - return detectAuto(devicefile, suffix, handler); + return detectAuto(file, suffix, handler); } // If the devicefile is rtlwmbus then the suffix can be a frequency // or the actual command line to use. // E.g. rtlwmbus rtlwmbux:868.95M rtlwmbus:rtl_sdr | rtl_wmbus - if (devicefile == "rtlwmbus") + if (file == "rtlwmbus") { debug("(detect) driver: rtlwmbus\n"); - return { DEVICE_RTLWMBUS, "", false }; + return { { file, suffix }, DEVICE_RTLWMBUS, 0, false }; } - if (devicefile == "rtl433") + if (file == "rtl433") { debug("(detect) driver: rtl433\n"); - return { DEVICE_RTL433, "", false }; + return { { file, suffix}, DEVICE_RTL433, 0, false }; } // Is it a file named simulation_xxx.txt ? - if (checkIfSimulationFile(devicefile.c_str())) + if (checkIfSimulationFile(file.c_str())) { debug("(detect) driver: simulation file\n"); - return { DEVICE_SIMULATOR, devicefile, false }; + return { { file, suffix }, DEVICE_SIMULATOR, 0, false }; } - bool is_tty = checkCharacterDeviceExists(devicefile.c_str(), false); - bool is_stdin = devicefile == "stdin"; - bool is_file = checkFileExists(devicefile.c_str()); + bool is_tty = checkCharacterDeviceExists(file.c_str(), false); + bool is_stdin = file == "stdin"; + bool is_file = checkFileExists(file.c_str()); debug("(detect) is_tty=%d is_stdin=%d is_file=%d\n", is_tty, is_stdin, is_file); if (!is_tty && !is_stdin && !is_file) { - debug("(detect) not a valid device file %s\n", devicefile.c_str()); + debug("(detect) not a valid device file %s\n", file.c_str()); // Oups, not a valid devicefile. - return { DEVICE_UNKNOWN, "", false }; + return { { file, suffix }, DEVICE_UNKNOWN, 0, false }; } bool override_tty = !is_tty; - if (suffix == "amb8465") return { DEVICE_AMB8465, devicefile, 0, override_tty }; - if (suffix == "im871a") return { DEVICE_IM871A, devicefile, 0, override_tty }; - if (suffix == "rfmrx2") return { DEVICE_RFMRX2, devicefile, 0, override_tty }; - if (suffix == "rtlwmbus") return { DEVICE_RTLWMBUS, devicefile, 0, override_tty }; - if (suffix == "rtl433") return { DEVICE_RTL433, devicefile, 0, override_tty }; - if (suffix == "cul") return { DEVICE_CUL, devicefile, 0, override_tty }; - if (suffix == "d1tc") return { DEVICE_D1TC, devicefile, 0, override_tty }; - if (suffix == "wmb13u") return { DEVICE_WMB13U, devicefile, 0, override_tty }; - if (suffix == "simulation") return { DEVICE_SIMULATOR, devicefile, 0, override_tty }; + if (suffix == "amb8465") return { { file, suffix }, DEVICE_AMB8465, 0, override_tty }; + if (suffix == "im871a") return { { file, suffix }, DEVICE_IM871A, 0, override_tty }; + if (suffix == "rfmrx2") return { { file, suffix }, DEVICE_RFMRX2, 0, override_tty }; + if (suffix == "rtlwmbus") return { { file, suffix }, DEVICE_RTLWMBUS, 0, override_tty }; + if (suffix == "rtl433") return { { file, suffix}, DEVICE_RTL433, 0, override_tty }; + if (suffix == "cul") return { { file, suffix}, DEVICE_CUL, 0, override_tty }; + if (suffix == "d1tc") return { { file, suffix}, DEVICE_D1TC, 0, override_tty }; + if (suffix == "wmb13u") return { { file, suffix}, DEVICE_WMB13U, 0, override_tty }; + if (suffix == "simulation") return { { file, suffix}, DEVICE_SIMULATOR, 0, override_tty }; // If the suffix is a number, then assume that it is a baud rate. - if (isNumber(suffix)) return { DEVICE_RAWTTY, devicefile, atoi(suffix.c_str()), override_tty }; + if (isNumber(suffix)) return { { file, suffix} , DEVICE_RAWTTY, atoi(suffix.c_str()), override_tty }; // If the suffix is empty and its not a tty, then read raw telegrams from stdin or the file. - if (suffix == "" && !is_tty) return { DEVICE_RAWTTY, devicefile, 0, true }; + if (suffix == "" && !is_tty) return { { file, suffix}, DEVICE_RAWTTY, 0, true }; if (suffix != "") { @@ -704,7 +712,7 @@ Detected detectWMBusDeviceSetting(string devicefile, // Ok, we are left with a single /dev/ttyUSB0 lets talk to it // to figure out what is connected to it. We currently only // know how to detect Imst, Amber or CUL dongles. - return detectImstAmberCul(devicefile, suffix, handler); + return detectImstAmberCul(file, suffix, handler); } /* @@ -3319,10 +3327,16 @@ bool Telegram::findFormatBytesFromKnownMeterSignatures(vector *format_byt return ok; } +WMBusCommonImplementation::~WMBusCommonImplementation() +{ + info("(wmbus) deleted %s\n", toString(type())); +} + WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t, SerialCommunicationManager *manager, unique_ptr serial) : manager_(manager), + is_working_(true), type_(t), serial_(std::move(serial)) { @@ -3331,7 +3345,7 @@ WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t, // Invoke the check status once per minute. Unless internal testing, then it is every 2 seconds. int default_timer = isInternalTestingEnabled() ? CHECKSTATUS_TIMER_INTERNAL_TESTING : CHECKSTATUS_TIMER; - manager_->startRegularCallback(default_timer, call(this,checkStatus), toString(t)); + manager_->startRegularCallback(toString(t), default_timer, call(this,checkStatus)); } WMBusDeviceType WMBusCommonImplementation::type() @@ -3437,6 +3451,20 @@ bool WMBusCommonImplementation::reset() return true; } +void WMBusCommonImplementation::disconnectedFromDevice() +{ + if (is_working_) + { + info("(wmbus) lost %s closing %s\n", device().c_str(), toString(type())); + is_working_ = false; + } +} + +bool WMBusCommonImplementation::isWorking() +{ + return is_working_; +} + void WMBusCommonImplementation::checkStatus() { if (protocol_error_count_ >= 20) @@ -3673,6 +3701,7 @@ AccessCheck findAndDetect(SerialCommunicationManager *manager, AccessCheck rc = check(dev, manager); if (rc == AccessCheck::AccessOK) return AccessCheck::AccessOK; } + if (ac == AccessCheck::NotSameGroup) { // Device exists, but you do not belong to its group! @@ -3682,27 +3711,6 @@ AccessCheck findAndDetect(SerialCommunicationManager *manager, return AccessCheck::NotSameGroup; } - for (int n=0; n < 9; ++n) - { - dev = device_root+"_"+to_string(n); - debug("(%s) exists? %s\n", dongle_name.c_str(), dev.c_str()); - AccessCheck ac = checkIfExistsAndSameGroup(dev); - *out_device = dev; - if (ac == AccessCheck::AccessOK) - { - debug("(%s) checking %s\n", dongle_name.c_str(), dev.c_str()); - AccessCheck rc = check(dev, manager); - if (rc == AccessCheck::AccessOK) return AccessCheck::AccessOK; - // If we get here, the device /dev/im871a_0 could be locked - // try /dev/im871a_1 etc... - } - if (ac == AccessCheck::NotSameGroup) - { - // Device exists, but you do not belong to its group! - return AccessCheck::NotSameGroup; - } - } - *out_device = ""; // No device found! return AccessCheck::NotThere; @@ -3935,3 +3943,33 @@ LIST_OF_MBUS_DEVICES } return "?"; } + +bool isPossibleDevice(string arg, Device *device) +{ + size_t colon = arg.find(":"); + + if (colon == string::npos) + { + + device->file = arg; + device->suffix = ""; + device->linkmodes = ""; + return true; + } + + device->file = arg.substr(0, colon); + string rest = arg.substr(colon+1); + + colon = rest.find(":"); + if (colon == string::npos) + { + device->suffix = rest; + device->linkmodes = ""; + return true; + } + + device->suffix = rest.substr(0, colon); + device->linkmodes = rest.substr(colon+1); + + return true; +} diff --git a/src/wmbus.h b/src/wmbus.h index f647e0f..e5400b8 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -30,6 +30,56 @@ bool trimCRCsFrameFormatA(std::vector &payload); bool trimCRCsFrameFormatB(std::vector &payload); +struct Device +{ + // A typical device is: + // /dev/ttyUSB0:im871a:c1,t1 + // /dev/ttyUSB1:amb8465 + // /rtlwmbus::any + std::string file; // /dev/ttyUSB0, simulation_meter.txt, stdin file.raw + std::string suffix; // rtlwmbus im871a amb8465 38400 rtl433 + std::string linkmodes; // c1,t1,s1 +}; + +#define LIST_OF_MBUS_DEVICES \ + X(DEVICE_UNKNOWN) \ + X(DEVICE_CUL)\ + X(DEVICE_D1TC)\ + X(DEVICE_IM871A)\ + X(DEVICE_AMB8465)\ + X(DEVICE_RFMRX2)\ + X(DEVICE_SIMULATOR)\ + X(DEVICE_RTLWMBUS)\ + X(DEVICE_RTL433)\ + X(DEVICE_RAWTTY)\ + X(DEVICE_WMB13U) + +enum WMBusDeviceType { +#define X(name) name, +LIST_OF_MBUS_DEVICES +#undef X +}; + +const char *toString(WMBusDeviceType t); + +struct Detected +{ + Device device; // Device information. + WMBusDeviceType type; // IM871A, AMB8465 etc. + int baudrate; // Baudrate to tty. + // If the override_tty is true, then do not allow the wmbus driver to open the device->file as a tty, + // instead open the device->file as a file instead . This is to allows feeding the wmbus drivers + // using stdin or a file. This is primarily used for internal testing. + bool override_tty; + + void set(WMBusDeviceType t, int br, bool ot) + { + type = t; + baudrate = br; + override_tty = ot; + } +}; + #define LIST_OF_LINK_MODES \ X(Any,any,--anylinkmode,0xffff) \ X(C1,c1,--c1,0x1) \ @@ -390,27 +440,6 @@ private: struct Meter; -#define LIST_OF_MBUS_DEVICES \ - X(DEVICE_UNKNOWN) \ - X(DEVICE_CUL)\ - X(DEVICE_D1TC)\ - X(DEVICE_IM871A)\ - X(DEVICE_AMB8465)\ - X(DEVICE_RFMRX2)\ - X(DEVICE_SIMULATOR)\ - X(DEVICE_RTLWMBUS)\ - X(DEVICE_RTL433)\ - X(DEVICE_RAWTTY)\ - X(DEVICE_WMB13U) - -enum WMBusDeviceType { -#define X(name) name, -LIST_OF_MBUS_DEVICES -#undef X -}; - -const char *toString(WMBusDeviceType t); - struct WMBus { virtual WMBusDeviceType type() = 0; @@ -426,7 +455,9 @@ struct WMBus virtual void onTelegram(function)> cb) = 0; virtual SerialDevice *serial() = 0; virtual void simulate() = 0; - // This will check if the wmbus devices needs reset. + // Return true if underlying device is ok and device in general seems to be working. + virtual bool isWorking() = 0; + // This will check if the wmbus devices needs a reset and then immediately perform the reset. virtual void checkStatus() = 0; // Close any underlying ttys or software and restart/reinitialize. // Return true if ok. @@ -438,21 +469,12 @@ struct WMBus virtual ~WMBus() = 0; }; - -struct Detected -{ - WMBusDeviceType type; // IM871A, AMB8465 etc - string devicefile; // /dev/ttyUSB0 /dev/ttyACM0 stdin simulation_abc.txt telegrams.raw - int baudrate; // If the suffix is a number, store the number here. - // If the override_tty is true, then do not allow the wmbus driver to open the tty, - // instead open the devicefile first. This is to allow feeding the wmbus drivers using stdin - // or a file or for internal testing. - bool override_tty; -}; - Detected detectWMBusDeviceSetting(string devicefile, string suffix, SerialCommunicationManager *manager); + +bool isPossibleDevice(string arg, Device *device); + unique_ptr openIM871A(string device, SerialCommunicationManager *manager, unique_ptr serial_override); unique_ptr openAMB8465(string device, SerialCommunicationManager *manager, @@ -518,15 +540,19 @@ FrameStatus checkWMBusFrame(vector &data, int *payload_len_out, int *payload_offset); -AccessCheck detectIM871A(string device, SerialCommunicationManager *handler); -AccessCheck detectAMB8465(string device, SerialCommunicationManager *handler); -AccessCheck detectRawTTY(string device, int baud, SerialCommunicationManager *handler); -AccessCheck detectRTLSDR(string device, SerialCommunicationManager *handler); -AccessCheck detectCUL(string device, SerialCommunicationManager *handler); -AccessCheck detectWMB13U(string device, SerialCommunicationManager *handler); +AccessCheck detectIM871A(string file, Detected *detected, SerialCommunicationManager *handler); +AccessCheck detectAMB8465(string file, Detected *detected, SerialCommunicationManager *handler); +AccessCheck detectRawTTY(string file, int baud, Detected *detected, SerialCommunicationManager *handler); +AccessCheck detectRTLSDR(string file, Detected *detected, SerialCommunicationManager *handler); +AccessCheck detectCUL(string file, Detected *detected, SerialCommunicationManager *handler); +AccessCheck detectWMB13U(string file, Detected *detected, SerialCommunicationManager *handler); // Try to factory reset an AMB8465 by trying all possible serial speeds and // restore to factory settings. AccessCheck factoryResetAMB8465(string device, SerialCommunicationManager *handler, int *was_baud); +Detected detectImstAmberCul(string file, + string suffix, + SerialCommunicationManager *handler); + #endif diff --git a/src/wmbus_amb8465.cc b/src/wmbus_amb8465.cc index 0a77d68..d659d59 100644 --- a/src/wmbus_amb8465.cc +++ b/src/wmbus_amb8465.cc @@ -109,6 +109,7 @@ WMBusAmber::WMBusAmber(unique_ptr serial, SerialCommunicationManag { sem_init(&command_wait_, 0, 0); manager_->listenTo(this->serial(),call(this,processSerialData)); + manager_->onDisappear(this->serial(),call(this,disconnectedFromDevice)); rssi_expected_ = true; reset(); } @@ -131,9 +132,9 @@ bool WMBusAmber::ping() { if (serial()->readonly()) return true; // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(amb8465)", "ping", command_lock_); // Ping it... - pthread_mutex_unlock(&command_lock_); + UNLOCK("(amb8465)", "ping", command_lock_); return true; } @@ -142,7 +143,7 @@ uint32_t WMBusAmber::getDeviceId() { if (serial()->readonly()) { return 0; } // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(amb8465)", "getDeviceId", command_lock_); vector msg(4); msg[0] = AMBER_SERIAL_SOF; @@ -172,7 +173,7 @@ uint32_t WMBusAmber::getDeviceId() } } - pthread_mutex_unlock(&command_lock_); + UNLOCK("(amb8465)", "getDeviceId", command_lock_); return id; } @@ -190,7 +191,7 @@ void WMBusAmber::getConfiguration() { if (serial()->readonly()) { return; } // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(amb8465)", "getConfiguration", command_lock_); vector msg(6); msg[0] = AMBER_SERIAL_SOF; @@ -207,7 +208,7 @@ void WMBusAmber::getConfiguration() if (!sent) { - pthread_mutex_unlock(&command_lock_); + UNLOCK("(amb8465)", "getConfiguration", command_lock_); return; } @@ -233,7 +234,7 @@ void WMBusAmber::getConfiguration() verbose("(amb8465) config: mode Preselect %02x\n", received_payload_[70+2]); } - pthread_mutex_unlock(&command_lock_); + UNLOCK("(amb8465)", "getConfiguration", command_lock_); } void WMBusAmber::deviceSetLinkModes(LinkModeSet lms) @@ -246,7 +247,7 @@ void WMBusAmber::deviceSetLinkModes(LinkModeSet lms) error("(amb8465) setting link mode(s) %s is not supported for amb8465\n", modes.c_str()); } - pthread_mutex_lock(&command_lock_); + LOCK("(amb8465)", "deviceSetLinkModes", command_lock_); vector msg(8); msg[0] = AMBER_SERIAL_SOF; @@ -281,7 +282,7 @@ void WMBusAmber::deviceSetLinkModes(LinkModeSet lms) if (sent) waitForResponse(); link_modes_ = lms; - pthread_mutex_unlock(&command_lock_); + UNLOCK("(amb8465)", "deviceSetLinkModes", command_lock_); } void WMBusAmber::waitForResponse() @@ -518,10 +519,11 @@ void WMBusAmber::handleMessage(int msgid, vector &frame) } } -AccessCheck detectAMB8465(string device, SerialCommunicationManager *manager) +AccessCheck detectAMB8465(string device, Detected *detected, SerialCommunicationManager *manager) { // Talk to the device and expect a very specific answer. auto serial = manager->createSerialDeviceTTY(device.c_str(), 9600); + serial->doNotUseCallbacks(); AccessCheck rc = serial->open(false); if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere; @@ -565,6 +567,9 @@ AccessCheck detectAMB8465(string device, SerialCommunicationManager *manager) data[7] != xorChecksum(data, 7)) { return AccessCheck::NotThere; } + + detected->set(WMBusDeviceType::DEVICE_AMB8465, 9600, false); + return AccessCheck::AccessOK; } diff --git a/src/wmbus_cul.cc b/src/wmbus_cul.cc index acb5c18..e68742a 100644 --- a/src/wmbus_cul.cc +++ b/src/wmbus_cul.cc @@ -100,6 +100,7 @@ WMBusCUL::WMBusCUL(unique_ptr serial, SerialCommunicationManager * { sem_init(&command_wait_, 0, 0); manager_->listenTo(this->serial(),call(this,processSerialData)); + manager_->onDisappear(this->serial(),call(this,disconnectedFromDevice)); reset(); } @@ -355,10 +356,11 @@ FrameStatus WMBusCUL::checkCULFrame(vector &data, } } -AccessCheck detectCUL(string device, SerialCommunicationManager *manager) +AccessCheck detectCUL(string device, Detected *detected, SerialCommunicationManager *manager) { // Talk to the device and expect a very specific answer. auto serial = manager->createSerialDeviceTTY(device.c_str(), 38400); + serial->doNotUseCallbacks(); AccessCheck rc = serial->open(false); if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere; @@ -399,5 +401,8 @@ AccessCheck detectCUL(string device, SerialCommunicationManager *manager) // TODO: check version string somehow serial->close(); + + detected->set(WMBusDeviceType::DEVICE_CUL, 38400, false); + return AccessCheck::AccessOK; } diff --git a/src/wmbus_im871a.cc b/src/wmbus_im871a.cc index bf8e3d2..f465894 100644 --- a/src/wmbus_im871a.cc +++ b/src/wmbus_im871a.cc @@ -76,7 +76,7 @@ private: static FrameStatus checkIM871AFrame(vector &data, size_t *frame_length, int *endpoint_out, int *msgid_out, int *payload_len_out, int *payload_offset); - friend AccessCheck detectIM871A(string device, SerialCommunicationManager *manager); + friend AccessCheck detectIM871A(string device, Detected *detected, SerialCommunicationManager *manager); void handleDevMgmt(int msgid, vector &payload); void handleRadioLink(int msgid, vector &payload); void handleRadioLinkTest(int msgid, vector &payload); @@ -101,6 +101,7 @@ WMBusIM871A::WMBusIM871A(unique_ptr serial, SerialCommunicationMan { sem_init(&command_wait_, 0, 0); manager_->listenTo(this->serial(),call(this,processSerialData)); + manager_->onDisappear(this->serial(),call(this,disconnectedFromDevice)); reset(); } @@ -108,7 +109,7 @@ bool WMBusIM871A::ping() { if (serial()->readonly()) return true; // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(im871)", "ping", command_lock_); vector msg(4); msg[0] = IM871A_SERIAL_SOF; @@ -122,7 +123,7 @@ bool WMBusIM871A::ping() if (sent) waitForResponse(); - pthread_mutex_unlock(&command_lock_); + UNLOCK("(im871)", "ping", command_lock_); return true; } @@ -130,7 +131,7 @@ uint32_t WMBusIM871A::getDeviceId() { if (serial()->readonly()) return 0; // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(im871)", "getDeviceId", command_lock_); vector msg(4); msg[0] = IM871A_SERIAL_SOF; @@ -165,7 +166,7 @@ uint32_t WMBusIM871A::getDeviceId() id = 0; } - pthread_mutex_unlock(&command_lock_); + UNLOCK("(im871)", "getDeviceId", command_lock_); return id; } @@ -173,7 +174,7 @@ LinkModeSet WMBusIM871A::getLinkModes() { if (serial()->readonly()) { return Any_bit; } // Feeding from stdin or file. - pthread_mutex_lock(&command_lock_); + LOCK("(im871)", "getLinkModes", command_lock_); vector msg(4); msg[0] = IM871A_SERIAL_SOF; @@ -188,7 +189,7 @@ LinkModeSet WMBusIM871A::getLinkModes() { // If we are using a serial override that will not respond, // then just return a value. - pthread_mutex_unlock(&command_lock_); + UNLOCK("(im871)", "getLinkModes", command_lock_); // Use the remembered link modes set before. return protectedGetLinkModes(); } @@ -320,7 +321,8 @@ LinkModeSet WMBusIM871A::getLinkModes() } } - pthread_mutex_unlock(&command_lock_); + UNLOCK("(im871)", "getLinkModes", command_lock_); + LinkModeSet lms; lms.addLinkMode(lm); return lms; @@ -344,7 +346,7 @@ void WMBusIM871A::deviceSetLinkModes(LinkModeSet lms) error("(im871a) setting link mode(s) %s is not supported for im871a\n", modes.c_str()); } - pthread_mutex_lock(&command_lock_); + LOCK("(im871)", "deviceSetLinkModes", command_lock_); vector msg(10); msg[0] = IM871A_SERIAL_SOF; @@ -386,14 +388,16 @@ void WMBusIM871A::deviceSetLinkModes(LinkModeSet lms) if (sent) waitForResponse(); - pthread_mutex_unlock(&command_lock_); + UNLOCK("(im871)", "deviceSetLinkModes", command_lock_); } void WMBusIM871A::waitForResponse() { while (manager_->isRunning()) { + trace("(im871) waitForResponse sem_wait command_wait_\n"); int rc = sem_wait(&command_wait_); + trace("(im871) waitForResponse waited command_wait_\n"); if (rc==0) break; if (rc==-1) { if (errno==EINTR) continue; @@ -649,10 +653,11 @@ void WMBusIM871A::handleHWTest(int msgid, vector &payload) } } -AccessCheck detectIM871A(string device, SerialCommunicationManager *manager) +AccessCheck detectIM871A(string file, Detected *detected, SerialCommunicationManager *manager) { // Talk to the device and expect a very specific answer. - auto serial = manager->createSerialDeviceTTY(device.c_str(), 57600); + auto serial = manager->createSerialDeviceTTY(file.c_str(), 57600); + serial->doNotUseCallbacks(); AccessCheck rc = serial->open(false); if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere; @@ -688,5 +693,7 @@ AccessCheck detectIM871A(string device, SerialCommunicationManager *manager) { return AccessCheck::NotThere; } + detected->set(WMBusDeviceType::DEVICE_IM871A, 57600, false); + return AccessCheck::AccessOK; } diff --git a/src/wmbus_rawtty.cc b/src/wmbus_rawtty.cc index 2dc2d2e..c61adb9 100644 --- a/src/wmbus_rawtty.cc +++ b/src/wmbus_rawtty.cc @@ -152,7 +152,7 @@ void WMBusRawTTY::processSerialData() } } -AccessCheck detectRawTTY(string device, int baud, SerialCommunicationManager *manager) +AccessCheck detectRawTTY(string device, int baud, Detected *detected, SerialCommunicationManager *manager) { // Since we do not know how to talk to the other end, it might not // even respond. The only thing we can do is to try to open the serial device. @@ -161,5 +161,8 @@ AccessCheck detectRawTTY(string device, int baud, SerialCommunicationManager *ma if (rc != AccessCheck::AccessOK) return AccessCheck::NotThere; serial->close(); + + detected->set(WMBusDeviceType::DEVICE_RAWTTY, baud, false); + return AccessCheck::AccessOK; } diff --git a/src/wmbus_rtlwmbus.cc b/src/wmbus_rtlwmbus.cc index a089359..1196435 100644 --- a/src/wmbus_rtlwmbus.cc +++ b/src/wmbus_rtlwmbus.cc @@ -288,8 +288,15 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector &data, return FullFrame; } -AccessCheck detectRTLSDR(string device, SerialCommunicationManager *manager) +AccessCheck detectRTLSDR(string device, Detected *detected, SerialCommunicationManager *manager) { // No more advanced test than that the /dev/rtlsdr link exists. - return checkIfExistsAndSameGroup(device); + AccessCheck rc = checkIfExistsAndSameGroup(device); + + if (rc == AccessCheck::AccessOK) + { + detected->set(WMBusDeviceType::DEVICE_RTLWMBUS,0, false); + } + + return rc; } diff --git a/src/wmbus_utils.h b/src/wmbus_utils.h index c427e73..4dd9dc1 100644 --- a/src/wmbus_utils.h +++ b/src/wmbus_utils.h @@ -29,14 +29,17 @@ string frameTypeKamstrupC1(int ft); struct WMBusCommonImplementation : public virtual WMBus { WMBusCommonImplementation(WMBusDeviceType t, SerialCommunicationManager *manager, unique_ptr serial_override); + ~WMBusCommonImplementation(); WMBusDeviceType type(); void setMeters(vector> *meters); void onTelegram(function)> cb); bool handleTelegram(vector frame); void checkStatus(); + bool isWorking(); void setTimeout(int seconds, std::string expected_activity); void setLinkModes(LinkModeSet lms); + void disconnectedFromDevice(); bool reset(); SerialDevice *serial() { if (serial_) return serial_.get(); else return NULL; } string device() { if (serial_) return serial_->device(); else return "?"; } @@ -55,6 +58,7 @@ struct WMBusCommonImplementation : public virtual WMBus private: + bool is_working_ {}; vector)>> telegram_listeners_; vector> *meters_; WMBusDeviceType type_ {}; diff --git a/src/wmbus_wmb13u.cc b/src/wmbus_wmb13u.cc index f6fe66b..0bd6fa8 100644 --- a/src/wmbus_wmb13u.cc +++ b/src/wmbus_wmb13u.cc @@ -203,7 +203,7 @@ void WMBusWMB13U::processSerialData() // Receive and accumulated serial data until a full frame has been received. serial()->receive(&data); // Unlock the serial lock. - pthread_mutex_unlock(&serial_lock_); + UNLOCK("(wmb13u)", "processSerialData", serial_lock_); read_buffer_.insert(read_buffer_.end(), data.begin(), data.end()); @@ -245,7 +245,7 @@ void WMBusWMB13U::processSerialData() bool WMBusWMB13U::enterConfigModee() { - pthread_mutex_lock(&serial_lock_); + LOCK("(wmb13u)", "enterConfigMode", serial_lock_); vector data; @@ -272,7 +272,8 @@ bool WMBusWMB13U::enterConfigModee() return true; fail: - pthread_mutex_unlock(&serial_lock_); + + UNLOCK("(wmb13u)", "enterConfigMode", serial_lock_); return false; } @@ -291,7 +292,7 @@ bool WMBusWMB13U::exitConfigModee() serial()->receive(&data); // Always unlock.... - pthread_mutex_unlock(&serial_lock_); + UNLOCK("(wmb13u)", "exitConfigMode", serial_lock_); if (!startsWith("OK", data)) return false; From 1378d5070020ef78fa3cee192512c84e9bb23548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 30 Aug 2020 21:47:47 +0200 Subject: [PATCH 02/61] Add files. --- src/ui.cc | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui.h | 54 +++++ 2 files changed, 672 insertions(+) create mode 100644 src/ui.cc create mode 100644 src/ui.h diff --git a/src/ui.cc b/src/ui.cc new file mode 100644 index 0000000..4953e25 --- /dev/null +++ b/src/ui.cc @@ -0,0 +1,618 @@ +/* + Copyright (C) 2020 Fredrik Öhrström + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include"ui.h" +#include"util.h" + +using namespace std; + +int screen_width_; +int screen_height_; + +function update_cb_; + +void initUI() +{ + initscr(); + getmaxyx(stdscr, screen_height_, screen_width_); + start_color(); + cbreak(); + curs_set(1); + noecho(); + keypad(stdscr, TRUE); + + init_pair(BG_PAIR, COLOR_WHITE, COLOR_BLUE); + init_pair(WIN_PAIR, COLOR_BLACK, COLOR_WHITE); + init_pair(TITLE_PAIR, COLOR_WHITE, COLOR_CYAN); + init_pair(HILIGHT_PAIR, COLOR_WHITE, COLOR_RED); + wbkgd(stdscr, COLOR_PAIR(BG_PAIR)); +} + +void registerUpdateCB(std::function cb) +{ + update_cb_ = cb; +} + +void printAt(WINDOW *win, int y, int x, const char *str, chtype color) +{ + wattron(win, color); + mvwprintw(win, y, x, "%s", str); + wattroff(win, color); + refresh(); +} + +void printMiddle(WINDOW *win, int y, int width, const char *str, chtype color) +{ + int len = strlen(str); + int wh, ww; + + getyx(win, wh, ww); + ((void)wh); + + int x = (width-len)/2; + wattron(win, color); + mvwprintw(win, y, x, "%s", str); + wattroff(win, color); + refresh(); +} + +int countEntries(const char *entries[]) +{ + int i = 0; + for (; entries[i] != 0; ++i); + return i+1; +} + +int maxWidth(const char *entries[]) +{ + int max = 0; + for (int i=0; entries[i] != 0; ++i) + { + int n = strlen(entries[i]); + if (max < n) max = n; + } + return max; +} + +int maxWidth(vector entries) +{ + int max = 0; + for (string& s : entries) + { + int n = s.length(); + if (max < n) max = n; + } + return max; +} + +int selectFromMenu(const char *title, const char *entries[]) +{ + vector menu; + int n_choices = countEntries(entries); + + for (int i=0; i entries) +{ + int selected = -1; + ITEM **menu_items; + int c; + MENU *menu; + WINDOW *frame_window, *menu_window; + int n_choices, i; + + n_choices = entries.size()+1; + menu_items = (ITEM **)calloc(n_choices, sizeof(ITEM *)); + for(i = 0; i < n_choices-1; ++i) + { + menu_items[i] = new_item(entries[i].c_str(), NULL); + } + menu_items[n_choices-1] = NULL; + + menu = new_menu(menu_items); + int mw = 0; + int mh = 0; + scale_menu(menu, &mh, &mw); + int w = mw+2; + int h = mh+4; + if (w-2 < (int)strlen(title)) + { + w = (int)strlen(title)+2; + } + int x = screen_width_/2-w/2; + int y = screen_height_/2-h/2; + frame_window = newwin(h, w, y, x); + + int mx = (w-mw)/2; + int my = 3; + menu_window = derwin(frame_window, mh, mw, my, mx); + + set_menu_fore(menu, COLOR_PAIR(HILIGHT_PAIR)); + set_menu_back(menu, COLOR_PAIR(WIN_PAIR)); + set_menu_grey(menu, COLOR_PAIR(3)); + + keypad(frame_window, TRUE); + + set_menu_win(menu, frame_window); + set_menu_sub(menu, menu_window); + + set_menu_mark(menu, ">"); + + box(frame_window, 0, 0); + wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); + + printMiddle(frame_window, 1, w, title, COLOR_PAIR(WIN_PAIR)); + mvwaddch(frame_window, 2, 0, ACS_LTEE); + mvwhline(frame_window, 2, 1, ACS_HLINE, 38); + mvwaddch(frame_window, 2, w-1, ACS_RTEE); + refresh(); + + post_menu(menu); + wrefresh(frame_window); + + update_cb_(); + + wtimeout(frame_window, 1000); + + bool running = true; + do + { + c = wgetch(frame_window); + ITEM *cur = current_item(menu); + selected = item_index(cur); + switch(c) + { + case ERR: + update_cb_(); + redrawwin(frame_window); + break; + case KEY_DOWN: + if (selected < n_choices-2) + { + menu_driver(menu, REQ_DOWN_ITEM); + } + else + { + menu_driver(menu, REQ_FIRST_ITEM); + } + break; + case KEY_UP: + if (selected > 0) + { + menu_driver(menu, REQ_UP_ITEM); + } + else + { + menu_driver(menu, REQ_LAST_ITEM); + } + break; + case '\n': + running = false; + break; + } + wrefresh(frame_window); + } while (running); + + unpost_menu(menu); + free_menu(menu); + delwin(frame_window); + erase(); + refresh(); + for(i = 0; i < n_choices; ++i) + { + free_item(menu_items[i]); + } + + return selected; +} + +void displayInformationAndWait(string title, vector entries, int px, int py) +{ + WINDOW *frame_window; + + update_cb_(); + + int mw = maxWidth(entries)+1; + int mh = entries.size(); + int w = mw+2; + int h = mh+4; + if (w-2 < (int)title.length()) + { + w = (int)title.length()+2; + } + int x = screen_width_/2-w/2; + int y = screen_height_/2-h/2; + if (px != -1) + { + x = px; + } + if (py != -1) + { + y = py; + } + frame_window = newwin(h, w, y, x); + + keypad(frame_window, TRUE); + + box(frame_window, 0, 0); + wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); + + printMiddle(frame_window, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); + mvwaddch(frame_window, 2, 0, ACS_LTEE); + mvwhline(frame_window, 2, 1, ACS_HLINE, 38); + mvwaddch(frame_window, 2, w-1, ACS_RTEE); + //refresh(); + + int r = 3; + for (string e : entries) + { + printAt(frame_window, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); + r++; + } + wrefresh(frame_window); + wtimeout(frame_window, 1000); + + bool running = true; + do + { + int c = wgetch(frame_window); + switch(c) + { + case ERR: + update_cb_(); + redrawwin(frame_window); + break; + case 27: + case '\n': + running = false; + break; + } + wrefresh(frame_window); + } while (running); + + delwin(frame_window); + erase(); + refresh(); +} + +void displayInformationNoWait(WINDOW **winp, string title, vector entries, int px, int py) +{ + WINDOW *win = *winp; + + if (win != NULL) + { + delwin(win); + *winp = NULL; + } + int mw = maxWidth(entries)+1; + int mh = entries.size(); + int w = mw+2; + int h = mh+4; + if (w-2 < (int)title.length()) + { + w = (int)title.length()+2; + } + int x = screen_width_/2-w/2; + int y = screen_height_/2-h/2; + if (px != -1) + { + x = px; + } + if (py != -1) + { + y = py; + } + win = newwin(h, w, y, x); + *winp = win; + + box(win, 0, 0); + wbkgd(win, COLOR_PAIR(WIN_PAIR)); + + printMiddle(win, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); + mvwaddch(win, 2, 0, ACS_LTEE); + mvwhline(win, 2, 1, ACS_HLINE, 38); + mvwaddch(win, 2, w-1, ACS_RTEE); +// refresh(); + + int r = 3; + for (string e : entries) + { + printAt(win, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); + r++; + } + wrefresh(win); +} + +void displayStatusLineNoWait(WINDOW **winp, vector entries, int px, int py) +{ + WINDOW *win = *winp; + + if (win != NULL) + { + delwin(win); + *winp = NULL; + } + int w = screen_width_; + int h = 1; + int x = 0; + int y = 0; + if (px != -1) + { + x = px; + } + if (py != -1) + { + y = py; + } + win = newwin(h, w, y, x); + *winp = win; + + wbkgd(win, COLOR_PAIR(WIN_PAIR)); + + int sum = 0; + for (string & e : entries) sum += e.length(); + int num = entries.size()-1; + if (num == 0) num = 1; + int spacing = (w-sum) / num; + +// printMiddle(win, 0, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); + + int xx = 0; + for (string & e : entries) + { + printAt(win, 0, xx, e.c_str(), COLOR_PAIR(WIN_PAIR)); + xx += e.length()+spacing; + } + + wrefresh(win); +} + +string displayInformationAndInput(string title, vector entries, int px, int py) +{ + WINDOW *frame_window; + + int mw = maxWidth(entries)+1; + int mh = entries.size(); + int w = mw+2; + int h = mh+4; + if (w-2 < (int)title.length()) + { + w = (int)title.length()+2; + } + int x = screen_width_/2-w/2; + int y = screen_height_/2-h/2; + if (px != -1) + { + x = px; + } + if (py != -1) + { + y = py; + } + frame_window = newwin(h, w, y, x); + + keypad(frame_window, TRUE); + + box(frame_window, 0, 0); + wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); + + printMiddle(frame_window, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); + mvwaddch(frame_window, 2, 0, ACS_LTEE); + mvwhline(frame_window, 2, 1, ACS_HLINE, 38); + mvwaddch(frame_window, 2, w-1, ACS_RTEE); + //refresh(); + + int r = 3; + for (string e : entries) + { + printAt(frame_window, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); + r++; + } + wrefresh(frame_window); + + char buf[128]; + mvwgetnstr(frame_window, 1, 1, buf, 127); + + debug("(sudo) %s\n", buf); + delwin(frame_window); + erase(); + refresh(); + + return buf; +} + +void notImplementedYet(string msg) +{ + vector entries; + entries.push_back(msg); + displayInformationAndWait("Not implemented yet", entries); +} + +static string driver(FORM *form, FIELD **fields, int ch, WINDOW *win) +{ + //int i; + string val = ""; + switch (ch) { + case 10: + // Or the current field buffer won't be sync with what is displayed + form_driver(form, REQ_NEXT_FIELD); + form_driver(form, REQ_PREV_FIELD); + val = string(field_buffer(fields[1], 0)); + /* + move(LINES-3, 2); + + for (i = 0; fields[i]; i++) + { + printw("%s", trim_whitespaces(field_buffer(fields[i], 0))); + + if (field_opts(fields[i]) & O_ACTIVE) + { + printw("\"\t"); + val = field_buffer(fields[i], 0); + } + else + { + printw(": \""); + } + } + */ + //refresh(); + //pos_form_cursor(form); + return val; + + case KEY_DOWN: + form_driver(form, REQ_NEXT_FIELD); + form_driver(form, REQ_END_LINE); + break; + + case KEY_UP: + form_driver(form, REQ_PREV_FIELD); + form_driver(form, REQ_END_LINE); + break; + + case KEY_LEFT: + form_driver(form, REQ_PREV_CHAR); + break; + + case KEY_RIGHT: + form_driver(form, REQ_NEXT_CHAR); + break; + + // Delete the char before cursor + case KEY_BACKSPACE: + case 127: + form_driver(form, REQ_DEL_PREV); + break; + + // Delete the char under the cursor + case KEY_DC: + form_driver(form, REQ_DEL_CHAR); + break; + + default: + debug("CHAR %d\n", ch); + form_driver(form, ch); + break; + } + + wrefresh(win); + return ""; +} + +string inputField(string title, vector entries, string label) +{ + FORM *form; + FIELD *fields[3]; + WINDOW *frame_window; + + update_cb_(); + + entries.push_back(""); // Add empty line on which the input field will be displayed. + + int mw = maxWidth(entries)+1; + int mh = entries.size(); + int w = mw+2; + int h = mh+4; + if (w-2 < (int)title.length()) + { + w = (int)title.length()+2; + } + w=50; + h=20; + int x = screen_width_/2-w/2; + int y = screen_height_/2-h/2; + frame_window = newwin(h, w, y, x); + + keypad(frame_window, TRUE); + + box(frame_window, 0, 0); + wbkgd(frame_window, COLOR_PAIR(WIN_PAIR)); + + printMiddle(frame_window, 1, w, title.c_str(), COLOR_PAIR(WIN_PAIR)); + mvwaddch(frame_window, 2, 0, ACS_LTEE); + mvwhline(frame_window, 2, 1, ACS_HLINE, w-2); + mvwaddch(frame_window, 2, w-1, ACS_RTEE); + wrefresh(frame_window); + + int r = 3; + for (string e : entries) + { + printAt(frame_window, r, 1, e.c_str(), COLOR_PAIR(WIN_PAIR)); + r++; + } + wrefresh(frame_window); +// wtimeout(frame_window, 1000); + + fields[0] = new_field(1, 10, 1, 1, 0, 0); + fields[1] = new_field(1, 20, 1, 15, 0, 0); + fields[2] = NULL; + set_field_fore(fields[0], COLOR_PAIR(WIN_PAIR)); + set_field_fore(fields[1], COLOR_PAIR(BG_PAIR)); + assert(fields[0] != NULL && fields[1] != NULL); + + set_field_buffer(fields[0], 0, "Password"); + set_field_buffer(fields[1], 0, "IFFOBIFFO"); + + set_field_opts(fields[0], O_VISIBLE | O_PUBLIC | O_AUTOSKIP); + set_field_opts(fields[1], O_VISIBLE | O_PUBLIC | O_EDIT | O_ACTIVE); // O_PUBLIC removed to hide contents. + + set_field_back(fields[1], A_UNDERLINE); + + form = new_form(fields); + assert(form != NULL); + set_form_win(form, frame_window); + WINDOW *subwin = derwin(frame_window, 5, w-2, 5, 1); + set_form_sub(form, subwin); + wbkgd(subwin, COLOR_PAIR(WIN_PAIR)); + post_form(form); + + wtimeout(frame_window, 1000); + wtimeout(subwin, 1000); + + wrefresh(frame_window); + wrefresh(subwin); + refresh(); + + int ch; + string pwd = ""; + pos_form_cursor(form); + form_driver(form, REQ_NEXT_FIELD); + for (;;) + { + ch = getch(); + update_cb_(); + if (ch == ERR) continue; + pwd = driver(form, fields, ch, subwin); + if (pwd != "") break; + } + + unpost_form(form); + free_form(form); + free_field(fields[0]); + free_field(fields[1]); + delwin(frame_window); + delwin(subwin); + refresh(); + + return pwd; +} diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..ae547e9 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2020 Fredrik Öhrström + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef UI_H +#define UI_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define BG_PAIR 1 +#define WIN_PAIR 2 +#define TITLE_PAIR 3 +#define HILIGHT_PAIR 4 + +void initUI(); + +void registerUpdateCB(std::function cb); + +void printAt(WINDOW *win, int y, int x, const char *str, chtype color); +void printMiddle(WINDOW *win, int y, int width, const char *str, chtype color); + +int selectFromMenu(const char *title, const char *menu[]); +int selectFromMenu(const char *title, std::vector menu); +void displayInformationAndWait(std::string title, std::vector entries, int px=-1, int py=-1); +void displayInformationNoWait(WINDOW **win, std::string title, std::vector entries, int px=-1, int py=-1); +void displayStatusLineNoWait(WINDOW **win, std::vector entries, int px=-1, int py=-1); +std::string displayInformationAndInput(std::string title, std::vector entries, int px=-1, int py=-1); +std::string inputField(std::string title, std::vector entries, std::string label); +void notImplementedYet(std::string msg); + +#endif From 8e6d39492deac496c0fdb7ac47828b3ae0067af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Wed, 2 Sep 2020 14:36:10 +0200 Subject: [PATCH 03/61] Update README. --- README.md | 50 ++++++++++++++++---------------------------------- src/main.cc | 3 ++- src/serial.cc | 1 + 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1796fa4..5200487 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,26 @@ Availability of **wmbusmeters** for other Linux distributions can be checked on Remove the wmbus dongle (im871a,amb8465,rfmrx2,cul,d1tc) or the generic rtlsdr dongle (RTL2838) from your computer. -`make; sudo make install` will install wmbusmeters as a daemon that starts -automatically when an appropriate wmbus usb dongle is inserted in the computer. +`make; sudo make install` will install wmbusmeters as a daemon. + +Check the contents of your `/etc/wmbusmeters.conf` file, assuming it +has `device=auto` and you are using a im871a,amb8465 or cul device, +then you can now start the daemon with `sudo systemctl start wmbusmeters`. + +When the daemon is running it will scan for wmbus devices every few seconds +and detect whenever a device is plugged in or removed. + +To have wmbusmeters start automatically when the computer boots do: +`sudo systemctl enable wmbusmeters` + +You can trigger a reload of the config files with `sudo killall -HUP wmbusmetersd` + (Note! make install only works for GNU/Linux. For MacOSX try to start `wmbusmetersd /tmp/thepidfile` from a script instead. Here you can also override the device: `wmbusmetersd --device=/dev/ttyXXY --listento=t1 /tmp/thepidfile`) Check the config file /etc/wmbusmeters.conf and edit the device to -point to your dongle. +point to your dongle or use auto ``` loglevel=normal device=/dev/ttyUSB0:im871a @@ -158,8 +170,7 @@ As you can use: As you can use: -auto, to have wmbusmeters look for the links /dev/im871a, /dev/amb8465, /dev/rfmrx2 and /dev/rtlsdr -(the links are automatically generated by udev if you have run the install scripts) +auto, to have wmbusmeters look existing serial devices and probe them to detect: im871a, amb8465 or cul. /dev/ttyUSB0:amb8465, if you have an amb8465 dongle assigned to ttyUSB0. Other suffixes are im871a,rfmrx2,d1tc,cul. @@ -395,7 +406,6 @@ Binary generated: `./wmbusmeters_0.8_armhf.deb` `/usr/bin/wmbusmeters` `/usr/sbin/wmbusmetersd` `/etc/systemd/system/wmbusmeters.service` -`/etc/udev/rules.d/99-wmbus-usb-serial.rules` `/etc/logrotate.d/wmbusmeters` creates these directories: @@ -404,34 +414,6 @@ creates these directories: and adds the user `wmbusmeters` with no login account. -This means that when a im871a/amb8465 dongle is inserted, then the -appropriate /dev/im871a or /dev/amb8465 link is created. Also the -wmbusmeters daemon will be automatically started/stopped whenever the -im871a/amb8465 dongle is inserted/removed, and the daemon starts when -the computer boots, if the dongle is already inserted. - -You can start/stop the daemon with `sudo systemctl stop wmbusmeters@-dev-im871a_0.service` -or `sudo systemctl stop wmbusmeters@-dev-amb8465_1.service` etc. - -You can trigger a reload of the config files with `sudo killall -HUP wmbusmetersd` - -If you add more dongles, then more daemons gets started, each with a unique name/nr. - -# Daemon without udev rules - -To start the daemon without the udev rules. Then do: -`make install EXTRA_INSTALL_OPTIONS='--no-udev-rules'` -then no udev rules will be added. - -(If you have already installed once before you might have to remove -/dev/udev/rules.d/99-wmbus-usb-serial.rules) - -You can now start/stop the daemon with `sudo systemctl stop wmbusmeters` -the device must of course be correct in the /etc/wmbusmeters.conf file. - -To have wmbusmeters start automatically when the computer boots do: -`sudo systemctl enable wmbusmeters` - # Common problems If the daemon has started then the wmbus device will be taken and you cannot start wmbusmeters manually. diff --git a/src/main.cc b/src/main.cc index 2680a5f..d440e2a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -276,7 +276,8 @@ unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *confi } LinkModeCalculationResult lmcr = calculateLinkModes(config, wmbus.get(), link_modes_matter); - if (lmcr.type != LinkModeCalculationResultType::Success) { + if (lmcr.type != LinkModeCalculationResultType::Success) + { error("%s\n", lmcr.msg.c_str()); } return wmbus; diff --git a/src/serial.cc b/src/serial.cc index 6e80d3b..35cb197 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -812,6 +812,7 @@ void SerialCommunicationManagerImp::closeAll() { LOCK("(serial)", "closeAll", devices_lock_); vector copy = devices_; + devices_.clear(); UNLOCK("(serial)", "closeAll", devices_lock_); for (SerialDeviceImp *d : copy) From 1653aea0b22be5350972fb01edd7f492e1631ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 6 Sep 2020 10:41:04 +0200 Subject: [PATCH 04/61] Detection loop is working. --- Makefile | 2 +- src/main.cc | 10 +++++----- src/serial.cc | 7 +++++++ src/serial.h | 4 ++++ src/wmbus.cc | 6 ++++-- src/wmbus_im871a.cc | 2 +- src/wmbus_utils.h | 2 ++ 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 0f678df..8b6719a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2019 Fredrik Öhrström +# Copyright (C) 2017-2020 Fredrik Öhrström # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/main.cc b/src/main.cc index d440e2a..b9a5043 100644 --- a/src/main.cc +++ b/src/main.cc @@ -266,7 +266,7 @@ unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *confi break; } case DEVICE_UNKNOWN: - warning("No wmbus device found! Exiting!\n"); + verbose("(main) internal error! cannot create an unknown device! exiting!\n"); if (config->daemon) { // If starting as a daemon, wait a bit so that systemd have time to catch up. sleep(1); @@ -408,7 +408,7 @@ void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationMa { if (!printed_warning_) { - info("(main) no wmbus devices detected.\n"); + info("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); printed_warning_ = true; } } @@ -522,11 +522,11 @@ bool start(Configuration *config) if (devices_.size() == 0) { - info("(main) no wmbus devices detected.\n"); + info("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); } - // Every 2 seconds, perform the exact same call again and again. - manager_->startRegularCallback("device_detector", + // Every 2 seconds detect any plugged in or removed wmbus devices. + manager_->startRegularCallback("HOT_PLUG_DETECTOR", 2, [&](){ detectAndConfigureWMBusDevices(config, manager_.get(), &devices_); diff --git a/src/serial.cc b/src/serial.cc index 35cb197..8ca4a2f 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -77,6 +77,7 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager void listenTo(SerialDevice *sd, function cb); void onDisappear(SerialDevice *sd, function cb); + void expectDevicesToWork(); void stop(); void startEventLoop(); void waitForStop(); @@ -711,6 +712,12 @@ void SerialCommunicationManagerImp::onDisappear(SerialDevice *sd, functionon_disappear_ = cb; } +void SerialCommunicationManagerImp::expectDevicesToWork() +{ + debug("(serial) expecting devices to work\n"); + expect_devices_to_work_ = true; +} + void SerialCommunicationManagerImp::stop() { // Notify the main waitForStop thread that we are stopped! diff --git a/src/serial.h b/src/serial.h index dad9636..08d64f1 100644 --- a/src/serial.h +++ b/src/serial.h @@ -78,6 +78,10 @@ struct SerialCommunicationManager virtual void listenTo(SerialDevice *sd, function cb) = 0; // Invoke cb callback when the serial device has disappeared! virtual void onDisappear(SerialDevice *sd, function cb) = 0; + // Normally the communication mananager runs for ever. + // But if you expect configured devices to work, then + // the manager will exit when there are no working devices. + virtual void expectDevicesToWork() = 0; virtual void stop() = 0; virtual void startEventLoop() = 0; virtual void waitForStop() = 0; diff --git a/src/wmbus.cc b/src/wmbus.cc index 9b21276..8dbb43b 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -3342,7 +3342,8 @@ bool Telegram::findFormatBytesFromKnownMeterSignatures(vector *format_byt WMBusCommonImplementation::~WMBusCommonImplementation() { - info("(wmbus) deleted %s\n", toString(type())); + manager_->stopRegularCallback(regular_cb_id_); + debug("(wmbus) deleted %s\n", toString(type())); } WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t, @@ -3358,7 +3359,8 @@ WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t, // Invoke the check status once per minute. Unless internal testing, then it is every 2 seconds. int default_timer = isInternalTestingEnabled() ? CHECKSTATUS_TIMER_INTERNAL_TESTING : CHECKSTATUS_TIMER; - manager_->startRegularCallback(toString(t), default_timer, call(this,checkStatus)); + string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+serial_->device(); + regular_cb_id_ = manager_->startRegularCallback(alarm_id, default_timer, call(this,checkStatus)); } WMBusDeviceType WMBusCommonImplementation::type() diff --git a/src/wmbus_im871a.cc b/src/wmbus_im871a.cc index f465894..988de97 100644 --- a/src/wmbus_im871a.cc +++ b/src/wmbus_im871a.cc @@ -97,7 +97,7 @@ unique_ptr openIM871A(string device, SerialCommunicationManager *manager, } WMBusIM871A::WMBusIM871A(unique_ptr serial, SerialCommunicationManager *manager) : - WMBusCommonImplementation(DEVICE_IM871A, manager,std::move(serial)) + WMBusCommonImplementation(DEVICE_IM871A, manager, std::move(serial)) { sem_init(&command_wait_, 0, 0); manager_->listenTo(this->serial(),call(this,processSerialData)); diff --git a/src/wmbus_utils.h b/src/wmbus_utils.h index 4dd9dc1..da99945 100644 --- a/src/wmbus_utils.h +++ b/src/wmbus_utils.h @@ -71,6 +71,8 @@ struct WMBusCommonImplementation : public virtual WMBus bool link_modes_configured_ {}; LinkModeSet link_modes_ {}; + int regular_cb_id_; + unique_ptr serial_; }; From d127f468076f7660583353664857f059013731a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Mon, 7 Sep 2020 10:36:39 +0200 Subject: [PATCH 05/61] Properly ignores non-wmbus devices. --- src/main.cc | 162 ++++++++++++++++++++++++++++---------------- src/meters.cc | 64 ++++++++++++++++- src/meters.h | 18 ++++- src/serial.cc | 4 +- src/util.h | 8 ++- src/wmbus.cc | 32 +++++---- src/wmbus.h | 1 - src/wmbus_im871a.cc | 4 +- src/wmbus_utils.h | 3 +- 9 files changed, 214 insertions(+), 82 deletions(-) diff --git a/src/main.cc b/src/main.cc index b9a5043..a154a70 100644 --- a/src/main.cc +++ b/src/main.cc @@ -34,14 +34,14 @@ #include #include #include +#include using namespace std; -void oneshotCheck(Configuration *config, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> *meters); +void oneshotCheck(Configuration *config, Telegram *t, Meter *meter); void setupLogFile(Configuration *config); -void setupMeters(Configuration *config, vector> *meters); -void attachMetersToPrinter(Configuration *config, vector> *meters, Printer *printer); -void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationManager *manager, vector> *devices); +void setupMeters(Configuration *config, MeterManager *manager); +void detectAndConfigureWMBusDevices(Configuration *config); unique_ptr createPrinter(Configuration *config); void logStartInformation(Configuration *config); bool start(Configuration *config); @@ -52,17 +52,20 @@ void startDaemon(string pid_file, string device_override, string listento_overri // monitoring the file descrtiptors for the ttys, // background shells. It also invokes regular callbacks // used for monitoring alarms and detecting new devices. -unique_ptr manager_; +unique_ptr serial_manager_; -// Registered meters to decode and relay. -// This does not change during runtime. -vector> meters_; +// Manage registered meters to decode and relay. +unique_ptr meter_manager_; // Current active set of wmbus devices that can receive telegrams. // This can change during runtime, plugging/unplugging wmbus dongles. -vector> devices_; +vector> wmbus_devices_; pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER; +// Remember devices that were not detected as wmbus devices. +// To avoid probing them again and again. +set not_wmbus_devices_; + // Rendering the telegrams to json,fields or shell calls is // done by the printer. unique_ptr printer_; @@ -303,7 +306,7 @@ void setupLogFile(Configuration *config) } } -void setupMeters(Configuration *config, vector> *meters) +void setupMeters(Configuration *config, MeterManager *manager) { for (auto &m : config->meters) { @@ -312,10 +315,13 @@ void setupMeters(Configuration *config, vector> *meters) { #define X(mname,link,info,type,cname) \ case MeterType::type: \ - meters->push_back(create##cname(m)); \ + { \ + auto newm = create##cname(m); \ + newm->addConversions(config->conversions); \ + manager->addMeter(std::move(newm)); \ verbose("(wmbusmeters) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ m.name.c_str(), m.id.c_str(), keymsg); \ - meters->back()->addConversions(config->conversions); \ + } \ break; LIST_OF_METERS #undef X @@ -324,6 +330,7 @@ LIST_OF_METERS break; } + /* if (config->list_shell_envs) { string ignore1, ignore2, ignore3; @@ -345,6 +352,7 @@ LIST_OF_METERS exit(0); } + if (config->list_fields) { printf("Fields produced by meter %s:\n", m.type.c_str()); @@ -357,31 +365,41 @@ LIST_OF_METERS } exit(0); } + */ } } -void attachMetersToPrinter(Configuration *config, vector> *meters, Printer *printer) +void remove_lost_devices_from_nots(vector &devices) { - for (auto &meter : *meters) + vector to_be_removed; + + // Iterate over the devices known to NOT be wmbus devices. + for (const string& nots : not_wmbus_devices_) { - meter->onUpdate([&](Telegram*t,Meter* meter) - { - printer->print(t,meter,&config->jsons,&config->selected_fields); - }); - meter->onUpdate([&](Telegram*t, Meter* meter) - { - oneshotCheck(config, manager_.get(), t, meter, meters); - }); + auto i = std::find(devices.begin(), devices.end(), nots); + if (i == devices.end()) + { + // The device has been removed, therefore + // we have to forget that the device was not a wmbus device. + // Since next time someone plugs in a device, it might be a different + // one getting the same /dev/ttyUSBxx + to_be_removed.push_back(nots); + } + } + + for (string& r : to_be_removed) + { + not_wmbus_devices_.erase(r); } } -void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationManager *manager, vector> *devices) +void detectAndConfigureWMBusDevices(Configuration *config) { - trace("(trace main) checking for dead wmbus devices...\n"); + trace("[MAIN] checking for dead wmbus devices...\n"); LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); vector not_working; - for (auto &w : devices_) + for (auto &w : wmbus_devices_) { if (!w->isWorking()) { @@ -391,20 +409,20 @@ void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationMa for (auto w : not_working) { - auto i = devices_.begin(); - while (i != devices_.end()) + auto i = wmbus_devices_.begin(); + while (i != wmbus_devices_.end()) { if (w == (*i).get()) { // The erased unique_ptr will delete the WMBus object. - devices_.erase(i); + wmbus_devices_.erase(i); break; } i++; } } - if (devices_.size() == 0) + if (wmbus_devices_.size() == 0) { if (!printed_warning_) { @@ -419,27 +437,46 @@ void detectAndConfigureWMBusDevices(Configuration *config, SerialCommunicationMa UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); - trace("(trace main) checking for new wmbus devices...\n"); + trace("[MAIN] checking for new wmbus devices...\n"); + + vector ds = serial_manager_->listSerialDevices(); + + // Did an non-wmbus-device get unplugged? Then remove it from the known-not-wmbus-device set. + remove_lost_devices_from_nots(ds); - vector ds = manager->listSerialDevices(); for (string& device : ds) { - trace("(trace main) serial device %s\n", device.c_str()); - SerialDevice *sd = manager->lookup(device); + trace("[MAIN] serial device %s\n", device.c_str()); + if (not_wmbus_devices_.count(device) > 0) + { + trace("[MAIN] skipping already probed not wmbus serial device %s\n", device.c_str()); + continue; + } + SerialDevice *sd = serial_manager_->lookup(device); if (sd == NULL) { debug("(main) device %s not currently used, detect contents...\n", device.c_str()); // This serial device is not in use. - Detected detected = detectImstAmberCul(device, "", manager); - if (detected.type != DEVICE_UNKNOWN) + Detected detected = detectImstAmberCul(device, "", serial_manager_.get()); + if (detected.type == DEVICE_UNKNOWN) { + // This serial device was something that we could not recognize. + // A modem, an android phone, a teletype Model 33, etc.... + // Mark this serial device as unknown, to avoid repeated detection attempts. + not_wmbus_devices_.insert(device); + info("(main) ignoring %s, it does respond as any of the supported wmbus devices.\n", device.c_str()); + } + else + { + // A newly plugged in device has been detected! Start using it! info("(main) detected %s on %s\n", toString(detected.type), device.c_str()); LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); - unique_ptr w = createWMBusDeviceFrom(&detected, config, manager); - devices->push_back(std::move(w)); - WMBus *wmbus = devices->back().get(); + unique_ptr w = createWMBusDeviceFrom(&detected, config, serial_manager_.get()); + wmbus_devices_.push_back(std::move(w)); + WMBus *wmbus = wmbus_devices_.back().get(); UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); wmbus->setLinkModes(config->listen_to_link_modes); + wmbus->onTelegram([&](vector data){return meter_manager_->handleTelegram(data);}); } } } @@ -497,39 +534,51 @@ bool start(Configuration *config) logStartInformation(config); // Create the manager monitoring all filedescriptors and invoking callbacks. - manager_ = createSerialCommunicationManager(config->exitafter, config->reopenafter); + serial_manager_ = createSerialCommunicationManager(config->exitafter, config->reopenafter); // If our software unexpectedly exits, then stop the manager, to try // to achive a nice shutdown. - onExit(call(manager_.get(),stop)); + onExit(call(serial_manager_.get(),stop)); // Create the printer object that knows how to translate // telegrams into json, fields that are written into log files // or sent to shell invocations. printer_ = createPrinter(config); + meter_manager_ = createMeterManager(); + // Create the Meter objects from the configuration. - setupMeters(config, &meters_); + setupMeters(config, meter_manager_.get()); + // Attach a received-telegram-callback from the meter and // attach it to the printer. - attachMetersToPrinter(config, &meters_, printer_.get()); + meter_manager_->forEachMeter( + [&](Meter *meter) + { + meter->onUpdate([&](Telegram *t,Meter *meter) + { + printer_->print(t, meter, &config->jsons, &config->selected_fields); + oneshotCheck(config, t, meter); + }); + } + ); - manager_->startEventLoop(); + serial_manager_->startEventLoop(); // Detect and initialize any devices. // Future changes are triggered through this callback. printed_warning_ = true; - detectAndConfigureWMBusDevices(config, manager_.get(), &devices_); + detectAndConfigureWMBusDevices(config); - if (devices_.size() == 0) + if (wmbus_devices_.size() == 0) { info("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); } // Every 2 seconds detect any plugged in or removed wmbus devices. - manager_->startRegularCallback("HOT_PLUG_DETECTOR", + serial_manager_->startRegularCallback("HOT_PLUG_DETECTOR", 2, [&](){ - detectAndConfigureWMBusDevices(config, manager_.get(), &devices_); + detectAndConfigureWMBusDevices(config); }); //wmbus->setMeters(&meters); @@ -552,7 +601,7 @@ bool start(Configuration *config) // to decoding the telegrams, finally invoking the printer. // The regular callback invoked to detect changes in the wmbus devices and // the alarm checks, is started in a separate thread. - manager_->waitForStop(); + serial_manager_->waitForStop(); if (config->daemon) { @@ -560,26 +609,25 @@ bool start(Configuration *config) } // Destroy any remaining allocated objects. - devices_.clear(); - meters_.clear(); + wmbus_devices_.clear(); + meter_manager_->removeAllMeters(); printer_.reset(); - manager_.reset(); + serial_manager_.reset(); restoreSignalHandlers(); return gotHupped(); } -void oneshotCheck(Configuration *config, SerialCommunicationManager *manager, Telegram *t, Meter *meter, vector> *meters) +void oneshotCheck(Configuration *config, Telegram *t, Meter *meter) { if (!config->oneshot) return; - for (auto &m : *meters) + if (meter_manager_->hasAllMetersReceivedATelegram()) { - if (m->numUpdates() == 0) return; + // All meters have received at least one update! Stop! + verbose("(main) all meters have received at least one update, stopping.\n"); + serial_manager_->stop(); } - // All meters have received at least one update! Stop! - verbose("(main) all meters have received at least one update, stopping.\n"); - manager->stop(); } void writePid(string pid_file, int pid) diff --git a/src/meters.cc b/src/meters.cc index 91b0bfb..a338c6c 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -25,6 +25,63 @@ #include #include +struct MeterManagerImplementation : public virtual MeterManager +{ + void addMeter(unique_ptr meter) + { + meters_.push_back(std::move(meter)); + } + + void removeAllMeters() + { + meters_.clear(); + } + + void forEachMeter(std::function cb) + { + for (auto &meter : meters_) + { + cb(meter.get()); + } + } + + bool hasAllMetersReceivedATelegram() + { + for (auto &meter : meters_) + { + if (meter->numUpdates() == 0) return false; + } + + return true; + } + + bool handleTelegram(vector data) + { + bool handled = false; + + for (auto &m : meters_) + { + bool h = m->handleTelegram(data); + if (h) handled = true; + } + if (isVerboseEnabled() && !handled) + { + verbose("(wmbus) telegram ignored by all configured meters!\n"); + } + return handled; + } + ~MeterManagerImplementation() {} + +private: + + vector> meters_; +}; + +unique_ptr createMeterManager() +{ + return unique_ptr(new MeterManagerImplementation); +} + MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi, MeterType type) : type_(type), name_(mi.name) @@ -44,7 +101,6 @@ MeterCommonImplementation::MeterCommonImplementation(MeterInfo &mi, for (auto j : mi.jsons) { addJson(j); } - //MeterCommonImplementation::bus()->onTelegram([this](vectorinput_frame){return this->handleTelegram(input_frame);}); } void MeterCommonImplementation::addConversions(std::vector cs) @@ -194,6 +250,12 @@ bool MeterCommonImplementation::isTelegramForMe(Telegram *t) name_.c_str(), toMeterName(type()).c_str(), possible_drivers.c_str()); + + if (possible_drivers == "unknown!") + { + warning("(meter) please consider opening an issue at https://github.com/weetmuts/wmbusmeters/\n"); + warning("(meter) to add support for this unknown mfct,media,version combination\n"); + } } debug("(meter) %s: yes for me\n", name_.c_str()); diff --git a/src/meters.h b/src/meters.h index d1c0318..fc9120b 100644 --- a/src/meters.h +++ b/src/meters.h @@ -23,6 +23,7 @@ #include"wmbus.h" #include +#include #include #define LIST_OF_METERS \ @@ -187,7 +188,7 @@ struct Meter virtual string datetimeOfUpdateHumanReadable() = 0; virtual string datetimeOfUpdateRobot() = 0; - virtual void onUpdate(function cb) = 0; + virtual void onUpdate(std::function cb) = 0; virtual int numUpdates() = 0; virtual void printMeter(Telegram *t, @@ -199,7 +200,8 @@ struct Meter vector *selected_fields) = 0; // The handleTelegram expects an input_frame where the DLL crcs have been removed. - bool handleTelegram(vector input_frame); + // Returns true of this meter handled this telegram! + virtual bool handleTelegram(vector input_frame) = 0; virtual bool isTelegramForMe(Telegram *t) = 0; virtual MeterKeys *meterKeys() = 0; @@ -215,6 +217,18 @@ struct Meter virtual ~Meter() = default; }; +struct MeterManager +{ + virtual void addMeter(unique_ptr meter) = 0; + virtual void removeAllMeters() = 0; + virtual void forEachMeter(std::function cb) = 0; + virtual bool handleTelegram(vector data) = 0; + virtual bool hasAllMetersReceivedATelegram() = 0; + virtual ~MeterManager() = default; +}; + +unique_ptr createMeterManager(); + struct WaterMeter : public virtual Meter { virtual double totalWaterConsumption(Unit u); // m3 diff --git a/src/serial.cc b/src/serial.cc index 8ca4a2f..aca9d72 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -845,7 +845,7 @@ void SerialCommunicationManagerImp::executeTimerCallbacks() { if (t.isTime(curr)) { - trace("(trace serial) invoking callback %d %s\n", t.id, t.name.c_str()); + trace("[SERIAL] invoking callback %d %s\n", t.id, t.name.c_str()); t.last_call = curr; t.callback(); } @@ -913,7 +913,7 @@ void *SerialCommunicationManagerImp::eventLoop() } } - trace("(trace serial) select timeout %d s\n", timeout.tv_sec); + trace("[SERIAL] select timeout %d s\n", timeout.tv_sec); bool num_devices = 0; LOCK("(serial)", "eventLoop2", devices_lock_); diff --git a/src/util.h b/src/util.h index 843a9a6..4ae4827 100644 --- a/src/util.h +++ b/src/util.h @@ -166,7 +166,11 @@ bool startsWith(std::string s, std::vector &data); // Sum the memory used by the heap and stack. size_t memoryUsage(); -#define LOCK(module,func,x) { trace("TRACE " module " " func " locking " #x "\n"); pthread_mutex_lock(&x); trace(module " " func " locked " #x "\n"); } -#define UNLOCK(module,func,x) { trace("TRACE " module " " func " unlocking " #x "\n"); pthread_mutex_unlock(&x); trace(module " " func " unlocked " #x "\n"); } +#define LOCK(module,func,x) { trace("[LOCKING] " module " " func " " #x "\n"); \ + pthread_mutex_lock(&x); \ + trace("[LOCKED] " module " " func " " #x "\n"); } +#define UNLOCK(module,func,x) { trace("[UNLOCKING] " module " " func " " #x "\n"); \ + pthread_mutex_unlock(&x); \ + trace("[UNLOCKED] " module " " func " " #x "\n"); } #endif diff --git a/src/wmbus.cc b/src/wmbus.cc index 8dbb43b..04b0be5 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -3368,11 +3368,6 @@ WMBusDeviceType WMBusCommonImplementation::type() return type_; } -void WMBusCommonImplementation::setMeters(vector> *meters) -{ - meters_ = meters; -} - void WMBusCommonImplementation::onTelegram(function)> cb) { telegram_listeners_.push_back(cb); @@ -3381,7 +3376,6 @@ void WMBusCommonImplementation::onTelegram(function)> cb) bool WMBusCommonImplementation::handleTelegram(vector frame) { bool handled = false; - last_received_ = time(NULL); for (auto f : telegram_listeners_) @@ -3392,10 +3386,6 @@ bool WMBusCommonImplementation::handleTelegram(vector frame) if (h) handled = true; } } - if (isVerboseEnabled() && !handled) - { - verbose("(wmbus) telegram ignored by all configured meters!\n"); - } return handled; } @@ -3506,7 +3496,7 @@ void WMBusCommonImplementation::checkStatus() time_t since = now-last_received_; if (timeout_ > 0 && since < timeout_) { - trace("(trace wmbus) No timeout. All ok. (%d s) Now %d seconds since last telegram was received.\n", since); + trace("[WMBUS] No timeout. All ok. (%d s) Now %d seconds since last telegram was received.\n", since); return; } @@ -3959,13 +3949,12 @@ LIST_OF_MBUS_DEVICES return "?"; } -bool isPossibleDevice(string arg, Device *device) +bool is_formatted_as_device(string arg, Device *device) { size_t colon = arg.find(":"); if (colon == string::npos) { - device->file = arg; device->suffix = ""; device->linkmodes = ""; @@ -3988,3 +3977,20 @@ bool isPossibleDevice(string arg, Device *device) return true; } + +bool isPossibleDevice(string arg, Device *device) +{ + bool ok = is_formatted_as_device(arg, device); + + if (!ok) return false; + + if (device->file == "auto") return true; + if (device->file == "stdin") return true; + if (device->file == "rtlwmbus") return true; + if (device->file == "rtl433") return true; + if (checkCharacterDeviceExists(device->file.c_str(), false) || + checkFileExists(device->file.c_str()) || + checkIfSimulationFile(device->file.c_str())) return true; + + return true; +} diff --git a/src/wmbus.h b/src/wmbus.h index 48798b6..f651218 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -451,7 +451,6 @@ struct WMBus virtual LinkModeSet supportedLinkModes() = 0; virtual int numConcurrentLinkModes() = 0; virtual bool canSetLinkModes(LinkModeSet lms) = 0; - virtual void setMeters(vector> *meters) = 0; virtual void setLinkModes(LinkModeSet lms) = 0; virtual void onTelegram(function)> cb) = 0; virtual SerialDevice *serial() = 0; diff --git a/src/wmbus_im871a.cc b/src/wmbus_im871a.cc index 988de97..a1dbc8c 100644 --- a/src/wmbus_im871a.cc +++ b/src/wmbus_im871a.cc @@ -395,9 +395,9 @@ void WMBusIM871A::waitForResponse() { while (manager_->isRunning()) { - trace("(im871) waitForResponse sem_wait command_wait_\n"); + trace("[IM871A] waitForResponse sem_wait command_wait_\n"); int rc = sem_wait(&command_wait_); - trace("(im871) waitForResponse waited command_wait_\n"); + trace("[IM871A] waitForResponse waited command_wait_\n"); if (rc==0) break; if (rc==-1) { if (errno==EINTR) continue; diff --git a/src/wmbus_utils.h b/src/wmbus_utils.h index da99945..a1cc652 100644 --- a/src/wmbus_utils.h +++ b/src/wmbus_utils.h @@ -32,7 +32,6 @@ struct WMBusCommonImplementation : public virtual WMBus ~WMBusCommonImplementation(); WMBusDeviceType type(); - void setMeters(vector> *meters); void onTelegram(function)> cb); bool handleTelegram(vector frame); void checkStatus(); @@ -60,7 +59,7 @@ struct WMBusCommonImplementation : public virtual WMBus bool is_working_ {}; vector)>> telegram_listeners_; - vector> *meters_; +// vector> *meters_; WMBusDeviceType type_ {}; int protocol_error_count_ {}; time_t timeout_ {}; // If longer silence than timeout, then reset dongle! It might have hanged! From 04175023a8570f44d4e8734b0b3665e8e72c24b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Mon, 7 Sep 2020 13:15:38 +0200 Subject: [PATCH 06/61] Now properly activates meters. --- src/wmbus.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmbus.cc b/src/wmbus.cc index 04b0be5..e2d27e0 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -3992,5 +3992,5 @@ bool isPossibleDevice(string arg, Device *device) checkFileExists(device->file.c_str()) || checkIfSimulationFile(device->file.c_str())) return true; - return true; + return false; } From 570c0c5454394b234fda2859ed4c2bab5c092fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Tue, 8 Sep 2020 14:55:01 +0200 Subject: [PATCH 07/61] Now simulations work. --- src/cmdline.cc | 8 +-- src/config.cc | 17 +++--- src/config.h | 8 ++- src/main.cc | 113 ++++++++++++++++++++++++++--------- src/meter_multical302.cc | 2 +- src/meter_omnipower.cc | 2 +- src/meter_supercom587.cc | 2 +- src/meters.cc | 20 +++++++ src/meters.h | 2 + src/printer.cc | 2 +- src/printer.h | 2 +- src/shell.cc | 2 +- src/testinternals.cc | 2 +- src/util.cc | 2 +- src/wmbus.cc | 123 +++++++++------------------------------ src/wmbus.h | 38 +++++++++--- src/wmbus_im871a.cc | 2 +- src/wmbus_utils.cc | 2 +- 18 files changed, 192 insertions(+), 157 deletions(-) diff --git a/src/cmdline.cc b/src/cmdline.cc index 2b0da5f..1c93325 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -446,14 +446,12 @@ unique_ptr parseCommandLine(int argc, char **argv) { while (argv[i]) { - Device device; - bool ok = isPossibleDevice(argv[i], &device); + bool ok = handleDevice(c, argv[i]); if (!ok) break; - c->wmbus_devices.push_back(device); i++; } - if (c->wmbus_devices.size() == 0) { + if (c->supplied_wmbus_devices.size() == 0) { error("You must supply at least one device to receive wmbus telegrams.\n"); } diff --git a/src/config.cc b/src/config.cc index a07a525..8d144f2 100644 --- a/src/config.cc +++ b/src/config.cc @@ -189,19 +189,22 @@ void handleInternalTesting(Configuration *c, string value) } } -void handleDevice(Configuration *c, string devicefile) +bool handleDevice(Configuration *c, string devicefile) { - // device can be: - // /dev/ttyUSB00 - // auto - // rtlwmbus:/usr/bin/rtl_sdr -f 868.9M -s 1600000 - | /usr/bin/rtl_wmbus - // simulation....txt (read telegrams from file) Device device; bool ok = isPossibleDevice(devicefile, &device); if (ok) { - c->wmbus_devices.push_back(device); + if (device.file == "auto") + { + c->use_auto_detect = true; + } + else + { + c->supplied_wmbus_devices.push_back(device); + } } + return ok; } void handleListenTo(Configuration *c, string mode) diff --git a/src/config.h b/src/config.h index f56e545..29b1093 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2019 Fredrik Öhrström + Copyright (C) 2019-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -79,8 +79,9 @@ struct Configuration bool oneshot {}; int exitafter {}; // Seconds to exit. int reopenafter {}; // Re-open the serial device repeatedly. Silly dongle. - std::vector wmbus_devices; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600 - std::vector mbus_devices; // auto, /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600 + std::vector supplied_wmbus_devices; // /dev/ttyUSB0, simulation.txt, rtlwmbus, /dev/ttyUSB1:9600 + bool use_auto_detect {}; // Set to true if auto was supplied as device. + std::vector supplied_mbus_devices; // /dev/ttyACM0 string telegram_reader; // A set of all link modes (union) that the user requests the wmbus dongle to listen to. LinkModeSet listen_to_link_modes; @@ -98,6 +99,7 @@ unique_ptr loadConfiguration(string root, string device_override, void handleConversions(Configuration *c, string s); void handleSelectedFields(Configuration *c, string s); +bool handleDevice(Configuration *c, string devicefile); enum class LinkModeCalculationResultType { diff --git a/src/main.cc b/src/main.cc index a154a70..c6919b9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -50,8 +50,9 @@ void startDaemon(string pid_file, string device_override, string listento_overri // The serial communication manager takes care of // monitoring the file descrtiptors for the ttys, -// background shells. It also invokes regular callbacks -// used for monitoring alarms and detecting new devices. +// background shells, files and stdin. It also invokes +// regular callbacks used for monitoring alarms and +// detecting new devices. unique_ptr serial_manager_; // Manage registered meters to decode and relay. @@ -369,7 +370,7 @@ LIST_OF_METERS } } -void remove_lost_devices_from_nots(vector &devices) +void remove_lost_devices_from_ignore_list(vector &devices) { vector to_be_removed; @@ -393,11 +394,11 @@ void remove_lost_devices_from_nots(vector &devices) } } -void detectAndConfigureWMBusDevices(Configuration *config) +void check_for_dead_wmbus_devices(Configuration *config) { trace("[MAIN] checking for dead wmbus devices...\n"); - LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + LOCK("(main)", "check_for_dead_wmbus_devices", devices_lock_); vector not_working; for (auto &w : wmbus_devices_) { @@ -435,16 +436,18 @@ void detectAndConfigureWMBusDevices(Configuration *config) printed_warning_ = false; } - UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + UNLOCK("(main)", "check_for_dead_wmbus_devices", devices_lock_); +} - trace("[MAIN] checking for new wmbus devices...\n"); +void perform_auto_scan_of_devices(Configuration *config) +{ + // Enumerate all serial, and other devices that might connect to a wmbus device. + vector devices = serial_manager_->listSerialDevices(); - vector ds = serial_manager_->listSerialDevices(); + // Did a non-wmbus-device get unplugged? Then remove it from the known-not-wmbus-device set. + remove_lost_devices_from_ignore_list(devices); - // Did an non-wmbus-device get unplugged? Then remove it from the known-not-wmbus-device set. - remove_lost_devices_from_nots(ds); - - for (string& device : ds) + for (string& device : devices) { trace("[MAIN] serial device %s\n", device.c_str()); if (not_wmbus_devices_.count(device) > 0) @@ -457,31 +460,75 @@ void detectAndConfigureWMBusDevices(Configuration *config) { debug("(main) device %s not currently used, detect contents...\n", device.c_str()); // This serial device is not in use. - Detected detected = detectImstAmberCul(device, "", serial_manager_.get()); + Detected detected = detectImstAmberCul(device, "", "", serial_manager_.get()); if (detected.type == DEVICE_UNKNOWN) { // This serial device was something that we could not recognize. // A modem, an android phone, a teletype Model 33, etc.... // Mark this serial device as unknown, to avoid repeated detection attempts. not_wmbus_devices_.insert(device); - info("(main) ignoring %s, it does respond as any of the supported wmbus devices.\n", device.c_str()); + info("(main) ignoring %s, it does not respond as any of the supported wmbus devices.\n", device.c_str()); } else { // A newly plugged in device has been detected! Start using it! info("(main) detected %s on %s\n", toString(detected.type), device.c_str()); - LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + LOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); unique_ptr w = createWMBusDeviceFrom(&detected, config, serial_manager_.get()); wmbus_devices_.push_back(std::move(w)); WMBus *wmbus = wmbus_devices_.back().get(); - UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + UNLOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); wmbus->setLinkModes(config->listen_to_link_modes); + //string using_link_modes = wmbus->getLinkModes().hr(); + //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); wmbus->onTelegram([&](vector data){return meter_manager_->handleTelegram(data);}); + wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); } } } } + +void detectAndConfigureWMBusDevices(Configuration *config) +{ + check_for_dead_wmbus_devices(config); + + for (auto &device : config->supplied_wmbus_devices) + { + SerialDevice *sd = serial_manager_->lookup(device.file); + if (sd != NULL) + { + trace("(main) %s already configured\n", sd->device().c_str()); + continue; + } + + Detected detected = detectWMBusDeviceSetting(device.file, + device.suffix, + device.linkmodes, + serial_manager_.get()); + + if (detected.type != DEVICE_UNKNOWN) + { + verbose("(main) configured and detected %s on %s\n", toString(detected.type), device.file.c_str()); + LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + unique_ptr w = createWMBusDeviceFrom(&detected, config, serial_manager_.get()); + wmbus_devices_.push_back(std::move(w)); + WMBus *wmbus = wmbus_devices_.back().get(); + UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); + wmbus->setLinkModes(config->listen_to_link_modes); + //string using_link_modes = wmbus->getLinkModes().hr(); + //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); + wmbus->onTelegram([&](vector data){return meter_manager_->handleTelegram(data);}); + wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); + } + } + + if (config->use_auto_detect) + { + perform_auto_scan_of_devices(config); + } +} + unique_ptr createPrinter(Configuration *config) { return unique_ptr(new Printer(config->json, config->fields, @@ -509,7 +556,7 @@ void logStartInformation(Configuration *config) verbose("(config) store meter files in: \"%s\"\n", config->meterfiles_dir.c_str()); } - for (auto &device : config->wmbus_devices) + for (auto &device : config->supplied_wmbus_devices) { verbose("(config) using device: %s %s\n", device.file.c_str(), device.suffix.c_str()); } @@ -581,21 +628,33 @@ bool start(Configuration *config) detectAndConfigureWMBusDevices(config); }); - //wmbus->setMeters(&meters); - //wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); - //wmbus->setLinkModes(config->listen_to_link_modes); - //string using_link_modes = wmbus->getLinkModes().hr(); - - //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - - //if (detected.type == DEVICE_SIMULATOR) { - //wmbus->simulate(); - //} if (config->daemon) { notice("(wmbusmeters) waiting for telegrams\n"); } + if (!meter_manager_->hasMeters()) + { + notice("No meters configured. Printing id:s of all telegrams heard!\n\n"); + + meter_manager_->onTelegram([](vector frame) { + Telegram t; + MeterKeys mk; + t.parserNoWarnings(); // Try a best effort parse, do not print any warnings. + t.parse(frame, &mk); + t.print(); + t.explainParse("(wmbus)",0); + logTelegram("(wmbus)", t.frame, 0, 0); + return true; + }); + } + + for (auto &w : wmbus_devices_) + { + // Real devices do nothing, but the simulator device will simulate. + w->simulate(); + } + // This thread now sleeps waiting for the serial communication manager to stop. // The manager has already started one thread that performs select and then callbacks // to decoding the telegrams, finally invoking the printer. diff --git a/src/meter_multical302.cc b/src/meter_multical302.cc index 59b530c..36417ec 100644 --- a/src/meter_multical302.cc +++ b/src/meter_multical302.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018-2019 Fredrik Öhrström + Copyright (C) 2018-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/meter_omnipower.cc b/src/meter_omnipower.cc index a275ce3..4e8002d 100644 --- a/src/meter_omnipower.cc +++ b/src/meter_omnipower.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018-2019 Fredrik Öhrström + Copyright (C) 2018-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/meter_supercom587.cc b/src/meter_supercom587.cc index db15e1f..fdc4c43 100644 --- a/src/meter_supercom587.cc +++ b/src/meter_supercom587.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/meters.cc b/src/meters.cc index a338c6c..d3aa7a2 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -55,8 +55,22 @@ struct MeterManagerImplementation : public virtual MeterManager return true; } + bool hasMeters() + { + return meters_.size() != 0; + } + bool handleTelegram(vector data) { + if (!hasMeters()) + { + if (on_telegram_) + { + on_telegram_(data); + } + return true; + } + bool handled = false; for (auto &m : meters_) @@ -70,11 +84,17 @@ struct MeterManagerImplementation : public virtual MeterManager } return handled; } + + void onTelegram(function)> cb) + { + on_telegram_ = cb; + } ~MeterManagerImplementation() {} private: vector> meters_; + function)> on_telegram_; }; unique_ptr createMeterManager() diff --git a/src/meters.h b/src/meters.h index fc9120b..de5e699 100644 --- a/src/meters.h +++ b/src/meters.h @@ -224,6 +224,8 @@ struct MeterManager virtual void forEachMeter(std::function cb) = 0; virtual bool handleTelegram(vector data) = 0; virtual bool hasAllMetersReceivedATelegram() = 0; + virtual bool hasMeters() = 0; + virtual void onTelegram(function)> cb) = 0; virtual ~MeterManager() = default; }; diff --git a/src/printer.cc b/src/printer.cc index c8eff89..c92aff9 100644 --- a/src/printer.cc +++ b/src/printer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/printer.h b/src/printer.h index dff5b48..fa1ec99 100644 --- a/src/printer.h +++ b/src/printer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/shell.cc b/src/shell.cc index 7afabaf..2776629 100644 --- a/src/shell.cc +++ b/src/shell.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/testinternals.cc b/src/testinternals.cc index 8ca01d0..d14aca4 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018-2019 Fredrik Öhrström + Copyright (C) 2018-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/util.cc b/src/util.cc index 0f00b67..2989473 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/wmbus.cc b/src/wmbus.cc index e2d27e0..f05e98e 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -470,94 +470,13 @@ string mediaTypeJSON(int a_field_device_type) return "Unknown"; } -#define CHECK_SAME_GROUP \ -if (ac == AccessCheck::NotSameGroup) \ -{ \ - /* The device exists and is not locked, but we cannot read it! */ \ - error("You are not in the same group as the device %s\n", devicefile.c_str()); \ -} - -Detected detectAuto(string devicefile, - string suffix, - SerialCommunicationManager *handler) -{ - assert(devicefile == "auto"); - - Detected detected; - detected.device = { devicefile, suffix }; - - if (suffix != "") - { - error("You cannot have a suffix appended to auto.\n"); - } - - AccessCheck ac; - - ac = findAndDetect(handler, &devicefile, - [&](string d, SerialCommunicationManager* m){ return detectIM871A(d, &detected, m);}, - "im871a", - "/dev/im871a"); - - if (ac == AccessCheck::AccessOK) - { - return detected; - } - CHECK_SAME_GROUP - - ac = findAndDetect(handler, &devicefile, - [&](string d, SerialCommunicationManager* m){ return detectAMB8465(d, &detected, m);}, - "amb8465", - "/dev/amb8465"); - - if (ac == AccessCheck::AccessOK) - { - return detected; - } - CHECK_SAME_GROUP - - ac = findAndDetect(handler, &devicefile, - [&](string d, SerialCommunicationManager* m){ return detectRawTTY(d, 38400, &detected, m);}, - "rfmrx2", - "/dev/rfmrx2"); - - if (ac == AccessCheck::AccessOK) - { - return detected; - } - CHECK_SAME_GROUP - - ac = findAndDetect(handler, &devicefile, - [&](string d, SerialCommunicationManager* m){ return detectCUL(d, &detected, m);}, - "cul", - "/dev/ttyUSB0"); - - if (ac == AccessCheck::AccessOK) - { - return detected; - } - CHECK_SAME_GROUP - - ac = findAndDetect(handler, &devicefile, - [&](string d, SerialCommunicationManager* m){ return detectRTLSDR(d, &detected, m);}, - "rtlsdr", - "/dev/rtlsdr"); - - if (ac == AccessCheck::AccessOK) - { - return detected; - } - CHECK_SAME_GROUP - - // We could not auto-detect any device. - return { { devicefile, "", ""}, DEVICE_UNKNOWN, 0, false }; -} - Detected detectImstAmberCul(string file, string suffix, + string linkmodes, SerialCommunicationManager *handler) { Detected detected {}; - detected.device = { file, suffix }; + detected.device = { file, suffix, "" }; // If im87a is tested first, a delay of 1s must be inserted // before amb8465 is tested, lest it will not respond properly. @@ -613,15 +532,10 @@ Detected detectImstAmberCul(string file, */ Detected detectWMBusDeviceSetting(string file, string suffix, + string linkmodes, SerialCommunicationManager *handler) { debug("(detect) \"%s\" \"%s\"\n", file.c_str(), suffix.c_str()); - // Look for /dev/im871a /dev/amb8465 /dev/rfmrx2 /dev/rtlsdr - if (file == "auto") - { - debug("(detect) driver: auto\n"); - return detectAuto(file, suffix, handler); - } // If the devicefile is rtlwmbus then the suffix can be a frequency // or the actual command line to use. @@ -683,7 +597,7 @@ Detected detectWMBusDeviceSetting(string file, // Ok, we are left with a single /dev/ttyUSB0 lets talk to it // to figure out what is connected to it. We currently only // know how to detect Imst, Amber or CUL dongles. - return detectImstAmberCul(file, suffix, handler); + return detectImstAmberCul(file, suffix, linkmodes, handler); } /* @@ -3359,7 +3273,9 @@ WMBusCommonImplementation::WMBusCommonImplementation(WMBusDeviceType t, // Invoke the check status once per minute. Unless internal testing, then it is every 2 seconds. int default_timer = isInternalTestingEnabled() ? CHECKSTATUS_TIMER_INTERNAL_TESTING : CHECKSTATUS_TIMER; - string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+serial_->device(); + string info = "simulator"; + if (serial != NULL) info = serial_->device(); + string alarm_id = "CHECK_STATUS "+string(toString(t))+":"+info; regular_cb_id_ = manager_->startRegularCallback(alarm_id, default_timer, call(this,checkStatus)); } @@ -3949,7 +3865,7 @@ LIST_OF_MBUS_DEVICES return "?"; } -bool is_formatted_as_device(string arg, Device *device) +bool split_string_at_colons(string arg, Device *device) { size_t colon = arg.find(":"); @@ -3980,17 +3896,30 @@ bool is_formatted_as_device(string arg, Device *device) bool isPossibleDevice(string arg, Device *device) { - bool ok = is_formatted_as_device(arg, device); - + device->clear(); + bool ok = split_string_at_colons(arg, device); if (!ok) return false; - if (device->file == "auto") return true; - if (device->file == "stdin") return true; - if (device->file == "rtlwmbus") return true; + if (device->file == "auto") + { + // No colons allowed. + if (device->suffix != "" || device->linkmodes != "") return false; + return true; + } + + if (device->file == "stdin") return true; // Suffix like rtlwmbus is allowed. + if (device->file == "rtlwmbus") return true; // Both device=commandline and linkmodes are allowed. if (device->file == "rtl433") return true; if (checkCharacterDeviceExists(device->file.c_str(), false) || checkFileExists(device->file.c_str()) || checkIfSimulationFile(device->file.c_str())) return true; + if (device->file.find('/') != string::npos) + { + // Meter names are forbidden to have slashes in their names. + // This is probably a path to /dev/ttyUSB0 that does not exist right now. + return true; + } + return false; } diff --git a/src/wmbus.h b/src/wmbus.h index f651218..a750a35 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,13 +32,32 @@ bool trimCRCsFrameFormatB(std::vector &payload); struct Device { - // A typical device is: - // /dev/ttyUSB0:im871a:c1,t1 - // /dev/ttyUSB1:amb8465 - // /rtlwmbus::any - std::string file; // /dev/ttyUSB0, simulation_meter.txt, stdin file.raw - std::string suffix; // rtlwmbus im871a amb8465 38400 rtl433 + // A typical device is: FILE : SUFFIX : LINKMODES + // + // FILE + // auto + // simulation_foo.txt + // rtlwmbus + // stdin + // + // FILE : SUFFIX + // /dev/ttyUSB0:im871a + // /dev/ttyUSB1:9600 + // rtlwmbus:434M + // + // FILE : SUFFIX : LINKMODES + // /dev/ttyUSB0:amb8465:c1,t1 + // + std::string file; // auto simulation_meter.txt, stdin, file.raw, rtlwmbus, /dev/ttyUSB0 + std::string suffix; // im871a, rtlwmbus, 9600, 868.9M, rtlwmbus-command line std::string linkmodes; // c1,t1,s1 + + void clear() + { + file = ""; + suffix = ""; + linkmodes = ""; + } }; #define LIST_OF_MBUS_DEVICES \ @@ -469,7 +488,9 @@ struct WMBus virtual ~WMBus() = 0; }; -Detected detectWMBusDeviceSetting(string devicefile, string suffix, +Detected detectWMBusDeviceSetting(string devicefile, + string suffix, + string linkmodes, SerialCommunicationManager *manager); @@ -553,6 +574,7 @@ AccessCheck factoryResetAMB8465(string device, SerialCommunicationManager *handl Detected detectImstAmberCul(string file, string suffix, + string linkmodes, SerialCommunicationManager *handler); #endif diff --git a/src/wmbus_im871a.cc b/src/wmbus_im871a.cc index a1dbc8c..e5ae8cf 100644 --- a/src/wmbus_im871a.cc +++ b/src/wmbus_im871a.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2017-2019 Fredrik Öhrström + Copyright (C) 2017-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/wmbus_utils.cc b/src/wmbus_utils.cc index 29c6a6b..96e0c76 100644 --- a/src/wmbus_utils.cc +++ b/src/wmbus_utils.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018-2019 Fredrik Öhrström + Copyright (C) 2018-2020 Fredrik Öhrström This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From c12a0e8e19dba2d3c4cadbc66f308a5ab1288b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Tue, 8 Sep 2020 20:50:51 +0200 Subject: [PATCH 08/61] Most tests pass. --- src/main.cc | 7 +++++-- src/meters.cc | 8 +++++--- src/meters.h | 4 ++-- src/meters_common_implementation.h | 2 +- src/wmbus.cc | 6 ++++-- src/wmbus.h | 3 +-- tests/test_unknown.sh | 2 ++ 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main.cc b/src/main.cc index c6919b9..aa72216 100644 --- a/src/main.cc +++ b/src/main.cc @@ -481,7 +481,8 @@ void perform_auto_scan_of_devices(Configuration *config) wmbus->setLinkModes(config->listen_to_link_modes); //string using_link_modes = wmbus->getLinkModes().hr(); //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - wmbus->onTelegram([&](vector data){return meter_manager_->handleTelegram(data);}); + bool simulated = detected.type == WMBusDeviceType::DEVICE_SIMULATOR; + wmbus->onTelegram([&, simulated](vector data){return meter_manager_->handleTelegram(data, simulated);}); wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); } } @@ -518,8 +519,10 @@ void detectAndConfigureWMBusDevices(Configuration *config) wmbus->setLinkModes(config->listen_to_link_modes); //string using_link_modes = wmbus->getLinkModes().hr(); //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - wmbus->onTelegram([&](vector data){return meter_manager_->handleTelegram(data);}); + bool simulated = detected.type == WMBusDeviceType::DEVICE_SIMULATOR; + wmbus->onTelegram([&, simulated](vector data){return meter_manager_->handleTelegram(data, simulated);}); wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); + serial_manager_->expectDevicesToWork(); } } diff --git a/src/meters.cc b/src/meters.cc index d3aa7a2..63f57cb 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -60,7 +60,7 @@ struct MeterManagerImplementation : public virtual MeterManager return meters_.size() != 0; } - bool handleTelegram(vector data) + bool handleTelegram(vector data, bool simulated) { if (!hasMeters()) { @@ -75,7 +75,7 @@ struct MeterManagerImplementation : public virtual MeterManager for (auto &m : meters_) { - bool h = m->handleTelegram(data); + bool h = m->handleTelegram(data, simulated); if (h) handled = true; } if (isVerboseEnabled() && !handled) @@ -412,11 +412,13 @@ string concatFields(Meter *m, Telegram *t, char c, vector &prints, vector return s; } -bool MeterCommonImplementation::handleTelegram(vector input_frame) +bool MeterCommonImplementation::handleTelegram(vector input_frame, bool simulated) { Telegram t; bool ok = t.parseHeader(input_frame); + if (simulated) t.markAsSimulated(); + if (!ok || !isTelegramForMe(&t)) { // This telegram is not intended for this meter. diff --git a/src/meters.h b/src/meters.h index de5e699..5c50d3f 100644 --- a/src/meters.h +++ b/src/meters.h @@ -201,7 +201,7 @@ struct Meter // The handleTelegram expects an input_frame where the DLL crcs have been removed. // Returns true of this meter handled this telegram! - virtual bool handleTelegram(vector input_frame) = 0; + virtual bool handleTelegram(vector input_frame, bool simulated) = 0; virtual bool isTelegramForMe(Telegram *t) = 0; virtual MeterKeys *meterKeys() = 0; @@ -222,7 +222,7 @@ struct MeterManager virtual void addMeter(unique_ptr meter) = 0; virtual void removeAllMeters() = 0; virtual void forEachMeter(std::function cb) = 0; - virtual bool handleTelegram(vector data) = 0; + virtual bool handleTelegram(vector data, bool simulated) = 0; virtual bool hasAllMetersReceivedATelegram() = 0; virtual bool hasMeters() = 0; virtual void onTelegram(function)> cb) = 0; diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index 2fce24e..aa63080 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -85,7 +85,7 @@ protected: // Print the dimensionless Text quantity, no unit is needed. void addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json); - bool handleTelegram(vector frame); + bool handleTelegram(vector frame, bool simulated); void printMeter(Telegram *t, string *human_readable, string *fields, char separator, diff --git a/src/wmbus.cc b/src/wmbus.cc index f05e98e..fd03546 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -1215,10 +1215,12 @@ bool Telegram::parseTPLConfig(std::vector::iterator &pos) if (meter_keys->confidentiality_key.size() != 16) { - if (meter_keys->isSimulation()) { + if (isSimulated()) + { debug("(wmbus) simulation without keys, not generating Kmac and Kenc.\n"); return true; } + debug("(wmbus) no key, thus cannot execute kdf.\n"); return false; } AES_CMAC(&meter_keys->confidentiality_key[0], &input[0], 16, &mac[0]); @@ -1351,7 +1353,7 @@ bool Telegram::potentiallyDecrypt(vector::iterator &pos) } else if (tpl_sec_mode == TPLSecurityMode::AES_CBC_NO_IV) { - if (!meter_keys->hasConfidentialityKey() && meter_keys->isSimulation()) + if (!meter_keys->hasConfidentialityKey() && isSimulated()) { CHECK(2); addExplanationAndIncrementPos(pos, 2, "%02x%02x (already) decrypted check bytes", *(pos+0), *(pos+1)); diff --git a/src/wmbus.h b/src/wmbus.h index a750a35..ef13f4e 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -294,11 +294,9 @@ struct MeterKeys { vector confidentiality_key; vector authentication_key; - bool simulation {}; bool hasConfidentialityKey() { return confidentiality_key.size() > 0; } bool hasAuthenticationKey() { return authentication_key.size() > 0; } - bool isSimulation() { return simulation; } }; struct Telegram @@ -413,6 +411,7 @@ struct Telegram void explainParse(string intro, int from); bool isSimulated() { return is_simulated_; } + void markAsSimulated() { is_simulated_ = true; } // Extracted mbus values. std::map> values; diff --git a/tests/test_unknown.sh b/tests/test_unknown.sh index c8a366a..7388dca 100755 --- a/tests/test_unknown.sh +++ b/tests/test_unknown.sh @@ -14,6 +14,8 @@ Forren lansensm 00010206 NOKEY cat > $TEST/test_expected.txt < Date: Tue, 8 Sep 2020 22:11:32 +0200 Subject: [PATCH 09/61] Updated --listenvs= and --listfields= --- CHANGES | 4 + README.md | 6 +- src/cmdline.cc | 11 +- src/config.h | 2 + src/main.cc | 173 ++++++++++++++++++----------- src/meters.cc | 22 +++- src/meters.h | 19 ++++ src/meters_common_implementation.h | 13 +-- src/util.cc | 9 ++ src/util.h | 1 + test.sh | 4 +- tests/test_additional_json.sh | 3 +- tests/test_oneshot.sh | 15 +-- 13 files changed, 183 insertions(+), 99 deletions(-) diff --git a/CHANGES b/CHANGES index 833342c..6b2c712 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ + +To list the shell envs for a meter do --listenvs=multical21 +To list the fields avilable for a meter do --listfields=multical21 + Version 0.9.36: 2020-09-08 Added support for detection of the proper driver diff --git a/README.md b/README.md index 9042466..12592cd 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ depending on if you are running as a daemon or not. # Running without config files, good for experimentation and test. ``` -wmbusmeters version: 0.9.15 +wmbusmeters version: 0.9.36 Usage: wmbusmeters {options} {:suffix} ( [meter_name] [meter_type]{:} [meter_id] [meter_key] )* As you can use: @@ -151,8 +151,8 @@ As you can use: --listento=c1,t1,s1 tell the wmbus dongle to listen to these link modes different dongles support different combinations of modes --c1 --t1 --s1 --s1m ... another way to set the link mode for the dongle - --listenvs list the env variables available for the meter - --listfields list the fields selectable for the meter + --listenvs= list the env variables available for the given meter type + --listfields= list the fields selectable for the given meter type --logfile= use this file instead of stdout --logtelegrams log the contents of the telegrams for easy replay --meterfiles= store meter readings in dir diff --git a/src/cmdline.cc b/src/cmdline.cc index 1c93325..5631b31 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -387,13 +387,15 @@ unique_ptr parseCommandLine(int argc, char **argv) { i++; continue; } - if (!strncmp(argv[i], "--listenvs", 10)) { + if (!strncmp(argv[i], "--listenvs=", 11)) { c->list_shell_envs = true; + c->list_meter = string(argv[i]+11); i++; continue; } - if (!strncmp(argv[i], "--listfields", 12)) { + if (!strncmp(argv[i], "--listfields=", 13)) { c->list_fields = true; + c->list_meter = string(argv[i]+13); i++; continue; } @@ -451,7 +453,10 @@ unique_ptr parseCommandLine(int argc, char **argv) { i++; } - if (c->supplied_wmbus_devices.size() == 0) { + if (c->supplied_wmbus_devices.size() == 0 && + !c->list_shell_envs && + !c->list_fields) + { error("You must supply at least one device to receive wmbus telegrams.\n"); } diff --git a/src/config.h b/src/config.h index 29b1093..bd0844c 100644 --- a/src/config.h +++ b/src/config.h @@ -76,6 +76,8 @@ struct Configuration std::string alarm_expected_activity; // Only warn when within these time periods. bool list_shell_envs {}; bool list_fields {}; + // When asking for envs or fields, this is the meter type to list for. + std::string list_meter; bool oneshot {}; int exitafter {}; // Seconds to exit. int reopenafter {}; // Re-open the serial device repeatedly. Silly dongle. diff --git a/src/main.cc b/src/main.cc index aa72216..be46832 100644 --- a/src/main.cc +++ b/src/main.cc @@ -40,13 +40,16 @@ using namespace std; void oneshotCheck(Configuration *config, Telegram *t, Meter *meter); void setupLogFile(Configuration *config); -void setupMeters(Configuration *config, MeterManager *manager); +void setup_meters(Configuration *config, MeterManager *manager); void detectAndConfigureWMBusDevices(Configuration *config); unique_ptr createPrinter(Configuration *config); void logStartInformation(Configuration *config); bool start(Configuration *config); void startUsingConfigFiles(string root, bool is_daemon, string device_override, string listento_override); void startDaemon(string pid_file, string device_override, string listento_override); // Will use config files. +void list_shell_envs(Configuration *config, string meter_type); +void list_fields(Configuration *config, string meter_type); +unique_ptr create_meter(Configuration *config, MeterType type, MeterInfo *mi, const char *keymsg); // The serial communication manager takes care of // monitoring the file descrtiptors for the ttys, @@ -78,12 +81,15 @@ int main(int argc, char **argv) { auto config = parseCommandLine(argc, argv); - if (config->version) { + if (config->version) + { printf("wmbusmeters: " VERSION "\n"); printf(COMMIT "\n"); exit(0); } - if (config->license) { + + if (config->license) + { const char * license = R"LICENSE( Copyright (C) 2017-2020 Fredrik Öhrström @@ -108,23 +114,40 @@ provided you with this binary. Read the full license for all details. puts(license); exit(0); } - if (config->need_help) { + + if (config->list_shell_envs) + { + list_shell_envs(config.get(), config->list_meter); + exit(0); + } + + if (config->list_fields) + { + list_fields(config.get(), config->list_meter); + exit(0); + } + + if (config->need_help) + { printf("wmbusmeters version: " VERSION "\n"); const char *short_manual = #include"short_manual.h" puts(short_manual); } else - if (config->daemon) { + if (config->daemon) + { startDaemon(config->pid_file, config->device_override, config->listento_override); exit(0); } else - if (config->useconfig) { + if (config->useconfig) + { startUsingConfigFiles(config->config_root, false, config->device_override, config->listento_override); exit(0); } - else { + else + { // We want the data visible in the log file asap! setbuf(stdout, NULL); start(config.get()); @@ -287,6 +310,56 @@ unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *confi return wmbus; } +void list_shell_envs(Configuration *config, string meter_type) +{ + string ignore1, ignore2, ignore3; + vector envs; + Telegram t; + MeterInfo mi; + unique_ptr meter = create_meter(config, toMeterType(meter_type), &mi, ""); + meter->printMeter(&t, + &ignore1, + &ignore2, config->separator, + &ignore3, + &envs, + &config->jsons, + &config->selected_fields); + + for (auto &e : envs) { + int p = e.find('='); + string key = e.substr(0,p); + printf("%s\n", key.c_str()); + } +} + +void list_fields(Configuration *config, string meter_type) +{ + MeterInfo mi; + unique_ptr meter = create_meter(config, toMeterType(meter_type), &mi, ""); + + int width = 0; + for (auto &p : meter->prints()) + { + if ((int)p.field_name.size() > width) width = p.field_name.size(); + } + + string id = padLeft("id", width); + printf("%s The meter id number.\n", id.c_str()); + string name = padLeft("name", width); + printf("%s Your name for the meter.\n", name.c_str()); + string type = padLeft("type", width); + printf("%s Meter type/driver.\n", type.c_str()); + string timestamp = padLeft("timestamp", width); + printf("%s Timestamp when wmbusmeters received the telegram.\n", timestamp.c_str()); + for (auto &p : meter->prints()) + { + if (p.vname == "") continue; + string fn = padLeft(p.field_name, width); + printf("%s %s\n", fn.c_str(), p.help.c_str()); + } +} + + void setupLogFile(Configuration *config) { if (config->use_logfile) @@ -307,66 +380,38 @@ void setupLogFile(Configuration *config) } } -void setupMeters(Configuration *config, MeterManager *manager) +unique_ptr create_meter(Configuration *config, MeterType type, MeterInfo *mi, const char *keymsg) +{ + unique_ptr newm; + + switch (type) + { +#define X(mname,link,info,type,cname) \ + case MeterType::type: \ + { \ + newm = create##cname(*mi); \ + newm->addConversions(config->conversions); \ + verbose("(main) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ + mi->name.c_str(), mi->id.c_str(), keymsg); \ + return newm; \ + } \ + break; +LIST_OF_METERS +#undef X + case MeterType::UNKNOWN: + error("No such meter type \"%s\"\n", mi->type.c_str()); + break; + } + return newm; +} + +void setup_meters(Configuration *config, MeterManager *manager) { for (auto &m : config->meters) { const char *keymsg = (m.key[0] == 0) ? "not-encrypted" : "encrypted"; - switch (toMeterType(m.type)) - { -#define X(mname,link,info,type,cname) \ - case MeterType::type: \ - { \ - auto newm = create##cname(m); \ - newm->addConversions(config->conversions); \ - manager->addMeter(std::move(newm)); \ - verbose("(wmbusmeters) configured \"%s\" \"" #mname "\" \"%s\" %s\n", \ - m.name.c_str(), m.id.c_str(), keymsg); \ - } \ - break; -LIST_OF_METERS -#undef X - case MeterType::UNKNOWN: - error("No such meter type \"%s\"\n", m.type.c_str()); - break; - } - - /* - if (config->list_shell_envs) - { - string ignore1, ignore2, ignore3; - vector envs; - Telegram t; - meters->back()->printMeter(&t, - &ignore1, - &ignore2, config->separator, - &ignore3, - &envs, - &config->jsons, - &config->selected_fields); - printf("Environment variables provided to shell for meter %s:\n", m.type.c_str()); - for (auto &e : envs) { - int p = e.find('='); - string key = e.substr(0,p); - printf("%s\n", key.c_str()); - } - exit(0); - } - - - if (config->list_fields) - { - printf("Fields produced by meter %s:\n", m.type.c_str()); - printf("id\n"); - printf("name\n"); - printf("timestamp\n"); - for (auto &f : meters->back()->fields()) - { - printf("%s\n", f.c_str()); - } - exit(0); - } - */ + auto meter = create_meter(config, toMeterType(m.type), &m, keymsg); + manager->addMeter(std::move(meter)); } } @@ -597,7 +642,7 @@ bool start(Configuration *config) meter_manager_ = createMeterManager(); // Create the Meter objects from the configuration. - setupMeters(config, meter_manager_.get()); + setup_meters(config, meter_manager_.get()); // Attach a received-telegram-callback from the meter and // attach it to the printer. diff --git a/src/meters.cc b/src/meters.cc index 63f57cb..f4fec7b 100644 --- a/src/meters.cc +++ b/src/meters.cc @@ -32,6 +32,11 @@ struct MeterManagerImplementation : public virtual MeterManager meters_.push_back(std::move(meter)); } + Meter *lastAddedMeter() + { + return meters_.back().get(); + } + void removeAllMeters() { meters_.clear(); @@ -165,21 +170,25 @@ void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json) { string default_unit = unitToStringLowerCase(defaultUnitForQuantity(vquantity)); - fields_.push_back(vname+"_"+default_unit); - prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), getValueFunc, NULL, help, field, json }); + string field_name = vname+"_"+default_unit; + fields_.push_back(field_name); + prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), getValueFunc, NULL, help, field, json, field_name }); } void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, Unit unit, function getValueFunc, string help, bool field, bool json) { - prints_.push_back( { vname, vquantity, unit, getValueFunc, NULL, help, field, json }); + string default_unit = unitToStringLowerCase(defaultUnitForQuantity(vquantity)); + string field_name = vname+"_"+default_unit; + fields_.push_back(field_name); + prints_.push_back( { vname, vquantity, unit, getValueFunc, NULL, help, field, json, field_name }); } void MeterCommonImplementation::addPrint(string vname, Quantity vquantity, function getValueFunc, string help, bool field, bool json) { - prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), NULL, getValueFunc, help, field, json } ); + prints_.push_back( { vname, vquantity, defaultUnitForQuantity(vquantity), NULL, getValueFunc, help, field, json, vname } ); } vector MeterCommonImplementation::ids() @@ -192,6 +201,11 @@ vector MeterCommonImplementation::fields() return fields_; } +vector MeterCommonImplementation::prints() +{ + return prints_; +} + string MeterCommonImplementation::name() { return name_; diff --git a/src/meters.h b/src/meters.h index 5c50d3f..ef3d301 100644 --- a/src/meters.h +++ b/src/meters.h @@ -163,6 +163,10 @@ struct MeterInfo vector shells; vector jsons; // Additional static jsons that are added to each message. + MeterInfo() + { + } + MeterInfo(string n, string t, string i, string k, LinkModeSet lms, vector &s, vector &j) { name = n; @@ -175,12 +179,26 @@ struct MeterInfo } }; +struct Print +{ + string vname; // Value name, like: total current previous target + Quantity quantity; // Quantity: Energy, Volume + Unit default_unit; // Default unit for above quantity: KWH, M3 + function getValueDouble; // Callback to fetch the value from the meter. + function getValueString; // Callback to fetch the value from the meter. + string help; // Helpful information on this meters use of this value. + bool field; // If true, print in hr/fields output. + bool json; // If true, print in json and shell env variables. + string field_name; // Field name for default unit. +}; + struct Meter { // This meter listens to these ids. virtual vector ids() = 0; // This meter can report these fields, like total_m3, temp_c. virtual vector fields() = 0; + virtual vector prints() = 0; virtual string meterName() = 0; virtual string name() = 0; virtual MeterType type() = 0; @@ -220,6 +238,7 @@ struct Meter struct MeterManager { virtual void addMeter(unique_ptr meter) = 0; + virtual Meter*lastAddedMeter() = 0; virtual void removeAllMeters() = 0; virtual void forEachMeter(std::function cb) = 0; virtual bool handleTelegram(vector data, bool simulated) = 0; diff --git a/src/meters_common_implementation.h b/src/meters_common_implementation.h index aa63080..67a4e50 100644 --- a/src/meters_common_implementation.h +++ b/src/meters_common_implementation.h @@ -24,22 +24,11 @@ #include #include -struct Print -{ - string vname; // Value name, like: total current previous target - Quantity quantity; // Quantity: Energy, Volume - Unit default_unit; // Default unit for above quantity: KWH, M3 - function getValueDouble; // Callback to fetch the value from the meter. - function getValueString; // Callback to fetch the value from the meter. - string help; // Helpful information on this meters use of this value. - bool field; // If true, print in hr/fields output. - bool json; // If true, print in json and shell env variables. -}; - struct MeterCommonImplementation : public virtual Meter { vector ids(); vector fields(); + vector prints(); string name(); MeterType type(); diff --git a/src/util.cc b/src/util.cc index 2989473..5dc0ec9 100644 --- a/src/util.cc +++ b/src/util.cc @@ -844,6 +844,15 @@ void padWithZeroesTo(vector *content, size_t len, vector *full_con } } +static string space = " "; +string padLeft(string input, int width) +{ + int w = width-input.size(); + if (w < 0) return input; + assert(w < (int)space.length()); + return space.substr(0, w)+input; +} + int parseTime(string time) { int mul = 1; if (time.back() == 'h') { diff --git a/src/util.h b/src/util.h index 4ae4827..fd0b6d8 100644 --- a/src/util.h +++ b/src/util.h @@ -112,6 +112,7 @@ bool loadFile(std::string file, std::vector *buf); std::string eatTo(std::vector &v, std::vector::iterator &i, int c, size_t max, bool *eof, bool *err); void padWithZeroesTo(std::vector *content, size_t len, std::vector *full_content); +std::string padLeft(std::string input, int width); // Parse text string into seconds, 5h = (3600*5) 2m = (60*2) 1s = 1 int parseTime(std::string time); diff --git a/test.sh b/test.sh index 7ee3c2f..e7498dc 100755 --- a/test.sh +++ b/test.sh @@ -54,8 +54,8 @@ if [ "$?" != "0" ]; then RC="1"; fi tests/test_fields.sh $PROG if [ "$?" != "0" ]; then RC="1"; fi -#tests/test_oneshot.sh $PROG broken test -#if [ "$?" != "0" ]; then RC="1"; fi +tests/test_oneshot.sh $PROG broken test +if [ "$?" != "0" ]; then RC="1"; fi tests/test_wrongkeys.sh $PROG if [ "$?" != "0" ]; then RC="1"; fi diff --git a/tests/test_additional_json.sh b/tests/test_additional_json.sh index da68533..6ea42a0 100755 --- a/tests/test_additional_json.sh +++ b/tests/test_additional_json.sh @@ -29,8 +29,7 @@ if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi TESTNAME="Test additional shell envs from cmdline" TESTRESULT="ERROR" -$PROG --format=json --json_floor=5 --json_house="alfa beta" --listenvs --listento=c1 simulations/simulation_additional_json.txt \ - Vatten multical21 76348799 "" | grep METER_ > $TEST/test_output.txt +$PROG --json_floor=5 --json_house="alfa beta" --listenvs=multical21 > $TEST/test_output.txt ENVS=$(cat $TEST/test_output.txt | tr '\n' ' ') diff --git a/tests/test_oneshot.sh b/tests/test_oneshot.sh index 93c4574..f0a28e1 100755 --- a/tests/test_oneshot.sh +++ b/tests/test_oneshot.sh @@ -2,23 +2,20 @@ PROG="$1" +rm -rf testoutput mkdir -p testoutput TEST=testoutput SIM=simulations/simulation_c1.txt -cat $SIM | grep '^{' > $TEST/test_expected.txt +$PROG --oneshot --verbose $SIM MyHeater multical302 67676767 NOKEY MyTapWater multical21 76348799 NOKEY > $TEST/test_output.txt -$PROG --oneshot $SIM MyHeater multical302 '*' '' MyTapWater multical21 76348799 '' > $TEST/test_output.txt +RES=$(cat $TEST/test_output.txt | grep -o "(main) all meters have received at least one update, stopping." | tail -n 1) -RES=$(cat $TEST/test_output.txt | grep -o "all meters have received at least one update, stopping.") - -cat $TEST/test_output.txt - -if [ "$RES" = "all meters have received at least one update, stopping." ] +if [ "$RES" = "(main) all meters have received at least one update, stopping." ] then - echo Oneshot OK + echo OK: Test oneshot else - echo Fail oneshot check! + echo ERROR Fail oneshot check! exit 1 fi From c1d6d36a6d704240b422d7119a554316edecec0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Tue, 8 Sep 2020 22:22:30 +0200 Subject: [PATCH 10/61] Improved --listfields= --- test.sh | 11 ++++++----- tests/config7/etc/wmbusmeters.conf | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test.sh b/test.sh index e7498dc..021c89a 100755 --- a/test.sh +++ b/test.sh @@ -78,10 +78,11 @@ if [ "$?" != "0" ]; then RC="1"; fi tests/test_serial_bads.sh $PROG if [ "$?" != "0" ]; then RC="1"; fi -if [ "$(uname)" = "Linux" ] -then - tests/test_alarm.sh $PROG - if [ "$?" != "0" ]; then RC="1"; fi -fi +#if [ "$(uname)" = "Linux" ] +#then +# tests/test_alarm.sh $PROG +# if [ "$?" != "0" ]; then RC="1"; fi + +#fi exit $RC diff --git a/tests/config7/etc/wmbusmeters.conf b/tests/config7/etc/wmbusmeters.conf index 6d12d03..8e149de 100644 --- a/tests/config7/etc/wmbusmeters.conf +++ b/tests/config7/etc/wmbusmeters.conf @@ -1,4 +1,4 @@ -loglevel=normal +loglevel=debug # Set internaltesting=true to shorten times for test to finish in reasonable time. internaltesting=true device=must_be_overriden From 93272b1b0ca2efabe70a3419f25fcadaea59931d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Tue, 8 Sep 2020 22:34:24 +0200 Subject: [PATCH 11/61] Added missing include. --- src/main.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cc b/src/main.cc index be46832..b4faef6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include From 09e36b89991540846e8120994f9d4d0c61789adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Tue, 8 Sep 2020 23:05:42 +0200 Subject: [PATCH 12/61] Daemon working again. --- install.sh | 4 +++- src/cmdline.cc | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 94c8cba..40e23cd 100755 --- a/install.sh +++ b/install.sh @@ -90,8 +90,10 @@ if [ "$ADDUSER" = "true" ] then if [ -z "$ID" ] then + # Create the wmbusmeters group, if it does not already exist. + groupadd -f wmbusmeters # Create the wmbusmeters user - useradd --system --shell $USERSHELL --groups dialout wmbusmeters + useradd --system --shell $USERSHELL -g wmbusmeters --groups dialout wmbusmeters echo user: added wmbusmeters else echo user: wmbusmeters unmodified diff --git a/src/cmdline.cc b/src/cmdline.cc index 5631b31..433babd 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -454,6 +454,7 @@ unique_ptr parseCommandLine(int argc, char **argv) { } if (c->supplied_wmbus_devices.size() == 0 && + c->use_auto_detect == false && !c->list_shell_envs && !c->list_fields) { From e754cda2953764a72d94b078b52271820d4e4397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 13 Sep 2020 13:24:46 +0200 Subject: [PATCH 13/61] Added --listmeters and --listmeters=water --- src/cmdline.cc | 15 ++++++++++++++- src/config.h | 2 ++ src/main.cc | 18 ++++++++++++++++++ src/util.cc | 15 +++++++++++++++ src/util.h | 1 + 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/cmdline.cc b/src/cmdline.cc index 433babd..a985b47 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -399,6 +399,18 @@ unique_ptr parseCommandLine(int argc, char **argv) { i++; continue; } + if (!strncmp(argv[i], "--listmeters=", 13)) { + c->list_meters = true; + c->list_meters_search = string(argv[i]+13); + i++; + continue; + } + else if (!strncmp(argv[i], "--listmeters", 12)) { + c->list_meters = true; + c->list_meters_search = ""; + i++; + continue; + } if (!strcmp(argv[i], "--oneshot")) { c->oneshot = true; i++; @@ -456,7 +468,8 @@ unique_ptr parseCommandLine(int argc, char **argv) { if (c->supplied_wmbus_devices.size() == 0 && c->use_auto_detect == false && !c->list_shell_envs && - !c->list_fields) + !c->list_fields && + !c->list_meters) { error("You must supply at least one device to receive wmbus telegrams.\n"); } diff --git a/src/config.h b/src/config.h index bd0844c..18740a2 100644 --- a/src/config.h +++ b/src/config.h @@ -76,6 +76,8 @@ struct Configuration std::string alarm_expected_activity; // Only warn when within these time periods. bool list_shell_envs {}; bool list_fields {}; + bool list_meters {}; + std::string list_meters_search; // When asking for envs or fields, this is the meter type to list for. std::string list_meter; bool oneshot {}; diff --git a/src/main.cc b/src/main.cc index b4faef6..91508cb 100644 --- a/src/main.cc +++ b/src/main.cc @@ -51,6 +51,7 @@ void startUsingConfigFiles(string root, bool is_daemon, string device_override, void startDaemon(string pid_file, string device_override, string listento_override); // Will use config files. void list_shell_envs(Configuration *config, string meter_type); void list_fields(Configuration *config, string meter_type); +void list_meters(Configuration *config); unique_ptr create_meter(Configuration *config, MeterType type, MeterInfo *mi, const char *keymsg); // The serial communication manager takes care of @@ -129,6 +130,12 @@ provided you with this binary. Read the full license for all details. exit(0); } + if (config->list_meters) + { + list_meters(config.get()); + exit(0); + } + if (config->need_help) { printf("wmbusmeters version: " VERSION "\n"); @@ -361,6 +368,17 @@ void list_fields(Configuration *config, string meter_type) } } +void list_meters(Configuration *config) +{ +#define X(mname,link,info,type,cname) \ + if (config->list_meters_search == "" || \ + stringFoundCaseIgnored(#info, config->list_meters_search) || \ + stringFoundCaseIgnored(#mname, config->list_meters_search)) \ + printf("%-14s %s\n", #mname, #info); +LIST_OF_METERS +#undef X +} + void setupLogFile(Configuration *config) { diff --git a/src/util.cc b/src/util.cc index 5dc0ec9..ef2238e 100644 --- a/src/util.cc +++ b/src/util.cc @@ -19,6 +19,7 @@ #include"meters.h" #include"shell.h" +#include #include #include #include @@ -1391,3 +1392,17 @@ void setAlarmShells(vector &alarm_shells) { alarm_shells_ = alarm_shells; } + +bool stringFoundCaseIgnored(string haystack, string needle) +{ + // Modify haystack and needle, in place, to become lowercase. + std::for_each(haystack.begin(), haystack.end(), [](char & c) { + c = ::tolower(c); + }); + std::for_each(needle.begin(), needle.end(), [](char & c) { + c = ::tolower(c); + }); + + // Now use default c++ find, return true if needle was found in haystack. + return haystack.find(needle) != string::npos; +} diff --git a/src/util.h b/src/util.h index fd0b6d8..b407546 100644 --- a/src/util.h +++ b/src/util.h @@ -54,6 +54,7 @@ std::string strdatetime(struct tm *date); // Return for example: 2010-03-21 15:22:03 std::string strdatetimesec(struct tm *date); +bool stringFoundCaseIgnored(std::string haystack, std::string needle); void xorit(uchar *srca, uchar *srcb, uchar *dest, int len); void shiftLeft(uchar *srca, uchar *srcb, int len); From 71804f1d925dbce9e05687d92681edc07a55927b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 13 Sep 2020 15:39:09 +0200 Subject: [PATCH 14/61] Audo detect swradio to start rtlwmbus. --- src/main.cc | 139 +++++++++++++++++++++++++++++++----------- src/serial.cc | 69 ++++++++++++++++++--- src/serial.h | 7 ++- src/testinternals.cc | 2 +- src/wmbus.h | 11 +++- src/wmbus_rtl433.cc | 6 +- src/wmbus_rtlwmbus.cc | 5 +- 7 files changed, 185 insertions(+), 54 deletions(-) diff --git a/src/main.cc b/src/main.cc index 91508cb..2868111 100644 --- a/src/main.cc +++ b/src/main.cc @@ -71,7 +71,11 @@ pthread_mutex_t devices_lock_ = PTHREAD_MUTEX_INITIALIZER; // Remember devices that were not detected as wmbus devices. // To avoid probing them again and again. -set not_wmbus_devices_; +set not_serial_wmbus_devices_; + +// The software radio devices are always swradio devices +// but they might not be available for wmbusmeters. +set not_swradio_wmbus_devices_; // Rendering the telegrams to json,fields or shell calls is // done by the printer. @@ -239,7 +243,7 @@ unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *confi } verbose("(rtlwmbus) using command: %s\n", command.c_str()); } - wmbus = openRTLWMBUS(command, manager, + wmbus = openRTLWMBUS(detected->device.file, command, manager, [command](){ warning("(rtlwmbus) child process exited! " "Command was: \"%s\"\n", command.c_str()); @@ -275,7 +279,7 @@ unique_ptr createWMBusDeviceFrom(Detected *detected, Configuration *confi } verbose("(rtl433) using command: %s\n", command.c_str()); } - wmbus = openRTL433(command, manager, + wmbus = openRTL433(detected->device.file, command, manager, [command](){ warning("(rtl433) child process exited! " "Command was: \"%s\"\n", command.c_str()); @@ -435,12 +439,12 @@ void setup_meters(Configuration *config, MeterManager *manager) } } -void remove_lost_devices_from_ignore_list(vector &devices) +void remove_lost_serial_devices_from_ignore_list(vector &devices) { vector to_be_removed; // Iterate over the devices known to NOT be wmbus devices. - for (const string& nots : not_wmbus_devices_) + for (const string& nots : not_serial_wmbus_devices_) { auto i = std::find(devices.begin(), devices.end(), nots); if (i == devices.end()) @@ -455,7 +459,31 @@ void remove_lost_devices_from_ignore_list(vector &devices) for (string& r : to_be_removed) { - not_wmbus_devices_.erase(r); + not_serial_wmbus_devices_.erase(r); + } +} + +void remove_lost_swradio_devices_from_ignore_list(vector &devices) +{ + vector to_be_removed; + + // Iterate over the devices known to NOT be wmbus devices. + for (const string& nots : not_swradio_wmbus_devices_) + { + auto i = std::find(devices.begin(), devices.end(), nots); + if (i == devices.end()) + { + // The device has been removed, therefore + // we have to forget that the device was not a wmbus device. + // Since next time someone plugs in a device, it might be a different + // one getting the same /dev/ttyUSBxx + to_be_removed.push_back(nots); + } + } + + for (string& r : to_be_removed) + { + not_swradio_wmbus_devices_.erase(r); } } @@ -504,18 +532,36 @@ void check_for_dead_wmbus_devices(Configuration *config) UNLOCK("(main)", "check_for_dead_wmbus_devices", devices_lock_); } -void perform_auto_scan_of_devices(Configuration *config) +void open_wmbus_device(Configuration *config, string how, string device, Detected *detected) { - // Enumerate all serial, and other devices that might connect to a wmbus device. + // A newly plugged in device has been manually configured or automatically detected! Start using it! + info("(main) %s %s on %s\n", how.c_str(), toString(detected->type), device.c_str()); + + LOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); + unique_ptr w = createWMBusDeviceFrom(detected, config, serial_manager_.get()); + wmbus_devices_.push_back(std::move(w)); + WMBus *wmbus = wmbus_devices_.back().get(); + wmbus->setLinkModes(config->listen_to_link_modes); + //string using_link_modes = wmbus->getLinkModes().hr(); + //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); + bool simulated = detected->type == WMBusDeviceType::DEVICE_SIMULATOR; + wmbus->onTelegram([&, simulated](vector data){return meter_manager_->handleTelegram(data, simulated);}); + wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); + UNLOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); +} + +void perform_auto_scan_of_serial_devices(Configuration *config) +{ + // Enumerate all serial devices that might connect to a wmbus device. vector devices = serial_manager_->listSerialDevices(); // Did a non-wmbus-device get unplugged? Then remove it from the known-not-wmbus-device set. - remove_lost_devices_from_ignore_list(devices); + remove_lost_serial_devices_from_ignore_list(devices); for (string& device : devices) { trace("[MAIN] serial device %s\n", device.c_str()); - if (not_wmbus_devices_.count(device) > 0) + if (not_serial_wmbus_devices_.count(device) > 0) { trace("[MAIN] skipping already probed not wmbus serial device %s\n", device.c_str()); continue; @@ -531,29 +577,60 @@ void perform_auto_scan_of_devices(Configuration *config) // This serial device was something that we could not recognize. // A modem, an android phone, a teletype Model 33, etc.... // Mark this serial device as unknown, to avoid repeated detection attempts. - not_wmbus_devices_.insert(device); + not_serial_wmbus_devices_.insert(device); info("(main) ignoring %s, it does not respond as any of the supported wmbus devices.\n", device.c_str()); } else { - // A newly plugged in device has been detected! Start using it! - info("(main) detected %s on %s\n", toString(detected.type), device.c_str()); - LOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); - unique_ptr w = createWMBusDeviceFrom(&detected, config, serial_manager_.get()); - wmbus_devices_.push_back(std::move(w)); - WMBus *wmbus = wmbus_devices_.back().get(); - UNLOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); - wmbus->setLinkModes(config->listen_to_link_modes); - //string using_link_modes = wmbus->getLinkModes().hr(); - //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - bool simulated = detected.type == WMBusDeviceType::DEVICE_SIMULATOR; - wmbus->onTelegram([&, simulated](vector data){return meter_manager_->handleTelegram(data, simulated);}); - wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); + open_wmbus_device(config, "auto scan detected", device, &detected); } } } } +void perform_auto_scan_of_swradio_devices(Configuration *config) +{ + // Enumerate all swradio devices, that can be used. + vector devices = serial_manager_->listSWRadioDevices(); + + // Did an unavailable swradio-device get unplugged? Then remove it from the known-not-swradio-device set. + remove_lost_swradio_devices_from_ignore_list(devices); + + for (string& device : devices) + { + trace("[MAIN] swradio device %s\n", device.c_str()); + if (not_swradio_wmbus_devices_.count(device) > 0) + { + trace("[MAIN] skipping already probed swradio device %s\n", device.c_str()); + continue; + } + SerialDevice *sd = serial_manager_->lookup(device); + if (sd == NULL) + { + debug("(main) swradio device %s not currently used, detect contents...\n", device.c_str()); + // This serial device is not in use. + Detected detected; + AccessCheck ac = detectRTLSDR(device, &detected, serial_manager_.get()); + if (ac != AccessCheck::AccessOK) + { + // We cannot access this swradio device. + not_swradio_wmbus_devices_.insert(device); + info("(main) ignoring swradio %s since it is unavailable.\n", device.c_str()); + } + else + { + detected.device.file = device; + open_wmbus_device(config, "auto scan detected", device, &detected); + } + } + } +} + +void perform_auto_scan_of_devices(Configuration *config) +{ + perform_auto_scan_of_serial_devices(config); + perform_auto_scan_of_swradio_devices(config); +} void detectAndConfigureWMBusDevices(Configuration *config) { @@ -575,19 +652,7 @@ void detectAndConfigureWMBusDevices(Configuration *config) if (detected.type != DEVICE_UNKNOWN) { - verbose("(main) configured and detected %s on %s\n", toString(detected.type), device.file.c_str()); - LOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); - unique_ptr w = createWMBusDeviceFrom(&detected, config, serial_manager_.get()); - wmbus_devices_.push_back(std::move(w)); - WMBus *wmbus = wmbus_devices_.back().get(); - UNLOCK("(main)", "detectAndConfigureWMBusDevices", devices_lock_); - wmbus->setLinkModes(config->listen_to_link_modes); - //string using_link_modes = wmbus->getLinkModes().hr(); - //verbose("(config) listen to link modes: %s\n", using_link_modes.c_str()); - bool simulated = detected.type == WMBusDeviceType::DEVICE_SIMULATOR; - wmbus->onTelegram([&, simulated](vector data){return meter_manager_->handleTelegram(data, simulated);}); - wmbus->setTimeout(config->alarm_timeout, config->alarm_expected_activity); - serial_manager_->expectDevicesToWork(); + open_wmbus_device(config, "manual configuration", device.str(), &detected); } } diff --git a/src/serial.cc b/src/serial.cc index aca9d72..ffbf95a 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -70,7 +70,7 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager ~SerialCommunicationManagerImp(); unique_ptr createSerialDeviceTTY(string dev, int baud_rate); - unique_ptr createSerialDeviceCommand(string command, vector args, vector envs, + unique_ptr createSerialDeviceCommand(string device, string command, vector args, vector envs, function on_exit); unique_ptr createSerialDeviceFile(string file); unique_ptr createSerialDeviceSimulator(); @@ -95,6 +95,7 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager void resetInitiated() { debug("(serial) initiate reset\n"); resetting_ = true; } void resetCompleted() { debug("(serial) reset completed\n"); resetting_ = false; } vector listSerialDevices(); + vector listSWRadioDevices(); SerialDevice *lookup(std::string device); private: @@ -402,7 +403,7 @@ bool SerialDeviceTTY::working() struct SerialDeviceCommand : public SerialDeviceImp { - SerialDeviceCommand(string command, vector args, vector envs, + SerialDeviceCommand(string device, string command, vector args, vector envs, SerialCommunicationManagerImp *manager, function on_exit); ~SerialDeviceCommand(); @@ -413,12 +414,13 @@ struct SerialDeviceCommand : public SerialDeviceImp bool send(vector &data); int available(); bool working(); - string device() { return command_; } - + string device() { return device_; } + string command() { return command_; } SerialCommunicationManager *manager() { return manager_; } private: + string device_; string command_; int pid_ {}; vector args_; @@ -430,12 +432,15 @@ struct SerialDeviceCommand : public SerialDeviceImp function on_exit_; }; -SerialDeviceCommand::SerialDeviceCommand(string command, +SerialDeviceCommand::SerialDeviceCommand(string device, + string command, vector args, vector envs, SerialCommunicationManagerImp *manager, function on_exit) { + assert(device != ""); + device_ = device; command_ = command; args_ = args; envs_ = envs; @@ -468,6 +473,11 @@ void SerialDeviceCommand::close() stopBackgroundShell(pid_); pid_ = 0; } + if (on_disappear_) + { + on_disappear_(); + on_disappear_ = NULL; + } ::flock(fd_, LOCK_UN); ::close(fd_); fd_ = -1; @@ -672,12 +682,13 @@ unique_ptr SerialCommunicationManagerImp::createSerialDeviceTTY(st return unique_ptr(new SerialDeviceTTY(device, baud_rate, this)); } -unique_ptr SerialCommunicationManagerImp::createSerialDeviceCommand(string command, +unique_ptr SerialCommunicationManagerImp::createSerialDeviceCommand(string device, + string command, vector args, vector envs, function on_exit) { - return unique_ptr(new SerialDeviceCommand(command, args, envs, this, on_exit)); + return unique_ptr(new SerialDeviceCommand(device, command, args, envs, this, on_exit)); } unique_ptr SerialCommunicationManagerImp::createSerialDeviceFile(string file) @@ -1149,6 +1160,12 @@ vector SerialCommunicationManagerImp::listSerialDevices() list.push_back("Please add code here!"); return list; } +vector SerialCommunicationManagerImp::listSWRadioDevices() +{ + vector list; + list.push_back("Please add code here!"); + return list; +} #endif #if defined(__linux__) @@ -1263,6 +1280,44 @@ vector SerialCommunicationManagerImp::listSerialDevices() return found_serials; } +vector SerialCommunicationManagerImp::listSWRadioDevices() +{ + struct dirent **entries; + vector found_swradios; + string devdir = "/dev/"; + + int n = scandir(devdir.c_str(), &entries, NULL, sorty); + if (n < 0) + { + perror("scandir"); + return found_swradios; + } + + for (int i=0; id_name; + + if (name == ".." || name == ".") + { + free(entries[i]); + continue; + } + // swradio0 swradio1 swradio2 ... + if (name.length() > 7 && + !strncmp(name.c_str(), "swradio", 7) && + name[7] >= '0' && + name[7] <= '9') + { + string rtlsdr = devdir+name; + found_swradios.push_back(rtlsdr); + } + free(entries[i]); + } + free(entries); + + return found_swradios; +} + #endif #define CHECK_SPEED(x) { if (speed == x) return #x; } diff --git a/src/serial.h b/src/serial.h index 08d64f1..6566baa 100644 --- a/src/serial.h +++ b/src/serial.h @@ -65,7 +65,8 @@ struct SerialCommunicationManager // Read from a /dev/ttyUSB0 or /dev/ttyACM0 device with baud settings. virtual unique_ptr createSerialDeviceTTY(string dev, int baud_rate) = 0; // Read from a sub shell. - virtual unique_ptr createSerialDeviceCommand(string command, + virtual unique_ptr createSerialDeviceCommand(string device, + string command, vector args, vector envs, function on_exit) = 0; @@ -95,8 +96,10 @@ struct SerialCommunicationManager virtual void resetInitiated() = 0; virtual void resetCompleted() = 0; - // List all real serial devices. + // List all real serial devices (avoid pseudo ttys) virtual std::vector listSerialDevices() = 0; + // List all all rtlsdr swradio devices + virtual std::vector listSWRadioDevices() = 0; // Return a serial device for the given device, if it exists! Otherwise NULL. virtual SerialDevice *lookup(std::string device) = 0; virtual ~SerialCommunicationManager(); diff --git a/src/testinternals.cc b/src/testinternals.cc index d14aca4..a9b6c87 100644 --- a/src/testinternals.cc +++ b/src/testinternals.cc @@ -218,7 +218,7 @@ int test_linkmodes() unique_ptr wmbus_im871a = openIM871A("", manager.get(), std::move(serial1)); unique_ptr wmbus_amb8465 = openAMB8465("", manager.get(), std::move(serial2)); - unique_ptr wmbus_rtlwmbus = openRTLWMBUS("", manager.get(), [](){}, std::move(serial3)); + unique_ptr wmbus_rtlwmbus = openRTLWMBUS("", "", manager.get(), [](){}, std::move(serial3)); unique_ptr wmbus_rawtty = openRawTTY("", 0, manager.get(), std::move(serial4)); Configuration nometers_config; diff --git a/src/wmbus.h b/src/wmbus.h index ef13f4e..d2b485f 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -58,6 +58,13 @@ struct Device suffix = ""; linkmodes = ""; } + + string str() + { + if (linkmodes != "") return file+":"+suffix+":"+linkmodes; + if (suffix != "") return file+":"+suffix; + return file; + } }; #define LIST_OF_MBUS_DEVICES \ @@ -501,9 +508,9 @@ unique_ptr openAMB8465(string device, SerialCommunicationManager *manager unique_ptr serial_override); unique_ptr openRawTTY(string device, int baudrate, SerialCommunicationManager *manager, unique_ptr serial_override); -unique_ptr openRTLWMBUS(string device, SerialCommunicationManager *manager, std::function on_exit, +unique_ptr openRTLWMBUS(string device, string command, SerialCommunicationManager *manager, std::function on_exit, unique_ptr serial_override); -unique_ptr openRTL433(string device, SerialCommunicationManager *manager, std::function on_exit, +unique_ptr openRTL433(string device, string command, SerialCommunicationManager *manager, std::function on_exit, unique_ptr serial_override); unique_ptr openCUL(string device, SerialCommunicationManager *manager, unique_ptr serial_override); diff --git a/src/wmbus_rtl433.cc b/src/wmbus_rtl433.cc index bbe14c6..7ae381b 100644 --- a/src/wmbus_rtl433.cc +++ b/src/wmbus_rtl433.cc @@ -72,8 +72,8 @@ private: string setup_; }; -unique_ptr openRTL433(string command, SerialCommunicationManager *manager, - function on_exit, unique_ptr serial_override) +unique_ptr openRTL433(string device, string command, SerialCommunicationManager *manager, + function on_exit, unique_ptr serial_override) { vector args; vector envs; @@ -84,7 +84,7 @@ unique_ptr openRTL433(string command, SerialCommunicationManager *manager WMBusRTL433 *imp = new WMBusRTL433(std::move(serial_override), manager); return unique_ptr(imp); } - auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs, on_exit); + auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit); WMBusRTL433 *imp = new WMBusRTL433(std::move(serial), manager); return unique_ptr(imp); } diff --git a/src/wmbus_rtlwmbus.cc b/src/wmbus_rtlwmbus.cc index 1196435..642c162 100644 --- a/src/wmbus_rtlwmbus.cc +++ b/src/wmbus_rtlwmbus.cc @@ -72,7 +72,7 @@ private: string setup_; }; -unique_ptr openRTLWMBUS(string command, SerialCommunicationManager *manager, +unique_ptr openRTLWMBUS(string device, string command, SerialCommunicationManager *manager, function on_exit, unique_ptr serial_override) { vector args; @@ -84,7 +84,7 @@ unique_ptr openRTLWMBUS(string command, SerialCommunicationManager *manag WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial_override), manager); return unique_ptr(imp); } - auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs, on_exit); + auto serial = manager->createSerialDeviceCommand(device, "/bin/sh", args, envs, on_exit); WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial), manager); return unique_ptr(imp); } @@ -93,6 +93,7 @@ WMBusRTLWMBUS::WMBusRTLWMBUS(unique_ptr serial, SerialCommunicatio WMBusCommonImplementation(DEVICE_RTLWMBUS, manager, std::move(serial)) { manager_->listenTo(this->serial(),call(this,processSerialData)); + manager_->onDisappear(this->serial(),call(this,disconnectedFromDevice)); reset(); } From 802e62cbbdb8fc740e9195c24c7a67a4a956592e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Sun, 13 Sep 2020 16:55:22 +0200 Subject: [PATCH 15/61] Now logging defaults to stderr. --- README.md | 3 +- src/cmdline.cc | 9 ++++- src/config.h | 2 +- src/main.cc | 70 +++++++++++++++++++++++++++-------- src/serial.cc | 3 +- src/wmbus.cc | 34 +++++++++-------- src/wmbus.h | 9 ++++- tests/test_additional_json.sh | 6 +-- tests/test_aes.sh | 2 +- tests/test_apas.sh | 4 +- tests/test_c1_meters.sh | 2 +- tests/test_config1.sh | 2 +- tests/test_config4.sh | 2 +- tests/test_linkmodes.sh | 4 +- tests/test_listen_to_all.sh | 2 +- tests/test_meterfiles.sh | 10 ++--- tests/test_oneshot.sh | 4 +- tests/test_shell.sh | 2 +- tests/test_shell2.sh | 2 +- tests/test_t1_meters.sh | 4 +- tests/test_unknown.sh | 2 +- tests/test_wrongkeys.sh | 12 ++++-- 22 files changed, 127 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 12592cd..ee20de3 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,8 @@ As you can use: --separator= change field separator to c --shell= invokes cmdline with env variables containing the latest reading --useconfig= load config files from dir/etc - --usestderr write debug/verbose and logging output to stderr + --usestderr write notices/debug/verbose and other logging output to stderr (the default) + --usestdoutforlogging write debug/verbose and logging output to stdout --verbose for more information As you can use: diff --git a/src/cmdline.cc b/src/cmdline.cc index a985b47..213eda0 100644 --- a/src/cmdline.cc +++ b/src/cmdline.cc @@ -351,8 +351,13 @@ unique_ptr parseCommandLine(int argc, char **argv) { i++; continue; } - if (!strncmp(argv[i], "--usestderr=", 10)) { - c->use_stderr = true; + if (!strncmp(argv[i], "--usestderr", 11)) { + c->use_stderr_for_log = true; + i++; + continue; + } + if (!strncmp(argv[i], "--usestdoutforlogging", 13)) { + c->use_stderr_for_log = false; i++; continue; } diff --git a/src/config.h b/src/config.h index 18740a2..311500c 100644 --- a/src/config.h +++ b/src/config.h @@ -65,7 +65,7 @@ struct Configuration MeterFileNaming meterfiles_naming {}; MeterFileTimestamp meterfiles_timestamp {}; // Default is never. bool use_logfile {}; - bool use_stderr {}; + bool use_stderr_for_log = true; // Default is to use stderr for logging. std::string logfile; bool json {}; bool fields {}; diff --git a/src/main.cc b/src/main.cc index 2868111..e8b879f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -77,6 +77,10 @@ set not_serial_wmbus_devices_; // but they might not be available for wmbusmeters. set not_swradio_wmbus_devices_; +// When manually supplying stdin or a file, then, after +// it has been read, do not open it again! +set do_not_open_file_again_; + // Rendering the telegrams to json,fields or shell calls is // done by the printer. unique_ptr printer_; @@ -338,7 +342,8 @@ void list_shell_envs(Configuration *config, string meter_type) &config->jsons, &config->selected_fields); - for (auto &e : envs) { + for (auto &e : envs) + { int p = e.find('='); string key = e.substr(0,p); printf("%s\n", key.c_str()); @@ -498,6 +503,10 @@ void check_for_dead_wmbus_devices(Configuration *config) if (!w->isWorking()) { not_working.push_back(w.get()); + if (!config->use_auto_detect) + { + notice("Lost %s closing %s\n", w->device().c_str(), toString(w->type())); + } } } @@ -535,8 +544,14 @@ void check_for_dead_wmbus_devices(Configuration *config) void open_wmbus_device(Configuration *config, string how, string device, Detected *detected) { // A newly plugged in device has been manually configured or automatically detected! Start using it! - info("(main) %s %s on %s\n", how.c_str(), toString(detected->type), device.c_str()); - + if (config->use_auto_detect) + { + notice("Detected %s %s on %s\n", how.c_str(), toString(detected->type), device.c_str()); + } + else + { + verbose("(main) %s %s on %s\n", how.c_str(), toString(detected->type), device.c_str()); + } LOCK("(main)", "perform_auto_scan_of_devices", devices_lock_); unique_ptr w = createWMBusDeviceFrom(detected, config, serial_manager_.get()); wmbus_devices_.push_back(std::move(w)); @@ -571,7 +586,7 @@ void perform_auto_scan_of_serial_devices(Configuration *config) { debug("(main) device %s not currently used, detect contents...\n", device.c_str()); // This serial device is not in use. - Detected detected = detectImstAmberCul(device, "", "", serial_manager_.get()); + Detected detected = detectImstAmberCul(device, "", "", serial_manager_.get(), true, false, false); if (detected.type == DEVICE_UNKNOWN) { // This serial device was something that we could not recognize. @@ -645,14 +660,26 @@ void detectAndConfigureWMBusDevices(Configuration *config) continue; } - Detected detected = detectWMBusDeviceSetting(device.file, - device.suffix, - device.linkmodes, - serial_manager_.get()); - - if (detected.type != DEVICE_UNKNOWN) + if (do_not_open_file_again_.count(device.file) == 0) { - open_wmbus_device(config, "manual configuration", device.str(), &detected); + Detected detected = detectWMBusDeviceSetting(device.file, + device.suffix, + device.linkmodes, + serial_manager_.get()); + + if (detected.type != DEVICE_UNKNOWN) + { + if (detected.is_stdin || detected.is_file) + { + // Only read stdin and files once! + do_not_open_file_again_.insert(device.file); + } + open_wmbus_device(config, "manual configuration", device.str(), &detected); + } + } + else + { + trace("(MAIN) ignoring handled file %s\n", device.file.c_str()); } } @@ -708,7 +735,7 @@ bool start(Configuration *config) debugEnabled(config->debug); internalTestingEnabled(config->internaltesting); traceEnabled(config->trace); - stderrEnabled(config->use_stderr); + stderrEnabled(config->use_stderr_for_log); setAlarmShells(config->alarm_shells); logStartInformation(config); @@ -749,16 +776,29 @@ bool start(Configuration *config) printed_warning_ = true; detectAndConfigureWMBusDevices(config); - if (wmbus_devices_.size() == 0) + if (!config->use_auto_detect) { - info("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); + serial_manager_->expectDevicesToWork(); + if (wmbus_devices_.size() == 0) + { + notice("(main) no wmbus device configured! Exiting.\n"); + serial_manager_->stop(); + } + } + else + { + if (wmbus_devices_.size() == 0) + { + notice("(main) no wmbus device detected, waiting for a device to be plugged in.\n"); + } } // Every 2 seconds detect any plugged in or removed wmbus devices. serial_manager_->startRegularCallback("HOT_PLUG_DETECTOR", 2, [&](){ - detectAndConfigureWMBusDevices(config); + if (serial_manager_ && config) + detectAndConfigureWMBusDevices(config); }); if (config->daemon) diff --git a/src/serial.cc b/src/serial.cc index ffbf95a..9ece822 100644 --- a/src/serial.cc +++ b/src/serial.cc @@ -596,7 +596,7 @@ void SerialDeviceFile::close() ::close(fd_); fd_ = -1; manager_->closed(this); - verbose("(serialtty) closed %s %d\n", file_.c_str(), fd_); + verbose("(serialfile) closed %s %d\n", file_.c_str(), fd_); } void SerialDeviceFile::checkIfShouldReopen() @@ -895,6 +895,7 @@ void *SerialCommunicationManagerImp::eventLoop() { if (!d->skippingCallbacks()) { + trace("(SERIAL) select read on fd %d\n", d->fd()); FD_SET(d->fd(), &readfds); } if (!d->working()) all_working = false; diff --git a/src/wmbus.cc b/src/wmbus.cc index fd03546..603be2c 100644 --- a/src/wmbus.cc +++ b/src/wmbus.cc @@ -473,7 +473,10 @@ string mediaTypeJSON(int a_field_device_type) Detected detectImstAmberCul(string file, string suffix, string linkmodes, - SerialCommunicationManager *handler) + SerialCommunicationManager *handler, + bool is_tty, + bool is_stdin, + bool is_file) { Detected detected {}; detected.device = { file, suffix, "" }; @@ -505,7 +508,7 @@ Detected detectImstAmberCul(string file, } // We could not auto-detect either. - return { { file, suffix }, DEVICE_UNKNOWN, 0, false }; + return { { file, suffix }, DEVICE_UNKNOWN, 0, false, is_tty, is_stdin, is_file }; } /** @@ -564,6 +567,7 @@ Detected detectWMBusDeviceSetting(string file, bool is_file = checkFileExists(file.c_str()); debug("(detect) is_tty=%d is_stdin=%d is_file=%d\n", is_tty, is_stdin, is_file); + if (!is_tty && !is_stdin && !is_file) { debug("(detect) not a valid device file %s\n", file.c_str()); @@ -573,21 +577,21 @@ Detected detectWMBusDeviceSetting(string file, bool override_tty = !is_tty; - if (suffix == "amb8465") return { { file, suffix }, DEVICE_AMB8465, 0, override_tty }; - if (suffix == "im871a") return { { file, suffix }, DEVICE_IM871A, 0, override_tty }; - if (suffix == "rfmrx2") return { { file, suffix }, DEVICE_RFMRX2, 0, override_tty }; - if (suffix == "rtlwmbus") return { { file, suffix }, DEVICE_RTLWMBUS, 0, override_tty }; - if (suffix == "rtl433") return { { file, suffix}, DEVICE_RTL433, 0, override_tty }; - if (suffix == "cul") return { { file, suffix}, DEVICE_CUL, 0, override_tty }; - if (suffix == "d1tc") return { { file, suffix}, DEVICE_D1TC, 0, override_tty }; - if (suffix == "wmb13u") return { { file, suffix}, DEVICE_WMB13U, 0, override_tty }; - if (suffix == "simulation") return { { file, suffix}, DEVICE_SIMULATOR, 0, override_tty }; + if (suffix == "amb8465") return { { file, suffix }, DEVICE_AMB8465, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "im871a") return { { file, suffix }, DEVICE_IM871A, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "rfmrx2") return { { file, suffix }, DEVICE_RFMRX2, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "rtlwmbus") return { { file, suffix }, DEVICE_RTLWMBUS, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "rtl433") return { { file, suffix}, DEVICE_RTL433, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "cul") return { { file, suffix}, DEVICE_CUL, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "d1tc") return { { file, suffix}, DEVICE_D1TC, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "wmb13u") return { { file, suffix}, DEVICE_WMB13U, 0, override_tty, is_tty, is_stdin, is_file }; + if (suffix == "simulation") return { { file, suffix}, DEVICE_SIMULATOR, 0, override_tty, is_tty, is_stdin, is_file }; // If the suffix is a number, then assume that it is a baud rate. - if (isNumber(suffix)) return { { file, suffix} , DEVICE_RAWTTY, atoi(suffix.c_str()), override_tty }; + if (isNumber(suffix)) return { { file, suffix} , DEVICE_RAWTTY, atoi(suffix.c_str()), override_tty, is_tty, is_stdin, is_file }; // If the suffix is empty and its not a tty, then read raw telegrams from stdin or the file. - if (suffix == "" && !is_tty) return { { file, suffix}, DEVICE_RAWTTY, 0, true }; + if (suffix == "" && !is_tty) return { { file, suffix}, DEVICE_RAWTTY, 0, true, is_tty, is_stdin, is_file }; if (suffix != "") { @@ -597,7 +601,7 @@ Detected detectWMBusDeviceSetting(string file, // Ok, we are left with a single /dev/ttyUSB0 lets talk to it // to figure out what is connected to it. We currently only // know how to detect Imst, Amber or CUL dongles. - return detectImstAmberCul(file, suffix, linkmodes, handler); + return detectImstAmberCul(file, suffix, linkmodes, handler, is_tty, is_stdin, is_file); } /* @@ -3378,7 +3382,7 @@ void WMBusCommonImplementation::disconnectedFromDevice() { if (is_working_) { - info("(wmbus) lost %s closing %s\n", device().c_str(), toString(type())); + debug("(wmbus) disconnect %s closing %s\n", device().c_str(), toString(type())); is_working_ = false; } } diff --git a/src/wmbus.h b/src/wmbus.h index d2b485f..f748c12 100644 --- a/src/wmbus.h +++ b/src/wmbus.h @@ -97,12 +97,16 @@ struct Detected // instead open the device->file as a file instead . This is to allows feeding the wmbus drivers // using stdin or a file. This is primarily used for internal testing. bool override_tty; + bool is_tty; + bool is_file; + bool is_stdin; void set(WMBusDeviceType t, int br, bool ot) { type = t; baudrate = br; override_tty = ot; + is_tty = is_file = is_stdin = false; } }; @@ -581,6 +585,9 @@ AccessCheck factoryResetAMB8465(string device, SerialCommunicationManager *handl Detected detectImstAmberCul(string file, string suffix, string linkmodes, - SerialCommunicationManager *handler); + SerialCommunicationManager *handler, + bool is_tty, + bool is_stdin, + bool is_file); #endif diff --git a/tests/test_additional_json.sh b/tests/test_additional_json.sh index 6ea42a0..78051ba 100755 --- a/tests/test_additional_json.sh +++ b/tests/test_additional_json.sh @@ -10,7 +10,7 @@ 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 \ MyTapWater multical21 76348799 "" \ - > $TEST/test_output.txt + > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then @@ -29,7 +29,7 @@ if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi TESTNAME="Test additional shell envs from cmdline" TESTRESULT="ERROR" -$PROG --json_floor=5 --json_house="alfa beta" --listenvs=multical21 > $TEST/test_output.txt +$PROG --json_floor=5 --json_house="alfa beta" --listenvs=multical21 > $TEST/test_output.txt 2> $TEST/test_stderr.txt ENVS=$(cat $TEST/test_output.txt | tr '\n' ' ') @@ -65,7 +65,7 @@ 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 --listento=t1 > $TEST/test_output.txt +$PROG --useconfig=tests/config6 --device=simulations/simulation_shell.txt --listento=t1 > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then diff --git a/tests/test_aes.sh b/tests/test_aes.sh index 22cc0d2..2fa3704 100755 --- a/tests/test_aes.sh +++ b/tests/test_aes.sh @@ -16,7 +16,7 @@ cat $TEST/test_input.txt | $PROG --format=json "stdin:rtlwmbus" \ ApWater apator162 88888888 00000000000000000000000000000000 \ Vatten multical21 76348799 28F64A24988064A079AA2C807D6102AE \ Wasser supercom587 77777777 5065747220486F6C79737A6577736B69 \ - > $TEST/test_output.txt + > $TEST/test_output.txt 2> $TEST/test_stderr.txt cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt diff --git a/tests/test_apas.sh b/tests/test_apas.sh index 0c6a2b3..e972482 100755 --- a/tests/test_apas.sh +++ b/tests/test_apas.sh @@ -18,7 +18,7 @@ METERS="Wasser apator162 20202020 NOKEY MyTapWatere apator162 27202020 NOKEY" cat simulations/simulation_apas.txt | grep '^{' > $TEST/test_expected.txt -$PROG --format=json simulations/simulation_apas.txt $METERS > $TEST/test_output.txt +$PROG --format=json simulations/simulation_apas.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt @@ -31,7 +31,7 @@ then fi cat simulations/simulation_apas.txt | grep '^|' | sed 's/^|//' > $TEST/test_expected.txt -$PROG --format=fields simulations/simulation_apas.txt $METERS > $TEST/test_output.txt +$PROG --format=fields simulations/simulation_apas.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9].[0-9][0-9]$/1111-11-11 11:11.11/' > $TEST/test_responses.txt diff --git a/tests/test_c1_meters.sh b/tests/test_c1_meters.sh index 1046d7a..c8ff30a 100755 --- a/tests/test_c1_meters.sh +++ b/tests/test_c1_meters.sh @@ -17,7 +17,7 @@ $PROG --format=json simulations/simulation_c1.txt \ MyElement qcaloric 78563412 "" \ Rum cma12w 66666666 "" \ My403Cooling multical403 78780102 "" \ - > $TEST/test_output.txt + > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then diff --git a/tests/test_config1.sh b/tests/test_config1.sh index 44956ed..93f9e10 100755 --- a/tests/test_config1.sh +++ b/tests/test_config1.sh @@ -9,7 +9,7 @@ TESTRESULT="ERROR" cat simulations/simulation_c1.txt | grep '^{' > $TEST/test_expected.txt -$PROG --useconfig=tests/config1 > $TEST/test_output.txt +$PROG --useconfig=tests/config1 > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then diff --git a/tests/test_config4.sh b/tests/test_config4.sh index 43ffb6a..1afc1b2 100755 --- a/tests/test_config4.sh +++ b/tests/test_config4.sh @@ -9,7 +9,7 @@ TESTRESULT="ERROR" cat simulations/simulation_conversionsadded.txt | grep '^{' > $TEST/test_expected.txt -$PROG --useconfig=tests/config4 > $TEST/test_output.txt +$PROG --useconfig=tests/config4 > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then diff --git a/tests/test_linkmodes.sh b/tests/test_linkmodes.sh index 6b051bd..8ac59c6 100755 --- a/tests/test_linkmodes.sh +++ b/tests/test_linkmodes.sh @@ -44,7 +44,7 @@ fi TESTNAME="Test that setting multical21 to t1 fails" TESTRESULT="ERROR" -MSG=$($PROG --listento=c1,t1 simulations/simulation_t1_and_c1.txt \ +MSG=$($PROG --listento=c1,t1 --usestdoutforlog simulations/simulation_t1_and_c1.txt \ MyTapWater multical21:t1 76348799 "" \ Wasser apator162:c1 20202020 "") @@ -60,7 +60,7 @@ fi TESTNAME="Test that the warning for missed telegrams work" TESTRESULT="ERROR" -MSG=$($PROG --s1 simulations/simulation_t1_and_c1.txt \ +MSG=$($PROG --s1 --usestdoutforlog simulations/simulation_t1_and_c1.txt \ MyTapWater multical21:c1 76348799 "" \ Wasser apator162:t1 20202020 "" | tr -d ' \n') diff --git a/tests/test_listen_to_all.sh b/tests/test_listen_to_all.sh index f681eb7..9354104 100755 --- a/tests/test_listen_to_all.sh +++ b/tests/test_listen_to_all.sh @@ -196,7 +196,7 @@ fi TESTNAME="Test listen and print any meter heard on stdout" TESTRESULT="ERROR" -$PROG --t1 simulations/simulation_t1.txt 2>&1 > $LOGFILE +$PROG --t1 simulations/simulation_t1.txt 2> $LOGFILE RES=$(diff $LOGFILE $LOGFILE_EXPECTED) diff --git a/tests/test_meterfiles.sh b/tests/test_meterfiles.sh index db99f6f..76fa249 100755 --- a/tests/test_meterfiles.sh +++ b/tests/test_meterfiles.sh @@ -10,7 +10,7 @@ TESTRESULT="ERROR" rm -f /tmp/MyTapWater cat simulations/simulation_c1.txt | grep '^{' | grep 76348799 | tail -n 1 > $TEST/test_expected.txt -$PROG --meterfiles --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" > /dev/null +$PROG --meterfiles --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" > /dev/null 2> $TEST/test_stderr.txt cat /tmp/MyTapWater | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt if [ "$?" = "0" ] @@ -28,7 +28,7 @@ TESTRESULT="ERROR" rm -rf /tmp/testmeters mkdir /tmp/testmeters cat simulations/simulation_c1.txt | grep '^{' | grep 76348799 | tail -n 1 > $TEST/test_expected.txt -$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=name-id --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" > /dev/null +$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=name-id --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" > /dev/null 2> $TEST/test_stderr.txt cat /tmp/testmeters/MyTapWater-76348799 | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt if [ "$?" = "0" ] @@ -46,7 +46,7 @@ TESTRESULT="ERROR" rm -rf /tmp/testmeters mkdir /tmp/testmeters cat simulations/simulation_c1.txt | grep '^{' | grep 76348799 | tail -n 1 > $TEST/test_expected.txt -$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" +$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" 2> $TEST/test_stderr.txt cat /tmp/testmeters/76348799 | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt if [ "$?" = "0" ] @@ -65,7 +65,7 @@ TESTRESULT="ERROR" rm -rf /tmp/testmeters mkdir /tmp/testmeters cat simulations/simulation_c1.txt | grep '^{' | grep 76348799 | tail -n 1 > $TEST/test_expected.txt -$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --meterfilestimestamp=day --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" +$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --meterfilestimestamp=day --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" 2> $TEST/test_stderr.txt cat /tmp/testmeters/76348799_$(date +%Y-%m-%d) | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt if [ "$?" = "0" ] @@ -80,7 +80,7 @@ if [ "$TESTRESULT" = "ERROR" ]; then echo ERROR: $TESTNAME; exit 1; fi rm -rf /tmp/testmeters mkdir /tmp/testmeters cat simulations/simulation_c1.txt | grep '^{' | grep 76348799 | tail -n 1 > $TEST/test_expected.txt -$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --meterfilestimestamp=minute --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" +$PROG --meterfiles=/tmp/testmeters --meterfilesnaming=id --meterfilestimestamp=minute --format=json simulations/simulation_c1.txt MyTapWater multical21 76348799 "" 2> $TEST/test_stderr.txt cat /tmp/testmeters/76348799_$(date +%Y-%m-%d_%H:%M) | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_response.txt diff $TEST/test_expected.txt $TEST/test_response.txt if [ "$?" = "0" ] diff --git a/tests/test_oneshot.sh b/tests/test_oneshot.sh index f0a28e1..bbc84b8 100755 --- a/tests/test_oneshot.sh +++ b/tests/test_oneshot.sh @@ -8,9 +8,9 @@ mkdir -p testoutput TEST=testoutput SIM=simulations/simulation_c1.txt -$PROG --oneshot --verbose $SIM MyHeater multical302 67676767 NOKEY MyTapWater multical21 76348799 NOKEY > $TEST/test_output.txt +$PROG --oneshot --verbose $SIM MyHeater multical302 67676767 NOKEY MyTapWater multical21 76348799 NOKEY > $TEST/test_output.txt 2> $TEST/test_stderr.txt -RES=$(cat $TEST/test_output.txt | grep -o "(main) all meters have received at least one update, stopping." | tail -n 1) +RES=$(cat $TEST/test_stderr.txt | grep -o "(main) all meters have received at least one update, stopping." | tail -n 1) if [ "$RES" = "(main) all meters have received at least one update, stopping." ] then diff --git a/tests/test_shell.sh b/tests/test_shell.sh index 6e0537c..3114585 100755 --- a/tests/test_shell.sh +++ b/tests/test_shell.sh @@ -8,7 +8,7 @@ TEST=testoutput TESTNAME="Test shell invocation" TESTRESULT="ERROR" -$PROG --shell='echo "$METER_JSON"' simulations/simulation_shell.txt MWW supercom587 12345678 "" > $TEST/test_output.txt +$PROG --shell='echo "$METER_JSON"' simulations/simulation_shell.txt MWW supercom587 12345678 "" > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff --git a/tests/test_shell2.sh b/tests/test_shell2.sh index 2aace32..43ccbfb 100755 --- a/tests/test_shell2.sh +++ b/tests/test_shell2.sh @@ -7,7 +7,7 @@ mkdir -p $TEST TESTNAME="Test shell in config file" TESTRESULT="ERROR" -$PROG --useconfig=tests/config5 > $TEST/test_output.txt +$PROG --useconfig=tests/config5 > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then diff --git a/tests/test_t1_meters.sh b/tests/test_t1_meters.sh index 7365b89..aa18a3b 100755 --- a/tests/test_t1_meters.sh +++ b/tests/test_t1_meters.sh @@ -36,7 +36,7 @@ METERS="MyWarmWater supercom587 12345678 NOKEY Witer topaseskr 78563412 NOKEY" cat simulations/simulation_t1.txt | grep '^{' > $TEST/test_expected.txt -$PROG --format=json simulations/simulation_t1.txt $METERS > $TEST/test_output.txt +$PROG --format=json simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt @@ -51,7 +51,7 @@ then fi cat simulations/simulation_t1.txt | grep '^|' | sed 's/^|//' > $TEST/test_expected.txt -$PROG --format=fields simulations/simulation_t1.txt $METERS > $TEST/test_output.txt +$PROG --format=fields simulations/simulation_t1.txt $METERS > $TEST/test_output.txt 2> $TEST/test_stderr.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9].[0-9][0-9]$/1111-11-11 11:11.11/' > $TEST/test_responses.txt diff --git a/tests/test_unknown.sh b/tests/test_unknown.sh index 7388dca..e482d82 100755 --- a/tests/test_unknown.sh +++ b/tests/test_unknown.sh @@ -21,7 +21,7 @@ cat > $TEST/test_expected.txt < $TEST/test_output.txt +$PROG --format=json --usestdoutforlogging simulations/simulation_unknown.txt $METERS > $TEST/test_output.txt if [ "$?" = "0" ] then cat $TEST/test_output.txt | sed 's/"timestamp":"....-..-..T..:..:..Z"/"timestamp":"1111-11-11T11:11:11Z"/' > $TEST/test_responses.txt diff --git a/tests/test_wrongkeys.sh b/tests/test_wrongkeys.sh index 8eff2e7..b6e396e 100755 --- a/tests/test_wrongkeys.sh +++ b/tests/test_wrongkeys.sh @@ -16,9 +16,15 @@ cat $TEST/test_input.txt | $PROG --format=json "stdin:rtlwmbus" \ ApWater apator162 88888888 00000000000000000000000000000001 \ Vatten multical21 76348799 28F64A24988064A079AA2C807D6102AF \ Wasser supercom587 77777777 5065747220486F6C79737A6577736B6A \ - > $TEST/test_output.txt + > $TEST/test_output.txt 2> $TEST/test_stderr.txt -cat $TEST/test_output.txt | grep -v '{"media' > $TEST/test_response.txt +if [ -s $TEST/test_output.txt ] +then + echo "Bad no stdout expected! But got bytes anyway!" + echo "ERROR: $TESTNAME" + TESTRESULT="ERROR" + exit 1 +fi cat < $TEST/test_expected.txt (wmbus) decrypted content failed check, did you use the correct decryption key? Ignoring telegram. @@ -26,7 +32,7 @@ cat < $TEST/test_expected.txt (wmbus) decrypted content failed check, did you use the correct decryption key? Ignoring telegram. EOF -diff $TEST/test_expected.txt $TEST/test_response.txt +diff $TEST/test_expected.txt $TEST/test_stderr.txt if [ "$?" = "0" ] then echo "OK: $TESTNAME" From 891e3f4228549fbe96cc63265d95f19217ee93bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Fri, 18 Sep 2020 20:05:59 +0200 Subject: [PATCH 16/61] Alarm test work again. --- CHANGES | 26 +++++++++ README.md | 3 + install.sh | 24 +++++--- simulations/simulation_alarm.txt | 2 +- src/admin.cc | 19 ------ src/main.cc | 92 +++++++++++++++++++++++++----- src/serial.cc | 57 ++++++++++++------ src/serial.h | 2 + src/shell.cc | 19 ++++++ src/shell.h | 1 + src/testinternals.cc | 1 + src/timings.h | 6 +- src/wmbus.cc | 79 +++++++++++++++---------- src/wmbus_im871a.cc | 4 +- src/wmbus_rtlwmbus.cc | 5 ++ src/wmbus_simulator.cc | 6 +- src/wmbus_utils.h | 2 - test.sh | 11 ++-- tests/config7/etc/wmbusmeters.conf | 4 +- tests/test_alarm.sh | 14 +++-- 20 files changed, 264 insertions(+), 113 deletions(-) diff --git a/CHANGES b/CHANGES index 6b2c712..a94ac1e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,32 @@ +IMPORTANT CHANGES THAT MIGHT AFFECT YOU! + +** No udev rules + +Wmbusmeters new default behaviour is to NOT exit if no wmbus +device is found. It stays running all the time even if no +wmbus device is detected. As soon as a device is inserted +it will detect/configure and start using the device. + +When reading stdin and files, then wmbusmeters will exit +as soon as there is no more data. + +This means that the udev rules no longer are needed and must be +removed. This will be done automatically by the install script. + +** Use stderr for logging, stdout for data. + +wmbusmeters now uses STDERR as default for info/verbose/debug/trace output. +This will not affect you if you run wmbusmeters as a daemon, +but if you start wmbusmeters yourself you should check where stderr is going. +To use the old style add --usestdoutforlogging. + +** New features + To list the shell envs for a meter do --listenvs=multical21 To list the fields avilable for a meter do --listfields=multical21 +To list all meters do --listmeters +To search for a meter do --listmeters=water or --listmeters=multi Version 0.9.36: 2020-09-08 diff --git a/README.md b/README.md index ee20de3..887de41 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,9 @@ Usage: wmbusmeters {options} {:suffix} ( [meter_name] [meter_type]{: you can use: --addconversions=+ add conversion to these units to json and meter env variables (GJ) + --alarmexpectedactivity=mon-fri(08-17) Specify when the timeout is tested, default is mon-sun(00-23) + --alarmshell= invokes cmdline when an alarm triggers + --alarmtimeout=