/** * (c) Jochen Dolze, 2019 * * as1440.c 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. * * as1440.c 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 as1440.c. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include int tty_fd; bool verbose; bool nounit; bool trim0; bool debug; int hangup(int); int msleep(long msec) { struct timespec ts; int res; if (msec < 0) { errno = EINVAL; return -1; } ts.tv_sec = msec / 1000; ts.tv_nsec = (msec % 1000) * 1000000; do { res = nanosleep(&ts, &ts); } while (res && errno == EINTR); return res; } int init_tty(int fd, struct termios *tio) { if (verbose) fprintf(stderr, "init tty\n"); memset(tio, 0, sizeof(struct termios)); tio->c_cflag = CS7 | CREAD | CLOCAL | PARENB; tio->c_cc[VTIME] = 40; // 4 seconds; if (cfsetospeed(tio, B300) == -1) { perror("cfsetospeed"); return -2; } if (cfsetispeed(tio, B300) == -1) { perror("cfsetispeed"); return -2; } if (tcsetattr(fd, TCSANOW, tio) == -1) { perror("tcsetattr"); return -2; } if (tcflush(fd, TCIOFLUSH) == -1) { perror("tcflush"); return -2; } if (verbose) fprintf(stderr, "init tty ok\n"); return 0; } int init(int fd, char *maxbaud) { if (verbose) fprintf(stderr, "init\n"); if (write(fd, "/?!\r\n", 5) == -1) { perror("write"); return -2; } char rbuf[25]; int l = 0; unsigned char c = 'X'; while (c != 10) { if (read(fd, &c, 1) <= 0) { fprintf(stderr, "timeout waiting for init response\n"); return -1; } if ((c != 10) && (c != 13)) rbuf[l++] = c; if (l > 21) { rbuf[l] = 0; fprintf(stderr, "too many chars (%i>21) in init response: '%s'\n", l, rbuf); return -1; } } rbuf[l] = 0; if (l != 21) { fprintf(stderr, "response hasn't 21 characters: '%s'\n", rbuf); return -1; } if (strncmp(rbuf, "/ELS", 4)) { fprintf(stderr, "response doesn't start with /ELS: '%s'", rbuf); return -1; } if ((rbuf[5] != '\\') || (rbuf[6] != '@')) { fprintf(stderr, "response doesn't contain \\@: '%s'", rbuf); return -1; } *maxbaud = rbuf[4]; msleep(800); // mandatory! if (verbose) fprintf(stderr, "init ok\n"); return 0; } int changebaud(int fd, struct termios *tio, char baudrate) { int newbaud = (1 << (baudrate - 48)) * 300; if (verbose) fprintf(stderr, "change baudrate to B%i\n", newbaud); if (write(fd, "\x06", 1) == -1) { perror("write"); return -2; } if (write(fd, "0", 1) == -1) { perror("write"); return -2; } if (write(fd, &baudrate, 1) == -1) { perror("write"); return -2; } if (write(fd, "1\r\n", 3) == -1) { perror("write"); return -2; } msleep(400); // mandatory! int reto, reti; switch (baudrate) { case '0': break; case '1': reto = cfsetospeed(tio, B600); reti = cfsetispeed(tio, B600); break; case '2': reto = cfsetospeed(tio, B1200); reti = cfsetispeed(tio, B1200); break; case '3': reto = cfsetospeed(tio, B2400); reti = cfsetispeed(tio, B2400); break; case '4': reto = cfsetospeed(tio, B4800); reti = cfsetispeed(tio, B4800); break; case '5': reto = cfsetospeed(tio, B9600); reti = cfsetispeed(tio, B9600); break; case '6': reto = cfsetospeed(tio, B19200); reti = cfsetispeed(tio, B19200); break; default: fprintf(stderr, "unknown baudrate '%c' (%i)\n", baudrate, newbaud); return -1; } if (baudrate != '0') { if (reto == -1) { perror("cfsetospeed"); return -2; } if (reti == -1) { perror("cfsetispeed"); return -2; } if (tcsetattr(fd, TCSANOW, tio) == -1) { perror("tcsetattr"); return -2; } if (tcflush(fd, TCIOFLUSH) == -1) { perror("tcflush"); return -2; } } if (verbose) fprintf(stderr, "change baudrate to B%i ok\n", newbaud); return 0; } int readanswer(int fd, bool dontprint) { unsigned char c = 'x'; char rbuf[50]; int l = 0; while (c != 3) { if (read(fd, &c, 1) <= 0) { fprintf(stderr, "timeout waiting for answer\n"); return -2; } if (c > 13) rbuf[l++] = c; if (l > 48) { rbuf[l] = 0; fprintf(stderr, "answer too long (%i): '%s'\n", l, rbuf); return -1; } } rbuf[l] = 0; read(fd, &c, 1); // read BCC if (dontprint) return 0; if ((rbuf[0] < 48) || (rbuf[0] > 57)) { fprintf(stderr, "answer with wrong format: '%s'\n", rbuf); return -1; } if (debug) fprintf(stderr,"received '%s'\n",rbuf); int i; bool inval = false; bool dotrim = trim0; char *p = rbuf; while (*p) { if ((inval) && (dotrim)) { if ((*p == '0') && (*(p + 1)) != '.') { memmove(p, p + 1, strlen(p + 1)); continue; } else { dotrim = false; } } if (*p == '(') { *p = 9; inval = true; } if (*p == ')') { *p = 0; } if (*p == '*') { if (inval) { if (nounit) { *p = 0; } else { *p = 32; } } else { *p = '_'; } } p++; } char *s = strchr(rbuf, ':'); if (s) { fprintf(stdout, "%s\n", ++s); } else { fprintf(stdout, "%snotfound\n", rbuf); } fflush(stdout); return 0; } char calcBCC(unsigned char *buf) { int i; char BCC = buf[0]; for (i = 1; i < strlen(buf); i++) { BCC = BCC ^ buf[i]; if (buf[i] == 3) break; } return BCC; } int sendpwd(int fd, char *pwd) { if (verbose) fprintf(stderr, "sendpwd\n"); if (strlen(pwd) != 8) { fprintf(stderr, "password not 8 characters: '%s'\n", pwd); hangup(fd); return -1; } char buf[20]; sprintf(buf, "%cP1%c(%s)%c ", 1, 2, pwd, 3); int len = strlen(buf); buf[len - 1] = calcBCC(&buf[1]); if (write(fd, buf, len) == -1) { perror("write"); return -2; } if (verbose) fprintf(stderr, "sendpwd ok\n"); return 0; } int getr5(int fd, char *value) { char buf[20]; sprintf(buf, "%cR5%c%s()%c ", 1, 2, value, 3); int len = strlen(buf); buf[len - 1] = calcBCC(&buf[1]); /* int i; for (i = 0; i < len; i++) { if (buf[i] > 10) { fprintf(stderr, "%c", buf[i]); } else { fprintf(stderr, " %x ", buf[i]); } } fprintf(stderr, "\n"); */ if (write(fd, buf, len) == -1) { perror("write"); return -2; } return 0; } int hangup(int fd) { if (verbose) fprintf(stderr, "hangup\n"); msleep(200); char buf[] = { 1, 'B', '0', 3, 'q' }; if (write(fd, buf, strlen(buf)) == -1) { perror("write"); return -2; } if (verbose) fprintf(stderr, "hangup ok\n"); return 0; } int readack(int fd) { if (verbose) fprintf(stderr, "readack\n"); unsigned char c; if (read(fd, &c, 1) == -1) { perror("read"); return -2; } if (c == 1) { fprintf(stderr, "got hangup instead of ACK\n"); return -1; } if (c == 21) { fprintf(stderr, "got NAK instead of ACK\n"); return -1; } if (c != 6) { fprintf(stderr, "got %i instead of ACK\n", c); return -1; } if (verbose) fprintf(stderr, "readack ok\n"); return 0; } void cleanup(void) { if (tty_fd != -1) { hangup(tty_fd); close(tty_fd); tty_fd = -1; } } void ctrlc(int sig) { exit(EXIT_SUCCESS); } void usage(char *name) { fprintf(stderr, "usage: %s -F device -O obisval1[:obisval2...] [-p password ] [-v] [-n] [-e] [-d]\n" " -v\tverbose\n" " -d\tinclude debug output\n" " -t\tleft trim zeros\n" " -n\tdon't add units\n" " -e\tendless loop (end with ctrl-c)\n\n", name); } int main(int argc, char **argv) { if (argc == 1) { usage(argv[0]); exit(EXIT_FAILURE); } int opt; char *dev = NULL; char *pwd = "00000000"; char *obis = NULL; bool endless = false; debug = false; verbose = false; nounit = false; trim0 = false; while ((opt = getopt(argc, argv, "F:O:p:vtned")) != -1) { switch (opt) { case 'F': dev = optarg; break; case 'O': obis = optarg; break; case 'v': verbose = true; break; case 'p': pwd = optarg; break; case 't': trim0 = true; case 'n': nounit = true; break; case 'e': endless = true; break; case 'd': debug = true; break; default: usage(argv[0]); exit(EXIT_FAILURE); } } if (!obis) { usage(argv[0]); exit(EXIT_FAILURE); } if (!dev) { usage(argv[0]); exit(EXIT_FAILURE); } if (strlen(pwd) != 8) { fprintf(stderr, "password must contain 8 characters (%lu): '%s'\n", strlen(pwd), pwd); exit(EXIT_FAILURE); } tty_fd = -1; atexit(cleanup); signal(SIGINT, ctrlc); signal(SIGPIPE, SIG_IGN); bool first = true; while (1) { if (!first) { if (verbose) fprintf(stderr, "waiting 30 seconds\n"); sleep(30); } first = false; if (tty_fd == -1) { if (verbose) fprintf(stderr, "opening %s\n", dev); tty_fd = open(dev, O_RDWR); if (tty_fd == -1) { if (verbose) perror(dev); if (endless) continue; return -1; } } struct termios tio; int ret = init_tty(tty_fd, &tio); if (ret < 0) { if (ret == -2) cleanup(); continue; } unsigned char maxbaud; ret = init(tty_fd, &maxbaud); if (ret < 0) { if (ret == -2) cleanup(); continue; } ret = changebaud(tty_fd, &tio, maxbaud); if (ret < 0) { if (ret == -2) cleanup(); continue; } ret = readanswer(tty_fd, true); if (ret < 0) { if (ret == -2) cleanup(); continue; } ret = sendpwd(tty_fd, pwd); if (ret < 0) { if (ret == -2) cleanup(); continue; } ret = readack(tty_fd); if (ret < 0) { if (ret == -2) cleanup(); continue; } bool noerror = true; int tc = 0; do { char *cobis = strdup(obis); char *ptr = strtok(cobis, ":"); while (ptr) { ret = getr5(tty_fd, ptr); if (ret < 0) { if (ret == -2) cleanup(); noerror = false; ptr = NULL; continue; } ret = readanswer(tty_fd, false); if (ret == 0) tc = 0; if (ret == -1) { noerror = false; ptr = NULL; continue; } if (ret == -2) { tc = tc + 1; if (tc > 8) { noerror = false; ptr = NULL; continue; } } ptr = strtok(NULL, ":"); } free(cobis); } while (noerror && endless); if (!endless) break; } exit(EXIT_SUCCESS); }