summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-10-10 21:25:01 +0200
committerAnton Luka Šijanec <anton@sijanec.eu>2022-10-10 21:25:01 +0200
commitd3d88101dc42ea5bd71ba2bf724db7e416a4f9a2 (patch)
tree993f617bda36a28b3e3b9de3502164be33cd06ec
parentprvi commit (diff)
downloadštevec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar.gz
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar.bz2
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar.lz
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar.xz
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.tar.zst
števec-d3d88101dc42ea5bd71ba2bf724db7e416a4f9a2.zip
-rw-r--r--.gitignore5
-rw-r--r--hw/platformio.ini (renamed from platformio.ini)0
-rw-r--r--hw/src/main.cpp (renamed from src/main.cpp)0
-rw-r--r--srv/c.c114
-rw-r--r--srv/d.c162
-rw-r--r--števec.service13
6 files changed, 294 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 03f4a3c..6fbe107 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,6 @@
+a.out
.pio
+db
+*.tsv
+c
+d
diff --git a/platformio.ini b/hw/platformio.ini
index 5503719..5503719 100644
--- a/platformio.ini
+++ b/hw/platformio.ini
diff --git a/src/main.cpp b/hw/src/main.cpp
index 5b0d7f9..5b0d7f9 100644
--- a/src/main.cpp
+++ b/hw/src/main.cpp
diff --git a/srv/c.c b/srv/c.c
new file mode 100644
index 0000000..eadda7c
--- /dev/null
+++ b/srv/c.c
@@ -0,0 +1,114 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#define S0(x) (x ? x : "")
+struct entry {
+ uint64_t time __attribute__((packed));
+ uint64_t value __attribute__((packed));
+} __attribute__((packed));
+int najdi (struct entry * db, int first, int last, long long int čas) {
+ if (first == last)
+ return first;
+ if (abs(first-last) == 1) {
+ if (labs(čas/1000-first*1000) < labs(čas/1000-last*1000))
+ return first;
+ else
+ return last;
+ }
+ if ((long long int) be64toh(db[(first+last)/2].time/1000) > čas*1000)
+ return najdi(db, (first+last)/2, last, čas);
+ else
+ return najdi(db, first, (first+last)/2-1, čas);
+}
+int main (int argc, char ** argv) {
+ int r = 0;
+ if (argc < 1+2)
+ error_at_line(1, 0, __FILE__, __LINE__, "uporaba: %s db informacije/preveri/rast/seštevek [natančnost=0] [začetek UNIXsek] [konec UNIXsek]\n\t"
+"informacije: pove UNIXus prvega in UNIXus zadnjega zapisa\n\t"
+"preveri: pove, če čas ni naraščajoč in kdaj se je števec resetiral\n\t"
+"rast: TSV s podatki <UNIXms>:<Wh skupno> najgosteje na [natančnost=0] ms\n\t"
+"seštevek: TSV s podatki <UNIXms>:<vatnih ur v [natančnost=10e3] ms>", S0(argv[0]));
+ int fd = -1;
+ struct entry * db = NULL;
+ if ((fd = open(argv[1], O_RDONLY | O_CLOEXEC)) == -1)
+ error_at_line(2, 0, __FILE__, __LINE__, "open");
+ struct stat statbuf;
+ if (fstat(fd, &statbuf) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "fstat");
+ r = 3;
+ goto r;
+ }
+ unsigned entries = statbuf.st_size / sizeof(struct entry);
+ if (!(db = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0))) {
+ error_at_line(0, errno, __FILE__, __LINE__, "mmap");
+ r = 4;
+ goto r;
+ }
+ if (argv[2][0] == 'i') {
+ printf("začetek\t%" PRIu64 " UNIXus\t%" PRIu64 " Wh\nkonec\t%" PRIu64 " UNIXus\t%" PRIu64 " Wh\nzapisov: %u\n", be64toh(db[0].time), be64toh(db[0].value), be64toh(db[entries-1].time), be64toh(db[entries-1].value), entries);
+ goto r;
+ }
+ if (argv[2][0] == 'p') {
+ uint64_t prev_time = 0;
+ uint64_t prev_value = 0;
+ for (unsigned i = 0; i < entries; i++) {
+ if (be64toh(db[0].time) < prev_time)
+ printf("čas se je zavrtel nazaj: i=%u a=%" PRIu64 " b=%" PRIu64 "\n", i, prev_time, be64toh(db[0].time));
+ if (be64toh(db[0].value) < prev_value)
+ printf("števec se je resetiral: i=%u a=%" PRIu64 " b=%" PRIu64 "\n", i, prev_value, be64toh(db[0].value));
+ prev_time = be64toh(db[0].time);
+ prev_value = be64toh(db[0].value);
+ }
+ goto r;
+ }
+ unsigned long long natančnost = 0;
+ if (argv[2][0] == 's')
+ natančnost = 10e3;
+ if (argc >= 1+3)
+ natančnost = atoi(argv[3]);
+ unsigned začetek = 0;
+ if (argc >= 1+4)
+ začetek = najdi(db, 0, entries-1, atoi(argv[4]));
+ unsigned konec = entries-1;
+ if (argc >= 1+5)
+ konec = najdi(db, 0, entries-1, atoi(argv[5]));
+ if (argv[2][0] == 's') {
+ uint64_t us = 0;
+ uint64_t Wh = 0;
+ unsigned long long prev_time = be64toh(db[začetek].time);
+ unsigned long long prev_value = be64toh(db[začetek].value);
+ for (unsigned i = začetek; i <= konec; i++) {
+ if (us + (be64toh(db[i].time)-prev_time) >= natančnost*1000) {
+ printf("%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\n", be64toh(db[i].time)-us, Wh, be64toh(db[i].time), us);
+ us = 0;
+ Wh = 0;
+ }
+ us += (be64toh(db[i].time)-prev_time);
+ Wh += (be64toh(db[i].value)-prev_value);
+ prev_time = be64toh(db[i].time);
+ prev_value = be64toh(db[i].value);
+ }
+ } else {
+ unsigned long long prev_print = 0;
+ for (unsigned i = začetek; i <= konec; i++) {
+ if (be64toh(db[i].time)-prev_print > natančnost*1000) {
+ printf("%" PRIu64 "\t%" PRIu64 "\n", be64toh(db[i].time), be64toh(db[i].value));
+ prev_print = be64toh(db[i].time);
+ }
+ }
+ }
+r:
+ if (db != NULL)
+ if (munmap(db, statbuf.st_size) == -1)
+ error_at_line(98, errno, __FILE__, __LINE__, "munmap");
+ if (fd == -1)
+ if (close(fd) == -1)
+ error_at_line(99, errno, __FILE__, __LINE__, "fclose");
+ return r;
+}
diff --git a/srv/d.c b/srv/d.c
new file mode 100644
index 0000000..3c9e482
--- /dev/null
+++ b/srv/d.c
@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <error.h>
+#include <endian.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <netdb.h>
+#define S0(x) (x ? x : "")
+struct entry {
+ uint64_t time __attribute__((packed));
+ uint64_t value __attribute__((packed));
+} __attribute__((packed));
+int samomor = 0;
+void handle_me (int s __attribute__((unused))) {
+ samomor++;
+}
+int main (int argc, char ** argv) {
+ if (argc < 1+1)
+ error_at_line(1, 0, __FILE__, __LINE__, "uporaba: %s db [port=35358] [bind=::] [src=]", S0(argv[0]));
+ struct sigaction act = {
+ .sa_handler = handle_me,
+ .sa_flags = SA_RESTART
+ };
+ if (sigaction(SIGINT, &act, NULL) == -1)
+ error_at_line(2, errno, __FILE__, __LINE__, "sigaction SIGINT");
+ if (sigaction(SIGTERM, &act, NULL) == -1)
+ error_at_line(3, errno, __FILE__, __LINE__, "sigaction SIGTERM");
+#define DB argv[1]
+ int port = 35358;
+ if (argc >= 1+2)
+ port = atoi(argv[2]);
+ struct sockaddr_in6 bind_address = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(port),
+ .sin6_addr = in6addr_any
+ };
+ if (argc >= 1+3)
+ switch (inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)) {
+ case 0:
+ error_at_line(4, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[3], &bind_address.sin6_addr)");
+ break;
+ case -1:
+ error_at_line(5, errno, __FILE__, __LINE__, "inet_pton");
+ }
+ struct sockaddr_in6 src = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(port),
+ .sin6_addr = in6addr_any
+ };
+ int whitelist_src = 0;
+ if (argc >= 1+4) {
+ whitelist_src++;
+ switch (inet_pton(AF_INET6, argv[4], &src.sin6_addr)) {
+ case 0:
+ error_at_line(6, 0, __FILE__, __LINE__, "!inet_pton(AF_INET6, argv[4], &src.sin6_addr)");
+ break;
+ case -1:
+ error_at_line(7, errno, __FILE__, __LINE__, "inet_pton");
+ }
+ }
+ int udp = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (udp == -1)
+ error_at_line(10, errno, __FILE__, __LINE__, "socket");
+ FILE * db = NULL;
+ int z = 1;
+ int r = 0;
+ if (setsockopt(udp, SOL_SOCKET, SO_BROADCAST, &z, sizeof z) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "setsockopt");
+ r = 11;
+ goto r;
+ }
+ if (bind(udp, (struct sockaddr *) &bind_address, sizeof bind_address)) {
+ error_at_line(0, errno, __FILE__, __LINE__, "bind");
+ r = 12;
+ goto r;
+ }
+ if (!(db = fopen(DB, "a"))) {
+ error_at_line(0, errno, __FILE__, __LINE__, "fopen");
+ r = 13;
+ goto r;
+ }
+ while (!samomor) {
+ struct pollfd pollfd = {
+ .fd = udp,
+ .events = POLLIN | POLLERR | POLLHUP | POLLNVAL
+ };
+ if (poll(&pollfd, 1, -1) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "poll");
+ r = 14;
+ goto r;
+ }
+ if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) || (pollfd.revents & POLLNVAL)) {
+ error_at_line(0, 0, __FILE__, __LINE__, "POLLERR || POLLHUP || POLLNVAL");
+ r = 15;
+ goto r;
+ }
+ struct sockaddr_storage sender;
+ socklen_t sender_len = sizeof sender;
+ char buf[512];
+ ssize_t bytes = recvfrom(udp, buf, 256, MSG_DONTWAIT, (struct sockaddr *) &sender, &sender_len);
+ if (bytes == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "recvfrom");
+ r = 16;
+ goto r;
+ }
+ struct timespec current_time;
+ if (clock_gettime(CLOCK_REALTIME, &current_time) == -1) {
+ error_at_line(0, errno, __FILE__, __LINE__, "clock_gettime");
+ r = 17;
+ goto r;
+ }
+ uint64_t current_time_us = current_time.tv_sec*1000000+current_time.tv_nsec/1000;
+ unsigned long long int current_Wh = strtoull(buf, NULL, 10);
+ struct entry entry = {
+ .time = htobe64(current_time_us),
+ .value = htobe64(current_Wh)
+ };
+ char sender_host[512];
+ char sender_port[512];
+ int gni = getnameinfo((struct sockaddr *) &sender, sender_len, sender_host, 512, sender_port, 512, NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
+ if (gni) {
+ error_at_line(0, 0, __FILE__, __LINE__, "getnameinfo: %s", gai_strerror(gni));
+ r = 19;
+ goto r;
+ }
+ time_t sedaj = time(NULL);
+ strcpy(buf, ctime(&sedaj));
+ buf[strlen(buf)-1] = '\0';
+ if (sender.ss_family != AF_INET6)
+ if (whitelist_src) {
+ fprintf(stderr, "[%s] %s:%s\tsender.ss_family != AF_INET6\n", buf, sender_host, sender_port);
+ continue;
+ }
+ if (whitelist_src && memcmp(&((struct sockaddr_in6 *) &sender)->sin6_addr, &src.sin6_addr, sizeof(src.sin6_addr))) {
+ fprintf(stderr, "[%s] %s:%s\tfailed src whitelist\n", buf, sender_host, sender_port);
+ continue;
+ }
+ fprintf(stderr, "[%s] %s:%s\t%llu Wh\n", buf, sender_host, sender_port, current_Wh);
+ if (fwrite(&entry, sizeof entry, 1, db) != 1) {
+ error_at_line(0, 0, __FILE__, __LINE__, "fwrite");
+ r = 18;
+ goto r;
+ }
+ }
+r:
+ if (close(udp) == -1)
+ error_at_line(19, errno, __FILE__, __LINE__, "close(udp)");
+ if (db != NULL)
+ if (fclose(db) == -1)
+ error_at_line(20, errno, __FILE__, __LINE__, "fclose(db)");
+ return r;
+}
diff --git a/števec.service b/števec.service
new file mode 100644
index 0000000..795db96
--- /dev/null
+++ b/števec.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=listens to reports of 1000 imp/kWh power meter counter via UDP
+After=network.target
+
+[Service]
+Type=simple
+DynamicUser=yes
+RuntimeDirectory=števec
+ExecStart=/usr/local/bin/števec
+Restart=no
+
+[Install]
+WantedBy=multi-user.target