]> vcs.slashdirt.org Git - sw/tic2json.git/commitdiff

vcs.slashdirt.org Git - sw/tic2json.git/commitdiff

git git / sw / tic2json.git / commitdiff
? search:
summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: b6ed330)
Move source to `src`
Sun, 3 Oct 2021 11:46:11 +0000 (13:46 +0200)
Sun, 3 Oct 2021 11:46:11 +0000 (13:46 +0200)
Avoid infinite recursion in `embedded`

22 files changed:
Makefile [deleted file] patch | blob | history
src/Makefile [new file with mode: 0644] patch | blob
src/tic.c [new file with mode: 0644] patch | blob
src/tic.h [new file with mode: 0644] patch | blob
src/tic2json.c [new file with mode: 0644] patch | blob
src/tic2json.h [new file with mode: 0644] patch | blob
src/ticv01.l [new file with mode: 0644] patch | blob
src/ticv01.y [new file with mode: 0644] patch | blob
src/ticv02.l [new file with mode: 0644] patch | blob
src/ticv02.y [new file with mode: 0644] patch | blob
tic.c [deleted file] patch | blob | history
tic.h [deleted file] patch | blob | history
tic2json.c [deleted file] patch | blob | history
tic2json.h [deleted file] patch | blob | history
ticv01.l [deleted file] patch | blob | history
ticv01.y [deleted file] patch | blob | history
ticv02.l [deleted file] patch | blob | history
ticv02.y [deleted file] patch | blob | history

diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index d7dcc98..0000000
--- a/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-MAIN := tic2json
-CFLAGS := -Wall -Os
-TICVERSIONS := 01 02
-
-# don't touch below this line
-
-TICS := $(addprefix ticv,$(TICVERSIONS))
-TICSDEFS := $(addprefix -DTICV,$(TICVERSIONS))
-LEXSRCS := $(addsuffix .lex.c,$(TICS))
-
-CFLAGS += $(TICSDEFS) -DBINNAME='"$(MAIN)"'
-
-all: $(MAIN)
-
-%.lex.c: %.l %.tab.h
-# The ideal size for the flex buffer is the length of the longest token expected, in bytes, plus a little more.
- flex -DYY_BUF_SIZE=128 -P$*yy -o$@ $<
-
-%.tab.h %.tab.c: %.y
- bison -Wno-other -p $*yy -d $<
-
-tic2json.o: $(addsuffix .tab.h,$(TICS))
-
-$(MAIN): $(addsuffix .tab.o,$(TICS)) $(addsuffix .lex.o,$(TICS)) tic.o tic2json.o
-
-csources: $(LEXSRCS)
-
-clean:
- $(RM) $(MAIN) *.output *.tab.h *.tab.c *.lex.c *.o
diff --git a/README.md b/README.md
index d929d55a517436bbf2f8e15d44aa001ae7c23e2d..b2d213f5d076f260fb15e8d7145fe5841e55f07d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ See LICENSE.md for details
## Building
-To build, run `make`
+To build, run `cd src; make`
**Note:** the build can be adjusted through the top Makefile variables.
In particular, it is possible to build support for only specific version(s) of the TIC.
diff --git a/embedded/ARM-Mbed/mbed-os-tic2json/tic2json/src b/embedded/ARM-Mbed/mbed-os-tic2json/tic2json/src
index 210be6a9380e2b790c6e44db388f9066939fd54f..b3e266f1221a8e4777caadffe7c4f64c92c3a93b 120000 (symlink)
--- a/embedded/ARM-Mbed/mbed-os-tic2json/tic2json/src
+++ b/embedded/ARM-Mbed/mbed-os-tic2json/tic2json/src
@@ -1 +1 @@
-/Users/varenet/NOBACKUP/tic2json
\ No newline at end of file
+../../../../src
\ No newline at end of file
diff --git a/embedded/ESP-RTOS/esptic/components/tic2json/src b/embedded/ESP-RTOS/esptic/components/tic2json/src
index 3efed95be5dbe948757e3e04ed2ccf1ee906ab8d..d753b57a1546bce9ea2e37ea4e8eeef57a5c19a8 120000 (symlink)
--- a/embedded/ESP-RTOS/esptic/components/tic2json/src
+++ b/embedded/ESP-RTOS/esptic/components/tic2json/src
@@ -1 +1 @@
-../../../../../
\ No newline at end of file
+../../../../../src
\ No newline at end of file
diff --git a/embedded/Pico/picotic/tic2json/src b/embedded/Pico/picotic/tic2json/src
index 11a54ed360106c9f6a2dbbdab67c7c5bd84ae617..b3e266f1221a8e4777caadffe7c4f64c92c3a93b 120000 (symlink)
--- a/embedded/Pico/picotic/tic2json/src
+++ b/embedded/Pico/picotic/tic2json/src
@@ -1 +1 @@
-../../../../
\ No newline at end of file
+../../../../src
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..d7dcc98
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,29 @@
+MAIN := tic2json
+CFLAGS := -Wall -Os
+TICVERSIONS := 01 02
+
+# don't touch below this line
+
+TICS := $(addprefix ticv,$(TICVERSIONS))
+TICSDEFS := $(addprefix -DTICV,$(TICVERSIONS))
+LEXSRCS := $(addsuffix .lex.c,$(TICS))
+
+CFLAGS += $(TICSDEFS) -DBINNAME='"$(MAIN)"'
+
+all: $(MAIN)
+
+%.lex.c: %.l %.tab.h
+# The ideal size for the flex buffer is the length of the longest token expected, in bytes, plus a little more.
+ flex -DYY_BUF_SIZE=128 -P$*yy -o$@ $<
+
+%.tab.h %.tab.c: %.y
+ bison -Wno-other -p $*yy -d $<
+
+tic2json.o: $(addsuffix .tab.h,$(TICS))
+
+$(MAIN): $(addsuffix .tab.o,$(TICS)) $(addsuffix .lex.o,$(TICS)) tic.o tic2json.o
+
+csources: $(LEXSRCS)
+
+clean:
+ $(RM) $(MAIN) *.output *.tab.h *.tab.c *.lex.c *.o
diff --git a/src/tic.c b/src/tic.c
new file mode 100644 (file)
index 0000000..016ec00
--- /dev/null
+++ b/src/tic.c
@@ -0,0 +1,56 @@
+//
+// tic.c
+// Common routines for TIC parsers
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * Common routines used by the TIC parsers.
+ * The code making use of the parsers must provide extra functions as mentioned in the header.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tic.h"
+
+bool filter_mode; ///< if true, switch lexers to configuration parsing
+bool *etiq_en; ///< when non-NULL, a token-indexed array, where the related token is emitted if the value is true. @note This could be made a bit field if memory is a concern
+
+void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data)
+{
+ // args come from the bison stack
+ int base;
+
+ field->horodate = horodate;
+ memcpy(&field->etiq, etiq, sizeof(field->etiq));
+
+ switch ((etiq->unittype & 0xF0)) {
+ case T_STRING:
+ field->data.s = data;
+ return;
+ case T_HEX:
+ base = 16;
+ break;
+ default:
+ base = 10;
+ break;
+ }
+ field->data.i = (int)strtol(data, NULL, base);
+ free(data);
+}
+
+void free_field(struct tic_field *field)
+{
+ free(field->horodate);
+ switch ((field->etiq.unittype & 0xF0)) {
+ case T_STRING:
+ free(field->data.s);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/tic.h b/src/tic.h
new file mode 100644 (file)
index 0000000..f01670a
--- /dev/null
+++ b/src/tic.h
@@ -0,0 +1,100 @@
+//
+// tic.h
+// Interface for TIC parsers
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * Interface for TIC parsers
+ * Users of this interface must implement the functions declared at the bottom:
+ * - print_field()
+ * - frame_sep()
+ * - frame_err()
+ */
+
+#ifndef tic_h
+#define tic_h
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#ifdef BAREBUILD
+ #define pr_err(format, ...) /* nothing */
+#else
+ #define pr_err(format, ...) fprintf(stderr, "ERREUR: " format, ## __VA_ARGS__)
+#endif
+
+/**
+ * TIC units.
+ * @warning The code assumes this fits on 4 bits
+ */
+enum tic_unit {
+ U_SANS = 0x00,
+ U_WH,
+ U_VARH,
+ U_A,
+ U_V,
+ U_KVA,
+ U_VA,
+ U_W,
+ U_MIN,
+ U_DAL,
+};
+
+/**
+ * TIC data types.
+ * By default everything is an int.
+ * @warning The code assumes this is packed in the upper 4 bits of a byte: must increment by 0x10.
+ */
+ enum data_type {
+ T_STRING = 0x10,
+ T_HEX = 0x20,
+};
+
+/** Internal parser representation of a TIC etiquette */
+struct tic_etiquette {
+ uint8_t tok; ///< bison token number
+ uint8_t unittype; ///< combined unit and type (see @tic_unit @data_type)
+ const char *label; ///< TIC "etiquette", as an ASCII string
+ const char *desc; ///< corresponding TIC long description
+};
+
+/** Internal parser representation of a TIC field (i.e. body of a dataset) */
+struct tic_field {
+ struct tic_etiquette etiq; ///< the field "etiquette"
+ union {
+ char *s;
+ int i;
+ } data; ///< the field data, if any
+ char *horodate; ///< the field horodate, if any
+};
+
+void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data);
+void free_field(struct tic_field *field);
+
+// The following functions must be provided by the output interface
+
+/**
+ * Called for each valid dataset.
+ * Used to print a TIC dataset in the desired output format.
+ * @param field the dataset to print
+ */
+void print_field(const struct tic_field *field);
+
+/**
+ * Called after each frame, valid or not.
+ * Used to print a frame separator in the desired output format.
+ */
+void frame_sep(void);
+
+/**
+ * Called whenever a frame error condition occurs.
+ * When frames or datasets have errors.
+ */
+void frame_err(void);
+
+#endif /* tic_h */
diff --git a/src/tic2json.c b/src/tic2json.c
new file mode 100644 (file)
index 0000000..a540b96
--- /dev/null
+++ b/src/tic2json.c
@@ -0,0 +1,487 @@
+//
+// tic2json.c
+// A tool to turn ENEDIS TIC data into pure JSON
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * Outputs as JSON a series of frames formatted as a list of fields or a dictionary.
+ * - for list mode, fields are { "label": "xxx", "data": "xxx", horodate: "xxx", "desc": "xxx", "unit": "xxx" }
+ * - for dict mode, the keys are the label, followed by { "data": "xxx", "horodate": "xxx", "desc": "xxx", "unit": "xxx" }
+ * with horodate optional, unit and data optional and possibly empty and data being either quoted string or number.
+ *
+ * Data errors can result in some/all datasets being omitted in the output frame (e.g. invalid datasets or datasets
+ * that did not pass checksum are not emitted): the JSON root object can then be empty but is still emitted.
+ * In dictionary mode the parser will report the frame status as "_tvalide" ("trame valide") followed by either 1
+ * for a valid frame or 0 for a frame containing errors (including dataset errors).
+ *
+ * Output JSON is guaranteed to always be valid for each frame. By default only frames are separated with newlines.
+ *
+ * @note: the program can only parse a single version of the TIC within one execution context.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef BAREBUILD
+ #include <unistd.h> // getopt
+ #include <err.h>
+#endif
+
+#include "tic.h"
+#ifdef TICV01
+ #include "ticv01.tab.h"
+ int ticv01yylex_destroy();
+#endif
+#ifdef TICV02
+ #include "ticv02.tab.h"
+ int ticv02yylex_destroy();
+#endif
+
+#include "tic2json.h"
+
+#define ticprintf(format, ...) printf(format, ## __VA_ARGS__)
+
+#ifdef BAREBUILD
+ #warning BAREBUILD currently requires defining only one version of supported TIC and does not provide main()
+ #ifdef PRINT2BUF
+ static char * ticbuf;
+ static size_t ticbufsize, ticbufavail;
+ static tic2json_framecb_t ticframecb;
+
+ #include <stdarg.h>
+ #undef ticprintf
+ int ticprintf(const char * restrict format, ...)
+ {
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = vsnprintf(ticbuf + (ticbufsize - ticbufavail), ticbufavail, format, args);
+ va_end(args);
+
+ if (ret >= ticbufavail) {
+ fprintf(stderr, "ERROR: output buffer too small!\n");
+ return ticbufavail;
+ }
+ else if (ret < 0)
+ return ret;
+
+ ticbufavail -= ret;
+
+ return (ret);
+ }
+ #endif /* PRINT2BUF */
+#endif /* BAREBUILD */
+
+#define TIC2JSON_VER "2.1"
+
+extern bool filter_mode;
+extern bool *etiq_en;
+
+/** Global configuration details */
+static struct {
+ const char *idtag;
+ char framedelims[2];
+ char fdelim;
+ int optflags;
+ unsigned int skipframes, framecount;
+ bool ferr;
+} tp;
+
+/** TIC units representation strings */
+static const char * tic_units[] = {
+ [U_SANS] = "",
+ [U_WH] = "Wh",
+ [U_VARH] = "VArh",
+ [U_A] = "A",
+ [U_V] = "V",
+ [U_KVA] = "kVA",
+ [U_VA] = "VA",
+ [U_W] = "W",
+ [U_MIN] = "mn",
+ [U_DAL] = "daL",
+};
+
+#ifdef TICV02
+static void print_stge_data(int data)
+{
+ const char sep = (tp.optflags & TIC2JSON_OPT_CRFIELD) ? '\n' : ' ';
+ uint32_t d = (uint32_t)data;
+
+
+ const char *const of[] = {
+ "fermé",
+ "ouvert",
+ };
+
+ const char *const coupure[] = {
+ "fermé",
+ "ouvert sur surpuissance",
+ "ouvert sur surtension",
+ "ouvert sur délestage",
+ "ouvert sur ordre CPL ou Euridis",
+ "ouvert sur une surchauffe avec une valeur de courant supérieure au courant de commutation maximal",
+ "ouvert sur une surchauffe avec une valeur de courant inférieure au courant de commutation maximal",
+ NULL,
+ };
+
+ const char *const euridis[] = {
+ "désactivée",
+ "activée sans sécurité",
+ NULL,
+ "activée avec sécurité",
+ };
+
+ const char *const cpl[] = {
+ "New/Unlock",
+ "New/Lock",
+ "Registered",
+ NULL,
+ };
+
+ const char *const tempo[] = {
+ "Pas d'annonce",
+ "Bleu",
+ "Blanc",
+ "Rouge",
+ };
+
+ const char *const pm[] = {
+ "pas",
+ "PM1",
+ "PM2",
+ "PM3",
+ };
+
+ ticprintf("{ "
+ "\"Contact sec\": \"%s\",%c"
+ "\"Organe de coupure\": \"%s\",%c"
+ "\"État du cache-bornes distributeur\": \"%s\",%c"
+ "\"Surtension sur une des phases\": \"%ssurtension\",%c"
+ "\"Dépassement de la puissance de référence\": \"%s\",%c"
+ "\"Fonctionnement producteur/consommateur\": \"%s\",%c"
+ "\"Sens de l'énergie active\": \"énergie active %s\",%c"
+ "\"Tarif en cours sur le contrat fourniture\": \"énergie ventilée sur Index %d\",%c"
+ "\"Tarif en cours sur le contrat distributeur\": \"énergie ventilée sur Index %d\",%c"
+ "\"Mode dégradé de l'horloge\": \"horloge %s\",%c"
+ "\"État de la sortie télé-information\": \"mode %s\",%c"
+ "\"État de la sortie communication Euridis\": \"%s\",%c"
+ "\"Statut du CPL\": \"%s\",%c"
+ "\"Synchronisation CPL\": \"compteur%s synchronisé\",%c"
+ "\"Couleur du jour pour le contrat historique tempo\": \"%s\",%c"
+ "\"Couleur du lendemain pour le contrat historique tempo\": \"%s\",%c"
+ "\"Préavis pointes mobiles\": \"%s en cours\",%c"
+ "\"Pointe mobile\": \"%s en cours\" }%c"
+ ,
+ of[d & 0x01], sep,
+ coupure[(d>>1) & 0x07], sep,
+ of[(d>>4) & 0x01], sep,
+ (d>>6) & 0x01 ? "" : "pas de ", sep,
+ (d>>7) & 0x01 ? "dépassement en cours" : "pas de dépassement", sep,
+ (d>>8) & 0x01 ? "producteur" : "consommateur", sep,
+ (d>>9) & 0x01 ? "négative" : "positive", sep,
+ ((d>>10) & 0x0F) + 1, sep,
+ ((d>>14) & 0x07) + 1, sep,
+ (d>>16) & 0x01 ? "en mode dégradée" : "correcte", sep,
+ (d>>17) & 0x01 ? "standard" : "historique", sep,
+ euridis[(d>>19) & 0x03], sep,
+ cpl[(d>>21) & 0x03], sep,
+ (d>>23) & 0x01 ? "" : " non", sep,
+ tempo[(d>>24) & 0x03], sep,
+ tempo[(d>>26) & 0x03], sep,
+ pm[(d>>28) & 0x03], sep,
+ pm[(d>>30) & 0x03], sep
+ );
+}
+#endif /* TICV02 */
+
+void print_field(const struct tic_field *field)
+{
+ const char fdictout[] = "%c \"%.8s\": { \"data\": ";
+ const char flistout[] = "%c{ \"label\": \"%.8s\", \"data\": ";
+ const char *format;
+ uint8_t type;
+
+ // filters
+ if (tp.framecount ||
+ ((tp.optflags & TIC2JSON_OPT_MASKZEROES) && (T_STRING != (field->etiq.unittype & 0xF0)) && (0 == field->data.i)) ||
+ (etiq_en && !etiq_en[field->etiq.tok]))
+ return;
+
+ format = (tp.optflags & TIC2JSON_OPT_DICTOUT) ? fdictout : flistout;
+
+ ticprintf(format, tp.fdelim, field->etiq.label);
+ switch (field->etiq.unittype & 0x0F) {
+ case U_SANS:
+ type = field->etiq.unittype & 0xF0;
+ if (T_STRING == type) {
+ ticprintf("\"%s\"", field->data.s ? field->data.s : "");
+ break;
+ }
+#ifdef TICV02
+ else if ((T_HEX == type) && (tp.optflags & TIC2JSON_OPT_PARSESTGE)) {
+ // XXX abuse the fact that STGE is the only U_SANS|T_HEX field
+ print_stge_data(field->data.i);
+ break;
+ }
+#endif /* TICV02 */
+ // fallthrough
+ default:
+ ticprintf("%d", field->data.i);
+ break;
+ }
+
+#ifdef TICV02
+ if (field->horodate) {
+ if (tp.optflags & TIC2JSON_OPT_LONGDATE) {
+ const char *o, *d = field->horodate;
+ switch (d[0]) {
+ default:
+ case ' ':
+ o = "";
+ break;
+ case 'E':
+ case 'e':
+ o = "+02:00";
+ break;
+ case 'H':
+ case 'h':
+ o = "+01:00";
+ break;
+ }
+ ticprintf(", \"horodate\": \"20%.2s-%.2s-%.2sT%.2s:%.2s:%.2s%s\"", d+1, d+3, d+5, d+7, d+9, d+11, o);
+ }
+ else
+ ticprintf(", \"horodate\": \"%s\"", field->horodate);
+ }
+#endif /* TICV02 */
+
+ if (tp.optflags & TIC2JSON_OPT_DESCFORM)
+ ticprintf(", \"desc\": \"%s\", \"unit\": \"%s\"", field->etiq.desc, tic_units[(field->etiq.unittype & 0x0F)]);
+
+ if (tp.idtag)
+ ticprintf(", \"id\": \"%s\"", tp.idtag);
+
+ ticprintf("}%c", (tp.optflags & TIC2JSON_OPT_CRFIELD) ? '\n': ' ');
+
+ tp.fdelim = ',';
+}
+
+void frame_sep(void)
+{
+ if (!tp.framecount--) {
+ tp.framecount = tp.skipframes;
+ if (tp.optflags & TIC2JSON_OPT_DICTOUT)
+ ticprintf("%c \"_tvalide\": %d", tp.fdelim, !tp.ferr);
+#ifdef PRINT2BUF
+ ticprintf("%c\n", tp.framedelims[1]);
+ if (ticframecb)
+ ticframecb(ticbuf, ticbufsize-ticbufavail, !tp.ferr);
+ ticbufavail = ticbufsize; // reset buffer
+ ticprintf("%c", tp.framedelims[0]);
+#else
+ ticprintf("%c\n%c", tp.framedelims[1], tp.framedelims[0]);
+#endif
+ }
+ tp.fdelim = ' ';
+ tp.ferr = 0;
+}
+
+void frame_err(void)
+{
+ tp.ferr = 1;
+}
+
+static inline void ticinit(void)
+{
+ filter_mode = false;
+ etiq_en = NULL;
+
+ memset(&tp, 0, sizeof(tp));
+ tp.framedelims[0] = '['; tp.framedelims[1] = ']';
+ tp.fdelim = ' ';
+}
+
+#ifndef BAREBUILD
+static void usage(void)
+{
+ printf( "usage: " BINNAME " {-1|-2} [-dhlnruVz] [-e fichier] [-i id] [-s N]\n" // FIXME -1|-2 always shown
+#ifdef TICV01
+ " -1\t\t" "Analyse les trames TIC version 01 \"historique\"\n"
+#endif
+#ifdef TICV02
+ " -2\t\t" "Analyse les trames TIC version 02 \"standard\"\n"
+#endif
+ "\n"
+ " -d\t\t" "Émet les trames sous forme de dictionaire plutôt que de liste\n"
+ " -e fichier\t" "Utilise <fichier> pour configurer le filtre d'étiquettes\n"
+ " -h\t\t" "Montre ce message d'aide et quitte\n"
+ " -i id\t\t" "Ajoute une balise \"id\" avec la valeur <id> à chaque groupe\n"
+ " -l\t\t" "Ajoute les descriptions longues et les unitées de chaque groupe\n"
+ " -n\t\t" "Insère une nouvelle ligne après chaque groupe\n"
+ " -r\t\t" "Interprète les horodates en format RFC3339\n"
+ " -s N\t\t" "Émet une trame toutes les <N> reçues\n"
+ " -u\t\t" "Décode le registre de statut sous forme de dictionnaire\n"
+ " -V\t\t" "Montre la version et quitte\n"
+ " -z\t\t" "Masque les groupes numériques à zéro\n"
+ "\n"
+ "Note: le fichier de configuration du filtre d'étiquettes doit commencer par une ligne comportant\n"
+ "uniquement la séquence de caractères suivante: `#ticfilter` (sans les apostrophes), suivi à partir de\n"
+ "la ligne suivante d'un nombre quelconque d'étiquettes TIC séparées par du blanc (espace, nouvelle ligne, etc).\n"
+ "Seuls les groupes dont les étiquettes sont ainsi listées seront alors émis par le programme.\n"
+ );
+}
+
+#ifdef TICV01
+void parse_config_v01(const char *filename);
+#endif
+
+#ifdef TICV02
+void parse_config_v02(const char *filename);
+#endif
+
+int main(int argc, char **argv)
+{
+ void (*parse_config)(const char *);
+ int (*yyparse)(void) = NULL;
+ int (*yylex_destroy)(void) = NULL;
+
+ const char *fconfig = NULL;
+ int ch;
+
+ ticinit();
+
+ while ((ch = getopt(argc, argv, "12de:hi:lnrs:uVz")) != -1) {
+ switch (ch) {
+#ifdef TICV01
+ case '1':
+ if (yyparse)
+ errx(-1, "ERREUR: Une seule version de TIC peut être analysée à la fois");
+ parse_config = parse_config_v01;
+ yyparse = ticv01yyparse;
+ yylex_destroy = ticv01yylex_destroy;
+ break;
+#endif
+#ifdef TICV02
+ case '2':
+ if (yyparse)
+ errx(-1, "ERREUR: Une seule version de TIC peut être analysée à la fois");
+ parse_config = parse_config_v02;
+ yyparse = ticv02yyparse;
+ yylex_destroy = ticv02yylex_destroy;
+ break;
+#endif
+ case 'd':
+ tp.optflags |= TIC2JSON_OPT_DICTOUT;
+ tp.framedelims[0] = '{'; tp.framedelims[1] = '}';
+ break;
+ case 'e':
+ fconfig = optarg;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ case 'i':
+ tp.idtag = optarg;
+ break;
+ case 'l':
+ tp.optflags |= TIC2JSON_OPT_DESCFORM;
+ break;
+ case 'n':
+ tp.optflags |= TIC2JSON_OPT_CRFIELD;
+ break;
+ case 'r':
+ tp.optflags |= TIC2JSON_OPT_LONGDATE;
+ break;
+ case 's':
+ tp.skipframes = (unsigned int)strtol(optarg, NULL, 10);
+ break;
+ case 'u':
+ tp.optflags |= TIC2JSON_OPT_PARSESTGE;
+ break;
+ case 'V':
+ printf( BINNAME " version " TIC2JSON_VER "\n"
+ "License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl-2.0.html>.\n"
+ "Copyright (C) 2021 Thibaut Varène.\n");
+ return 0;
+ case 'z':
+ tp.optflags |= TIC2JSON_OPT_MASKZEROES;
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!yyparse)
+ errx(-1, "ERREUR: version TIC non spécifiée");
+
+ if (fconfig)
+ parse_config(fconfig);
+
+ putchar(tp.framedelims[0]);
+ yyparse();
+ printf("%c\n", tp.framedelims[1]);
+ yylex_destroy();
+
+ free(etiq_en);
+ return 0;
+}
+
+#else /* BAREBUILD */
+
+extern FILE *ticv01yyin;
+extern FILE *ticv02yyin;
+
+#ifdef PRINT2BUF
+/**
+ * tic2json_main(), print to buffer variant.
+ * @param yyin the FILE to read TIC frames from
+ * @param optflags bitfield for tuning parser behavior
+ * @param buf an allocated buffer to write JSON data to
+ * @param size the size of the buffer
+ * @param an optional callback to call after each printed frame, before the buffer content is overwritten.
+ */
+void tic2json_main(FILE * yyin, int optflags, char * buf, size_t size, tic2json_framecb_t cb)
+#else
+void tic2json_main(FILE * yyin, int optflags)
+#endif
+{
+ ticinit();
+ tp.optflags = optflags;
+
+ if (tp.optflags & TIC2JSON_OPT_DICTOUT) {
+ tp.framedelims[0] = '{';
+ tp.framedelims[1] = '}';
+ }
+
+#ifdef PRINT2BUF
+ ticbuf = buf;
+ ticbufavail = ticbufsize = size;
+ ticframecb = cb;
+#endif
+
+ ticprintf("%c", tp.framedelims[0]);
+
+#if defined(TICV01)
+ ticv01yyin = yyin;
+ ticv01yyparse();
+ ticv01yylex_destroy();
+#elif defined(TICV02)
+ ticv02yyin = yyin;
+ ticv02yyparse();
+ ticv02yylex_destroy();
+#else
+ fprintf(stderr, "NO TIC VERSION DEFINED!\n"); // avoid utf-8
+#endif
+
+ ticprintf("%c\n", tp.framedelims[1]);
+}
+
+#endif /* !BAREBUILD */
diff --git a/src/tic2json.h b/src/tic2json.h
new file mode 100644 (file)
index 0000000..afd70f5
--- /dev/null
+++ b/src/tic2json.h
@@ -0,0 +1,35 @@
+//
+// tic2json.h
+//
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * exports for tic2json, mainly useful for embedded applications using tic2json_main().
+ */
+
+#ifndef tic2json_h
+#define tic2json_h
+
+/** enum for optflags bitfield */
+enum {
+ TIC2JSON_OPT_MASKZEROES = 0x01,
+ TIC2JSON_OPT_CRFIELD = 0x02,
+ TIC2JSON_OPT_DESCFORM = 0x04,
+ TIC2JSON_OPT_DICTOUT = 0x08,
+ TIC2JSON_OPT_LONGDATE = 0x10,
+ TIC2JSON_OPT_PARSESTGE = 0x20,
+};
+
+/**
+ * TIC frame callback
+ * @param buf the buffer received from tic2json_main(), filled with JSON data
+ * @param size the length of JSON data currently in the buffer
+ * @param valid true if TIC frame is valid, false otherwise
+ */
+typedef void (*tic2json_framecb_t)(char * buf, size_t size, bool valid);
+
+#endif /* tic2json_h */
diff --git a/src/ticv01.l b/src/ticv01.l
new file mode 100644 (file)
index 0000000..a9d3350
--- /dev/null
+++ b/src/ticv01.l
@@ -0,0 +1,153 @@
+/*
+// ticv01.l
+// A lexer for ENEDIS TIC version 01 protocol
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+*/
+
+/**
+ * @file
+ * Complete lexer for ENEDIS' TIC protocol version 01.
+ * Supports version 01, a.k.a "historique" as found on ENEDIS' Linky and Bleus meters.
+ * Ref doc: https://www.enedis.fr/media/2027/download
+ *
+ * "le champ « étiquette » ne contient aucun caractère ayant une valeur égale à celle
+ * du caractère-séparateur utilisé pour la trame (caractère ASCII « espace » ou caractère
+ * ASCII « tabulation horizontale » suivant le cas). Par contre, le champ « donnée »
+ * contenant l’information fournie par le groupe peut, lui, contenir des caractères ayant
+ * une valeur égale à celle du caractère-séparateur utilisé pour la trame."
+ *
+ * Mais NOI-CPT_54E indique toutefois:
+ * "Note : le caractère séparateur des champs "Horizontal Tab" HT (0x09), en mode standard
+ * est different du caractère séparateur "Space" SP (0x20) en mode historique.
+ * Cette disposition permet d’utiliser le caractère "Space" pour les données."
+ *
+ * On serait donc fondé à penser qu'en mode historique, ni les étiquettes ni les données
+ * ne contiennent 0x20. Du moins en sortie Linky "historique". Cette hypothèse est donc
+ * retenue jusqu'à preuve du contraire.
+ */
+
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+%option noyywrap
+
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+%option nounput
+
+/* we never directly read input */
+%option noinput
+
+/* we don't need the default rule */
+%option nodefault
+
+/* nounistd suppresses inclusion of the non-ANSI header file unistd.h.
+ This option is meant to target environments in which unistd.h does not exist.
+ Be aware that certain options may cause flex to generate code that relies on
+ functions normally found in unistd.h, (e.g. isatty(), read().)
+ If you wish to use these functions, you will have to inform your compiler where
+ to find them. See option-always-interactive. See option-read. */
+%option nounistd
+
+/* batch means that we'll never use the generated lexer interactively. */
+%option batch
+
+/* we only process 7-bit input */
+%option 7bit
+
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+%option nodebug
+
+%s FILTER
+%x DATA
+
+DATAC [\x21-\x7e]
+CHKSUM [\x20-\x5f]
+SEP \x20
+
+%{
+ #include "tic.h"
+ #include "ticv01.tab.h"
+
+ extern bool filter_mode;
+ static uint8_t checksum;
+
+ static void crc_calc(void)
+ {
+ for (size_t i = 0; i<yyleng; i++)
+ checksum += (uint8_t)yytext[i];
+ }
+%}
+
+%%
+
+ if (filter_mode)
+ BEGIN(FILTER);
+
+ /* only for filter */
+<FILTER>"#"ticfilter\n { return TICFILTER; }
+<FILTER>[ \t\n]+ /* ignore whitespace */
+
+ /* balises */
+<*>\x02 { BEGIN(INITIAL); return TOK_STX; }
+<*>\x03 { BEGIN(INITIAL); return TOK_ETX; }
+<*>\x04 { BEGIN(INITIAL); return TOK_EOT; }
+<*>\x0a { BEGIN(INITIAL); checksum=0; return FIELD_START; }
+<*>{SEP} { checksum += (uint8_t)*yytext; BEGIN(DATA); return TOK_SEP; }
+<*>{CHKSUM}\x0d {
+ checksum -= 0x20; // we have one space too many in the checksum: last SEP
+ checksum = (checksum & 0x3f) + 0x20;
+ if (checksum == (uint8_t)yytext[0]) return FIELD_OK;
+ else return FIELD_KO;
+ }
+
+
+ /* etiquettes - mode historique */
+ADCO { crc_calc(); ticv01yylval.label = "ADCO"; return ET_ADCO; }
+OPTARIF { crc_calc(); ticv01yylval.label = "OPTARIF"; return ET_OPTARIF; }
+ISOUSC { crc_calc(); ticv01yylval.label = "ISOUSC"; return ET_ISOUSC; }
+BASE { crc_calc(); ticv01yylval.label = "BASE"; return ET_BASE; }
+HCHC { crc_calc(); ticv01yylval.label = "HCHC"; return ET_HCHC; }
+HCHP { crc_calc(); ticv01yylval.label = "HCHP"; return ET_HCHP; }
+EJPHN { crc_calc(); ticv01yylval.label = "EJPHN"; return ET_EJPHN; }
+EJPHPM { crc_calc(); ticv01yylval.label = "EJPHPM"; return ET_EJPHPM; }
+BBRHCJB { crc_calc(); ticv01yylval.label = "BBRHCJB"; return ET_BBRHCJB; }
+BBRHPJB { crc_calc(); ticv01yylval.label = "BBRHPJB"; return ET_BBRHPJB; }
+BBRHCJW { crc_calc(); ticv01yylval.label = "BBRHCJW"; return ET_BBRHCJW; }
+BBRHPJW { crc_calc(); ticv01yylval.label = "BBRHPJW"; return ET_BBRHPJW; }
+BBRHCJR { crc_calc(); ticv01yylval.label = "BBRHCJR"; return ET_BBRHCJR; }
+BBRHPJR { crc_calc(); ticv01yylval.label = "BBRHPJR"; return ET_BBRHPJR; }
+PEJP { crc_calc(); ticv01yylval.label = "PEJP"; return ET_PEJP; }
+PTEC { crc_calc(); ticv01yylval.label = "PTEC"; return ET_PTEC; }
+DEMAIN { crc_calc(); ticv01yylval.label = "DEMAIN"; return ET_DEMAIN; }
+IINST { crc_calc(); ticv01yylval.label = "IINST"; return ET_IINST; }
+IINST1 { crc_calc(); ticv01yylval.label = "IINST1"; return ET_IINST1; }
+IINST2 { crc_calc(); ticv01yylval.label = "IINST2"; return ET_IINST2; }
+IINST3 { crc_calc(); ticv01yylval.label = "IINST3"; return ET_IINST3; }
+ADPS { crc_calc(); ticv01yylval.label = "ADPS"; return ET_ADPS; }
+IMAX { crc_calc(); ticv01yylval.label = "IMAX"; return ET_IMAX; }
+IMAX1 { crc_calc(); ticv01yylval.label = "IMAX1"; return ET_IMAX1; }
+IMAX2 { crc_calc(); ticv01yylval.label = "IMAX2"; return ET_IMAX2; }
+IMAX3 { crc_calc(); ticv01yylval.label = "IMAX3"; return ET_IMAX3; }
+PMAX { crc_calc(); ticv01yylval.label = "PMAX"; return ET_PMAX; }
+PAPP { crc_calc(); ticv01yylval.label = "PAPP"; return ET_PAPP; }
+HHPHC { crc_calc(); ticv01yylval.label = "HHPHC"; return ET_HHPHC; }
+MOTDETAT { crc_calc(); ticv01yylval.label = "MOTDETAT"; return ET_MOTDETAT; }
+PPOT { crc_calc(); ticv01yylval.label = "PPOT"; return ET_PPOT; }
+ADIR1 { crc_calc(); ticv01yylval.label = "ADIR1"; return ET_ADIR1; }
+ADIR2 { crc_calc(); ticv01yylval.label = "ADIR2"; return ET_ADIR2; }
+ADIR3 { crc_calc(); ticv01yylval.label = "ADIR3"; return ET_ADIR3; }
+GAZ { crc_calc(); ticv01yylval.label = "GAZ"; return ET_GAZ; }
+AUTRE { crc_calc(); ticv01yylval.label = "AUTRE"; return ET_AUTRE; }
+
+
+<DATA>{DATAC}+ { crc_calc(); ticv01yylval.text = strdup(yytext); return TOK_DATA; }
+
+<*>. { if (yy_flex_debug) pr_err("spurious character 0x%02hhx\n", *yytext); return *yytext; }
+
+<<EOF>> { yyterminate(); }
+%%
diff --git a/src/ticv01.y b/src/ticv01.y
new file mode 100644 (file)
index 0000000..f773c80
--- /dev/null
+++ b/src/ticv01.y
@@ -0,0 +1,181 @@
+//
+// ticv01.y
+// A parser for ENEDIS TIC version 01 protocol
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * This parser implements a complete grammar that supports TIC version 01
+ * as specified in Enedis-NOI-CPT_02E.pdf version 6.
+ *
+ * This parser does not allocate memory, except if a filter configuration is used in
+ * which case the etiq_en array will be allocated (it's a few hundred bytes).
+ * A left-recursion grammar has been implemented to keep the memory usage to the bare
+ * minimum as well. As a tradeoff, valid datasets are always emitted regardless of the
+ * overall status of the containing frame.
+ *
+ * Compteurs supportés:
+ * - Linky (mode historique)
+ * - Bleu monophasé (CBEMM / CBEMM ICC)
+ * - Bleu triphasé (CBETM)
+ * - Concentrateur de téléreport
+ *
+ * Pas de support des compteurs Jaune, Emeraude, PME-PMI ou Saphir.
+ */
+
+%{
+#include <stdlib.h>
+#include "tic.h"
+
+int ticv01yylex();
+int ticv01yylex_destroy();
+extern FILE *ticv01yyin;
+static void yyerror(const char *);
+
+extern bool filter_mode;
+extern bool *etiq_en;
+%}
+
+%union {
+ char *text;
+ const char *label;
+ struct tic_etiquette etiq;
+ struct tic_field field;
+}
+
+%verbose
+
+%token TOK_STX TOK_ETX TOK_EOT TOK_SEP
+%token FIELD_START FIELD_OK FIELD_KO
+%token TICFILTER
+
+%token <text> TOK_DATA
+
+%token <label> ET_ADCO ET_OPTARIF ET_ISOUSC ET_BASE ET_HCHC ET_HCHP ET_EJPHN ET_EJPHPM
+%token <label> ET_BBRHCJB ET_BBRHPJB ET_BBRHCJW ET_BBRHPJW ET_BBRHCJR ET_BBRHPJR
+%token <label> ET_PEJP ET_PTEC ET_DEMAIN ET_IINST ET_IINST1 ET_IINST2 ET_IINST3 ET_ADPS ET_IMAX ET_IMAX1 ET_IMAX2 ET_IMAX3
+%token <label> ET_PMAX ET_PAPP ET_HHPHC ET_MOTDETAT ET_PPOT ET_ADIR1 ET_ADIR2 ET_ADIR3
+%token <label> ET_GAZ ET_AUTRE
+
+%type <etiq> etiquette
+%type <field> field
+
+%destructor { free($$); } <text>
+%destructor { free_field(&$$); } <field>
+%destructor { } <> <*>
+
+%%
+
+start: filter | frames ;
+
+/* filter config only */
+filter:
+ TICFILTER etiquettes
+;
+
+etiquettes:
+ etiquette { etiq_en[1ドル.tok]=1; }
+ | etiquettes etiquette { etiq_en[2ドル.tok]=1; }
+ | error { YYABORT; }
+;
+
+/* stream processing */
+frames:
+ frame
+ | frames frame
+;
+
+frame:
+ TOK_STX datasets TOK_ETX { frame_sep(); }
+ | TOK_STX datasets TOK_EOT { /*mark error but don't print*/ frame_err(); frame_sep(); }
+ | error TOK_ETX { frame_err(); frame_sep(); pr_err("frame error\n"); yyerrok; }
+;
+
+datasets:
+ error { frame_err(); pr_err("dataset error\n"); }
+ | dataset
+ | datasets dataset
+;
+
+dataset:
+ FIELD_START field FIELD_OK { print_field(&2ドル); free_field(&2ドル); }
+ | FIELD_START field FIELD_KO { frame_err(); pr_err("dataset invalid checksum\n"); free_field(&2ドル); }
+ | FIELD_START error FIELD_OK { /*not a frame error*/ pr_err("unrecognized dataset\n"); yyerrok; }
+;
+
+field:
+ etiquette TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル NULL, 3ドル); }
+;
+
+etiquette:
+ ET_ADCO { $$.tok=yytranslate[ET_ADCO]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Adresse du compteur"; }
+ | ET_OPTARIF { $$.tok=yytranslate[ET_OPTARIF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Option tarifaire choisie"; }
+ | ET_ISOUSC { $$.tok=yytranslate[ET_ISOUSC]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité souscrite"; }
+ | ET_BASE { $$.tok=yytranslate[ET_BASE]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Base"; }
+ | ET_HCHC { $$.tok=yytranslate[ET_HCHC]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Heures Creuses: Heures Creuses"; }
+ | ET_HCHP { $$.tok=yytranslate[ET_HCHP]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Heures Creuses: Heures Pleines"; }
+ | ET_EJPHN { $$.tok=yytranslate[ET_EJPHN]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option EJP: Heures Normales"; }
+ | ET_EJPHPM { $$.tok=yytranslate[ET_EJPHPM]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option EJP: Heures de Pointe Mobile"; }
+ | ET_BBRHCJB { $$.tok=yytranslate[ET_BBRHCJB]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Bleus"; }
+ | ET_BBRHPJB { $$.tok=yytranslate[ET_BBRHPJB]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Bleus"; }
+ | ET_BBRHCJW { $$.tok=yytranslate[ET_BBRHCJW]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Blancs"; }
+ | ET_BBRHPJW { $$.tok=yytranslate[ET_BBRHPJW]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Blancs"; }
+ | ET_BBRHCJR { $$.tok=yytranslate[ET_BBRHCJR]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Rouges"; }
+ | ET_BBRHPJR { $$.tok=yytranslate[ET_BBRHPJR]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Rouges"; }
+ | ET_PEJP { $$.tok=yytranslate[ET_PEJP]; $$.unittype=U_MIN; $$.label=1ドル; $$.desc="Préavis Début EJP (30 min)"; }
+ | ET_PTEC { $$.tok=yytranslate[ET_PTEC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Période Tarifaire en cours"; }
+ | ET_DEMAIN { $$.tok=yytranslate[ET_DEMAIN]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Couleur du lendemain"; }
+ | ET_IINST { $$.tok=yytranslate[ET_IINST]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée"; }
+ | ET_IINST1 { $$.tok=yytranslate[ET_IINST1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 1"; }
+ | ET_IINST2 { $$.tok=yytranslate[ET_IINST2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 2"; }
+ | ET_IINST3 { $$.tok=yytranslate[ET_IINST3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 3"; }
+ | ET_ADPS { $$.tok=yytranslate[ET_ADPS]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement De Puissance Souscrite"; }
+ | ET_IMAX { $$.tok=yytranslate[ET_IMAX]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale"; }
+ | ET_IMAX1 { $$.tok=yytranslate[ET_IMAX1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 1"; }
+ | ET_IMAX2 { $$.tok=yytranslate[ET_IMAX2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 2"; }
+ | ET_IMAX3 { $$.tok=yytranslate[ET_IMAX3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 3"; }
+ | ET_PMAX { $$.tok=yytranslate[ET_PMAX]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Puissance maximale atteinte"; }
+ | ET_PAPP { $$.tok=yytranslate[ET_PAPP]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance apparente soutirée"; }
+ | ET_HHPHC { $$.tok=yytranslate[ET_HHPHC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Horaire Heures Pleines Heures Creuses"; }
+ | ET_MOTDETAT { $$.tok=yytranslate[ET_MOTDETAT]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Mot d'état du compteur"; }
+ | ET_PPOT { $$.tok=yytranslate[ET_PPOT]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Présence des potentiels"; }
+ | ET_ADIR1 { $$.tok=yytranslate[ET_ADIR1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 1"; }
+ | ET_ADIR2 { $$.tok=yytranslate[ET_ADIR2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 2"; }
+ | ET_ADIR3 { $$.tok=yytranslate[ET_ADIR3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 3"; }
+ | ET_GAZ { $$.tok=yytranslate[ET_GAZ]; $$.unittype=U_DAL; $$.label=1ドル; $$.desc="Index gaz"; }
+ | ET_AUTRE { $$.tok=yytranslate[ET_AUTRE]; $$.unittype=U_DAL; $$.label=1ドル; $$.desc="Index du troisième compteur"; }
+;
+
+%%
+
+#ifndef BAREBUILD
+void parse_config_v01(const char *filename)
+{
+ if (!(ticv01yyin = fopen(filename, "r"))) {
+ perror(filename);
+ exit(-1);
+ }
+
+ etiq_en = calloc(YYNTOKENS, sizeof(*etiq_en));
+ if (!etiq_en)
+ abort(); // OOM
+
+ filter_mode = true;
+ if (ticv01yyparse()) {
+ pr_err("%s: filter config error!\n", filename);
+ exit(-1);
+ }
+
+ fclose(ticv01yyin);
+ ticv01yylex_destroy();
+ ticv01yyin = stdin;
+ filter_mode = false;
+}
+#endif /* !BAREBUILD */
+
+static void yyerror(const char * s)
+{
+}
diff --git a/src/ticv02.l b/src/ticv02.l
new file mode 100644 (file)
index 0000000..492bdbf
--- /dev/null
+++ b/src/ticv02.l
@@ -0,0 +1,172 @@
+/*
+// ticv02.l
+// A lexer for ENEDIS TIC version 02 protocol
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+*/
+
+/**
+ * @file
+ * Complete lexer for ENEDIS' TIC protocol version 02.
+ * Supports version 02, a.k.a "standard" as found on ENEDIS' Linky smart meter.
+ * Ref doc: https://www.enedis.fr/media/2035/download
+ */
+
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+%option noyywrap
+
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+%option nounput
+
+/* we never directly read input */
+%option noinput
+
+/* we don't need the default rule */
+%option nodefault
+
+/* nounistd suppresses inclusion of the non-ANSI header file unistd.h.
+ This option is meant to target environments in which unistd.h does not exist.
+ Be aware that certain options may cause flex to generate code that relies on
+ functions normally found in unistd.h, (e.g. isatty(), read().)
+ If you wish to use these functions, you will have to inform your compiler where
+ to find them. See option-always-interactive. See option-read. */
+%option nounistd
+
+/* batch means that we'll never use the generated lexer interactively. */
+%option batch
+
+/* we only process 7-bit input */
+%option 7bit
+
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+%option nodebug
+
+%s FILTER
+%x DATA
+
+HORODATE [ EeHh][0-9]{12}
+DATAC [\x20-\x7e]
+CHKSUM [\x20-\x5f]
+SEP \x09
+
+%{
+ #include "tic.h"
+ #include "ticv02.tab.h"
+
+ extern bool filter_mode;
+ static uint8_t checksum;
+
+ static void crc_calc(void)
+ {
+ for (size_t i = 0; i<yyleng; i++)
+ checksum += (uint8_t)yytext[i];
+ }
+%}
+
+%%
+
+ if (filter_mode)
+ BEGIN(FILTER);
+
+ /* only for filter */
+<FILTER>"#"ticfilter\n { return TICFILTER; }
+<FILTER>[ \t\n]+ /* ignore whitespace */
+
+ /* balises */
+<*>\x02 { BEGIN(INITIAL); return TOK_STX; }
+<*>\x03 { BEGIN(INITIAL); return TOK_ETX; }
+<*>\x0a { BEGIN(INITIAL); checksum=0; return FIELD_START; }
+<*>{SEP} { checksum += (uint8_t)*yytext; BEGIN(DATA); return TOK_SEP; }
+<*>{CHKSUM}\x0d {
+ checksum = (checksum & 0x3f) + 0x20;
+ if (checksum == (uint8_t)yytext[0]) return FIELD_OK;
+ else return FIELD_KO;
+ }
+
+
+ /* etiquettes - mode standard */
+ADSC { crc_calc(); ticv02yylval.label = "ADSC"; return ET_ADSC; }
+VTIC { crc_calc(); ticv02yylval.label = "VTIC"; return ET_VTIC; }
+DATE { crc_calc(); ticv02yylval.label = "DATE"; return ET_DATE; }
+NGTF { crc_calc(); ticv02yylval.label = "NGTF"; return ET_NGTF; }
+LTARF { crc_calc(); ticv02yylval.label = "LTARF"; return ET_LTARF; }
+EAST { crc_calc(); ticv02yylval.label = "EAST"; return ET_EAST; }
+EASF01 { crc_calc(); ticv02yylval.label = "EASF01"; return ET_EASF01; }
+EASF02 { crc_calc(); ticv02yylval.label = "EASF02"; return ET_EASF02; }
+EASF03 { crc_calc(); ticv02yylval.label = "EASF03"; return ET_EASF03; }
+EASF04 { crc_calc(); ticv02yylval.label = "EASF04"; return ET_EASF04; }
+EASF05 { crc_calc(); ticv02yylval.label = "EASF05"; return ET_EASF05; }
+EASF06 { crc_calc(); ticv02yylval.label = "EASF06"; return ET_EASF06; }
+EASF07 { crc_calc(); ticv02yylval.label = "EASF07"; return ET_EASF07; }
+EASF08 { crc_calc(); ticv02yylval.label = "EASF08"; return ET_EASF08; }
+EASF09 { crc_calc(); ticv02yylval.label = "EASF09"; return ET_EASF09; }
+EASF10 { crc_calc(); ticv02yylval.label = "EASF10"; return ET_EASF10; }
+EASD01 { crc_calc(); ticv02yylval.label = "EASD01"; return ET_EASD01; }
+EASD02 { crc_calc(); ticv02yylval.label = "EASD02"; return ET_EASD02; }
+EASD03 { crc_calc(); ticv02yylval.label = "EASD03"; return ET_EASD03; }
+EASD04 { crc_calc(); ticv02yylval.label = "EASD04"; return ET_EASD04; }
+EAIT { crc_calc(); ticv02yylval.label = "EAIT"; return ET_EAIT; }
+ERQ1 { crc_calc(); ticv02yylval.label = "ERQ1"; return ET_ERQ1; }
+ERQ2 { crc_calc(); ticv02yylval.label = "ERQ2"; return ET_ERQ2; }
+ERQ3 { crc_calc(); ticv02yylval.label = "ERQ3"; return ET_ERQ3; }
+ERQ4 { crc_calc(); ticv02yylval.label = "ERQ4"; return ET_ERQ4; }
+IRMS1 { crc_calc(); ticv02yylval.label = "IRMS1"; return ET_IRMS1; }
+IRMS2 { crc_calc(); ticv02yylval.label = "IRMS2"; return ET_IRMS2; }
+IRMS3 { crc_calc(); ticv02yylval.label = "IRMS3"; return ET_IRMS3; }
+URMS1 { crc_calc(); ticv02yylval.label = "URMS1"; return ET_URMS1; }
+URMS2 { crc_calc(); ticv02yylval.label = "URMS2"; return ET_URMS2; }
+URMS3 { crc_calc(); ticv02yylval.label = "URMS3"; return ET_URMS3; }
+PREF { crc_calc(); ticv02yylval.label = "PREF"; return ET_PREF; }
+PCOUP { crc_calc(); ticv02yylval.label = "PCOUP"; return ET_PCOUP; }
+SINSTS { crc_calc(); ticv02yylval.label = "SINSTS"; return ET_SINSTS; }
+SINSTS1 { crc_calc(); ticv02yylval.label = "SINSTS1"; return ET_SINSTS1; }
+SINSTS2 { crc_calc(); ticv02yylval.label = "SINSTS2"; return ET_SINSTS2; }
+SINSTS3 { crc_calc(); ticv02yylval.label = "SINSTS3"; return ET_SINSTS3; }
+SMAXSN { crc_calc(); ticv02yylval.label = "SMAXSN"; return ET_SMAXSN; }
+SMAXSN1 { crc_calc(); ticv02yylval.label = "SMAXSN1"; return ET_SMAXSN1; }
+SMAXSN2 { crc_calc(); ticv02yylval.label = "SMAXSN2"; return ET_SMAXSN2; }
+SMAXSN3 { crc_calc(); ticv02yylval.label = "SMAXSN3"; return ET_SMAXSN3; }
+SMAXSN-1 { crc_calc(); ticv02yylval.label = "SMAXSN-1"; return ET_SMAXSNM1; }
+SMAXSN1-1 { crc_calc(); ticv02yylval.label = "SMAXSN1-1"; return ET_SMAXSN1M1; }
+SMAXSN2-1 { crc_calc(); ticv02yylval.label = "SMAXSN2-1"; return ET_SMAXSN2M1; }
+SMAXSN3-1 { crc_calc(); ticv02yylval.label = "SMAXSN3-1"; return ET_SMAXSN3M1; }
+SINSTI { crc_calc(); ticv02yylval.label = "SINSTI"; return ET_SINSTI; }
+SMAXIN { crc_calc(); ticv02yylval.label = "SMAXIN"; return ET_SMAXIN; }
+SMAXIN-1 { crc_calc(); ticv02yylval.label = "SMAXIN-1"; return ET_SMAXINM1; }
+CCASN { crc_calc(); ticv02yylval.label = "CCASN"; return ET_CCASN; }
+CCASN-1 { crc_calc(); ticv02yylval.label = "CCASN-1"; return ET_CCASNM1; }
+CCAIN { crc_calc(); ticv02yylval.label = "CCAIN"; return ET_CCAIN; }
+CCAIN-1 { crc_calc(); ticv02yylval.label = "CCAIN-1"; return ET_CCAINM1; }
+UMOY1 { crc_calc(); ticv02yylval.label = "UMOY1"; return ET_UMOY1; }
+UMOY2 { crc_calc(); ticv02yylval.label = "UMOY2"; return ET_UMOY2; }
+UMOY3 { crc_calc(); ticv02yylval.label = "UMOY3"; return ET_UMOY3; }
+STGE { crc_calc(); ticv02yylval.label = "STGE"; return ET_STGE; }
+DPM1 { crc_calc(); ticv02yylval.label = "DPM1"; return ET_DPM1; }
+FPM1 { crc_calc(); ticv02yylval.label = "FPM1"; return ET_FPM1; }
+DPM2 { crc_calc(); ticv02yylval.label = "DPM2"; return ET_DPM2; }
+FPM2 { crc_calc(); ticv02yylval.label = "FPM2"; return ET_FPM2; }
+DPM3 { crc_calc(); ticv02yylval.label = "DPM3"; return ET_DPM3; }
+FPM3 { crc_calc(); ticv02yylval.label = "FPM3"; return ET_FPM3; }
+MSG1 { crc_calc(); ticv02yylval.label = "MSG1"; return ET_MSG1; }
+MSG2 { crc_calc(); ticv02yylval.label = "MSG2"; return ET_MSG2; }
+PRM { crc_calc(); ticv02yylval.label = "PRM"; return ET_PRM; }
+RELAIS { crc_calc(); ticv02yylval.label = "RELAIS"; return ET_RELAIS; }
+NTARF { crc_calc(); ticv02yylval.label = "NTARF"; return ET_NTARF; }
+NJOURF { crc_calc(); ticv02yylval.label = "NJOURF"; return ET_NJOURF; }
+NJOURF\+1 { crc_calc(); ticv02yylval.label = "NJOURF+1"; return ET_NJOURFP1; }
+PJOURF\+1 { crc_calc(); ticv02yylval.label = "PJOURF+1"; return ET_PJOURFP1; }
+PPOINTE { crc_calc(); ticv02yylval.label = "PPOINTE"; return ET_PPOINTE; }
+
+<DATA>{HORODATE} { crc_calc(); ticv02yylval.text = strdup(yytext); return TOK_HDATE; }
+<DATA>{DATAC}+ { crc_calc(); ticv02yylval.text = strdup(yytext); return TOK_DATA; }
+
+<*>. { if (yy_flex_debug) pr_err("spurious character 0x%02hhx\n", *yytext); return *yytext; }
+
+<<EOF>> { yyterminate(); }
+%%
diff --git a/src/ticv02.y b/src/ticv02.y
new file mode 100644 (file)
index 0000000..6bfccca
--- /dev/null
+++ b/src/ticv02.y
@@ -0,0 +1,228 @@
+//
+// ticv02.y
+// A parser for ENEDIS TIC version 02 protocol
+//
+// (C) 2021 Thibaut VARENE
+// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
+//
+
+/**
+ * @file
+ * This parser implements a complete grammar that supports TIC version 02
+ * as specified in Enedis-NOI-CPT_54E.pdf version 3.
+ *
+ * This parser does not allocate memory, except if a filter configuration is used in
+ * which case the etiq_en array will be allocated (it's a few hundred bytes).
+ * A left-recursion grammar has been implemented to keep the memory usage to the bare
+ * minimum as well. As a tradeoff, valid datasets are always emitted regardless of the
+ * overall status of the containing frame.
+ */
+
+%{
+#include <stdlib.h>
+#include "tic.h"
+
+int ticv02yylex();
+int ticv02yylex_destroy();
+extern FILE *ticv02yyin;
+static void yyerror(const char *);
+
+extern bool filter_mode;
+extern bool *etiq_en;
+%}
+
+%union {
+ char *text;
+ const char *label;
+ struct tic_etiquette etiq;
+ struct tic_field field;
+}
+
+%verbose
+
+%token TOK_STX TOK_ETX TOK_SEP
+%token FIELD_START FIELD_OK FIELD_KO
+%token TICFILTER
+
+%token <text> TOK_HDATE TOK_DATA
+
+%token <label> ET_ADSC ET_VTIC ET_DATE ET_NGTF ET_LTARF
+%token <label> ET_EAST ET_EASF01 ET_EASF02 ET_EASF03 ET_EASF04 ET_EASF05 ET_EASF06 ET_EASF07 ET_EASF08 ET_EASF09 ET_EASF10
+%token <label> ET_EASD01 ET_EASD02 ET_EASD03 ET_EASD04 ET_EAIT ET_ERQ1 ET_ERQ2 ET_ERQ3 ET_ERQ4
+%token <label> ET_IRMS1 ET_IRMS2 ET_IRMS3 ET_URMS1 ET_URMS2 ET_URMS3 ET_PREF ET_PCOUP
+%token <label> ET_SINSTS ET_SINSTS1 ET_SINSTS2 ET_SINSTS3 ET_SMAXSN ET_SMAXSN1 ET_SMAXSN2 ET_SMAXSN3
+%token <label> ET_SMAXSNM1 ET_SMAXSN1M1 ET_SMAXSN2M1 ET_SMAXSN3M1 ET_SINSTI ET_SMAXIN ET_SMAXINM1
+%token <label> ET_CCASN ET_CCASNM1 ET_CCAIN ET_CCAINM1 ET_UMOY1 ET_UMOY2 ET_UMOY3 ET_STGE ET_DPM1 ET_FPM1 ET_DPM2 ET_FPM2 ET_DPM3 ET_FPM3
+%token <label> ET_MSG1 ET_MSG2 ET_PRM ET_RELAIS ET_NTARF ET_NJOURF ET_NJOURFP1 ET_PJOURFP1 ET_PPOINTE
+
+%type <etiq> etiquette etiquette_horodate etiquette_nodate
+%type <field> field field_horodate field_nodate
+
+%destructor { free($$); } <text>
+%destructor { free_field(&$$); } <field>
+%destructor { } <> <*>
+
+%%
+
+start: filter | frames ;
+
+/* filter config only */
+filter:
+ TICFILTER etiquettes
+;
+
+etiquettes:
+ etiquette { etiq_en[1ドル.tok]=1; }
+ | etiquettes etiquette { etiq_en[2ドル.tok]=1; }
+ | error { YYABORT; }
+;
+
+etiquette:
+ etiquette_horodate
+ | etiquette_nodate
+;
+
+/* stream processing */
+frames:
+ frame
+ | frames frame
+;
+
+frame:
+ TOK_STX datasets TOK_ETX { frame_sep(); }
+ | error TOK_ETX { frame_err(); frame_sep(); pr_err("frame error\n"); yyerrok; }
+;
+
+datasets:
+ error { frame_err(); pr_err("dataset error\n"); }
+ | dataset
+ | datasets dataset
+;
+
+dataset:
+ FIELD_START field FIELD_OK { print_field(&2ドル); free_field(&2ドル); }
+ | FIELD_START field FIELD_KO { frame_err(); pr_err("dataset invalid checksum\n"); free_field(&2ドル); }
+ | FIELD_START error FIELD_OK { /*not a frame error*/ pr_err("unrecognized dataset\n"); yyerrok; }
+;
+
+field: field_horodate
+ | field_nodate
+;
+
+field_horodate:
+ etiquette_horodate TOK_SEP TOK_HDATE TOK_SEP TOK_SEP { make_field(&$,ドル &1,ドル 3,ドル NULL); }
+ | etiquette_horodate TOK_SEP TOK_HDATE TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル 3,ドル 5ドル); }
+;
+
+field_nodate:
+ etiquette_nodate TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル NULL, 3ドル); }
+;
+
+etiquette_horodate:
+ ET_DATE { $$.tok=yytranslate[ET_DATE]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Date et heure courante"; }
+ | ET_SMAXSN { $$.tok=yytranslate[ET_SMAXSN]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n"; }
+ | ET_SMAXSN1 { $$.tok=yytranslate[ET_SMAXSN1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 1"; }
+ | ET_SMAXSN2 { $$.tok=yytranslate[ET_SMAXSN2]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 2"; }
+ | ET_SMAXSN3 { $$.tok=yytranslate[ET_SMAXSN3]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 3"; }
+ | ET_SMAXSNM1 { $$.tok=yytranslate[ET_SMAXSNM1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1"; }
+ | ET_SMAXSN1M1 { $$.tok=yytranslate[ET_SMAXSN1M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 1"; }
+ | ET_SMAXSN2M1 { $$.tok=yytranslate[ET_SMAXSN2M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 2"; }
+ | ET_SMAXSN3M1 { $$.tok=yytranslate[ET_SMAXSN3M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 3"; }
+ | ET_SMAXIN { $$.tok=yytranslate[ET_SMAXIN]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max injectée n"; }
+ | ET_SMAXINM1 { $$.tok=yytranslate[ET_SMAXINM1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max injectée n-1"; }
+ | ET_CCASN { $$.tok=yytranslate[ET_CCASN]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n de la courbe de charge active soutirée"; }
+ | ET_CCASNM1 { $$.tok=yytranslate[ET_CCASNM1]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n-1 de la courbe de charge active soutirée"; }
+ | ET_CCAIN { $$.tok=yytranslate[ET_CCAIN]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n de la courbe de charge active injectée"; }
+ | ET_CCAINM1 { $$.tok=yytranslate[ET_CCAINM1]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n-1 de la courbe de charge active injectée"; }
+ | ET_UMOY1 { $$.tok=yytranslate[ET_UMOY1]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 1"; }
+ | ET_UMOY2 { $$.tok=yytranslate[ET_UMOY2]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 2"; }
+ | ET_UMOY3 { $$.tok=yytranslate[ET_UMOY3]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 3"; }
+ | ET_DPM1 { $$.tok=yytranslate[ET_DPM1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 1"; }
+ | ET_FPM1 { $$.tok=yytranslate[ET_FPM1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 1"; }
+ | ET_DPM2 { $$.tok=yytranslate[ET_DPM2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 2"; }
+ | ET_FPM2 { $$.tok=yytranslate[ET_FPM2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 2"; }
+ | ET_DPM3 { $$.tok=yytranslate[ET_DPM3]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 3"; }
+ | ET_FPM3 { $$.tok=yytranslate[ET_FPM3]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 3"; }
+;
+
+etiquette_nodate:
+ ET_ADSC { $$.tok=yytranslate[ET_ADSC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Adresse Secondaire du Compteur"; }
+ | ET_VTIC { $$.tok=yytranslate[ET_VTIC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Version de la TIC"; }
+ | ET_NGTF { $$.tok=yytranslate[ET_NGTF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Nom du calendrier tarifaire fournisseur"; }
+ | ET_LTARF { $$.tok=yytranslate[ET_LTARF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Libellé tarif fournisseur en cours"; }
+ | ET_EAST { $$.tok=yytranslate[ET_EAST]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée totale"; }
+ | ET_EASF01 { $$.tok=yytranslate[ET_EASF01]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 01"; }
+ | ET_EASF02 { $$.tok=yytranslate[ET_EASF02]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 02"; }
+ | ET_EASF03 { $$.tok=yytranslate[ET_EASF03]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 03"; }
+ | ET_EASF04 { $$.tok=yytranslate[ET_EASF04]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 04"; }
+ | ET_EASF05 { $$.tok=yytranslate[ET_EASF05]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 05"; }
+ | ET_EASF06 { $$.tok=yytranslate[ET_EASF06]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 06"; }
+ | ET_EASF07 { $$.tok=yytranslate[ET_EASF07]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 07"; }
+ | ET_EASF08 { $$.tok=yytranslate[ET_EASF08]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 08"; }
+ | ET_EASF09 { $$.tok=yytranslate[ET_EASF09]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 09"; }
+ | ET_EASF10 { $$.tok=yytranslate[ET_EASF10]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 10"; }
+ | ET_EASD01 { $$.tok=yytranslate[ET_EASD01]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 01"; }
+ | ET_EASD02 { $$.tok=yytranslate[ET_EASD02]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 02"; }
+ | ET_EASD03 { $$.tok=yytranslate[ET_EASD03]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 03"; }
+ | ET_EASD04 { $$.tok=yytranslate[ET_EASD04]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 04"; }
+ | ET_EAIT { $$.tok=yytranslate[ET_EAIT]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active injectée totale"; }
+ | ET_ERQ1 { $$.tok=yytranslate[ET_ERQ1]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q1 totale"; }
+ | ET_ERQ2 { $$.tok=yytranslate[ET_ERQ2]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q2 totale"; }
+ | ET_ERQ3 { $$.tok=yytranslate[ET_ERQ3]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q3 totale"; }
+ | ET_ERQ4 { $$.tok=yytranslate[ET_ERQ4]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q4 totale"; }
+ | ET_IRMS1 { $$.tok=yytranslate[ET_IRMS1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 1"; }
+ | ET_IRMS2 { $$.tok=yytranslate[ET_IRMS2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 2"; }
+ | ET_IRMS3 { $$.tok=yytranslate[ET_IRMS3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 3"; }
+ | ET_URMS1 { $$.tok=yytranslate[ET_URMS1]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 1"; }
+ | ET_URMS2 { $$.tok=yytranslate[ET_URMS2]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 2"; }
+ | ET_URMS3 { $$.tok=yytranslate[ET_URMS3]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 3"; }
+ | ET_PREF { $$.tok=yytranslate[ET_PREF]; $$.unittype=U_KVA; $$.label=1ドル; $$.desc="Puissance app. de référence (PREF)"; }
+ | ET_PCOUP { $$.tok=yytranslate[ET_PCOUP]; $$.unittype=U_KVA; $$.label=1ドル; $$.desc="Puissance app. de coupure (PCOUP)"; }
+ | ET_SINSTS { $$.tok=yytranslate[ET_SINSTS]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée"; }
+ | ET_SINSTS1 { $$.tok=yytranslate[ET_SINSTS1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 1"; }
+ | ET_SINSTS2 { $$.tok=yytranslate[ET_SINSTS2]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 2"; }
+ | ET_SINSTS3 { $$.tok=yytranslate[ET_SINSTS3]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 3"; }
+ | ET_SINSTI { $$.tok=yytranslate[ET_SINSTI]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée injectée"; }
+ | ET_STGE { $$.tok=yytranslate[ET_STGE]; $$.unittype=U_SANS|T_HEX; $$.label=1ドル; $$.desc="Registre de Statuts"; }
+ | ET_MSG1 { $$.tok=yytranslate[ET_MSG1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Message court"; }
+ | ET_MSG2 { $$.tok=yytranslate[ET_MSG2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Message Ultra court"; }
+ | ET_PRM { $$.tok=yytranslate[ET_PRM]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="PRM"; }
+ | ET_RELAIS { $$.tok=yytranslate[ET_RELAIS]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Relais"; }
+ | ET_NTARF { $$.tok=yytranslate[ET_NTARF]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro de l'index tarifaire en cours"; }
+ | ET_NJOURF { $$.tok=yytranslate[ET_NJOURF]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro du jour en cours calendrier fournisseur"; }
+ | ET_NJOURFP1 { $$.tok=yytranslate[ET_NJOURFP1]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro du prochain jour calendrier fournisseur"; }
+ | ET_PJOURFP1 { $$.tok=yytranslate[ET_PJOURFP1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Profil du prochain jour calendrier fournisseur"; }
+ | ET_PPOINTE { $$.tok=yytranslate[ET_PPOINTE]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Profil du prochain jour de pointe"; }
+;
+
+
+%%
+
+#ifndef BAREBUILD
+void parse_config_v02(const char *filename)
+{
+ if (!(ticv02yyin = fopen(filename, "r"))) {
+ perror(filename);
+ exit(-1);
+ }
+
+ etiq_en = calloc(YYNTOKENS, sizeof(*etiq_en));
+ if (!etiq_en)
+ abort(); // OOM
+
+ filter_mode = true;
+ if (ticv02yyparse()) {
+ pr_err("%s: filter config error!\n", filename);
+ exit(-1);
+ }
+
+ fclose(ticv02yyin);
+ ticv02yylex_destroy();
+ ticv02yyin = stdin;
+ filter_mode = false;
+}
+#endif /* !BAREBUILD */
+
+static void yyerror(const char * s)
+{
+}
diff --git a/tic.c b/tic.c
deleted file mode 100644 (file)
index 016ec00..0000000
--- a/tic.c
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// tic.c
-// Common routines for TIC parsers
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * Common routines used by the TIC parsers.
- * The code making use of the parsers must provide extra functions as mentioned in the header.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "tic.h"
-
-bool filter_mode; ///< if true, switch lexers to configuration parsing
-bool *etiq_en; ///< when non-NULL, a token-indexed array, where the related token is emitted if the value is true. @note This could be made a bit field if memory is a concern
-
-void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data)
-{
- // args come from the bison stack
- int base;
-
- field->horodate = horodate;
- memcpy(&field->etiq, etiq, sizeof(field->etiq));
-
- switch ((etiq->unittype & 0xF0)) {
- case T_STRING:
- field->data.s = data;
- return;
- case T_HEX:
- base = 16;
- break;
- default:
- base = 10;
- break;
- }
- field->data.i = (int)strtol(data, NULL, base);
- free(data);
-}
-
-void free_field(struct tic_field *field)
-{
- free(field->horodate);
- switch ((field->etiq.unittype & 0xF0)) {
- case T_STRING:
- free(field->data.s);
- break;
- default:
- break;
- }
-}
diff --git a/tic.h b/tic.h
deleted file mode 100644 (file)
index f01670a..0000000
--- a/tic.h
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// tic.h
-// Interface for TIC parsers
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * Interface for TIC parsers
- * Users of this interface must implement the functions declared at the bottom:
- * - print_field()
- * - frame_sep()
- * - frame_err()
- */
-
-#ifndef tic_h
-#define tic_h
-
-#include <stdio.h>
-#include <inttypes.h>
-#include <stdbool.h>
-
-#ifdef BAREBUILD
- #define pr_err(format, ...) /* nothing */
-#else
- #define pr_err(format, ...) fprintf(stderr, "ERREUR: " format, ## __VA_ARGS__)
-#endif
-
-/**
- * TIC units.
- * @warning The code assumes this fits on 4 bits
- */
-enum tic_unit {
- U_SANS = 0x00,
- U_WH,
- U_VARH,
- U_A,
- U_V,
- U_KVA,
- U_VA,
- U_W,
- U_MIN,
- U_DAL,
-};
-
-/**
- * TIC data types.
- * By default everything is an int.
- * @warning The code assumes this is packed in the upper 4 bits of a byte: must increment by 0x10.
- */
- enum data_type {
- T_STRING = 0x10,
- T_HEX = 0x20,
-};
-
-/** Internal parser representation of a TIC etiquette */
-struct tic_etiquette {
- uint8_t tok; ///< bison token number
- uint8_t unittype; ///< combined unit and type (see @tic_unit @data_type)
- const char *label; ///< TIC "etiquette", as an ASCII string
- const char *desc; ///< corresponding TIC long description
-};
-
-/** Internal parser representation of a TIC field (i.e. body of a dataset) */
-struct tic_field {
- struct tic_etiquette etiq; ///< the field "etiquette"
- union {
- char *s;
- int i;
- } data; ///< the field data, if any
- char *horodate; ///< the field horodate, if any
-};
-
-void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data);
-void free_field(struct tic_field *field);
-
-// The following functions must be provided by the output interface
-
-/**
- * Called for each valid dataset.
- * Used to print a TIC dataset in the desired output format.
- * @param field the dataset to print
- */
-void print_field(const struct tic_field *field);
-
-/**
- * Called after each frame, valid or not.
- * Used to print a frame separator in the desired output format.
- */
-void frame_sep(void);
-
-/**
- * Called whenever a frame error condition occurs.
- * When frames or datasets have errors.
- */
-void frame_err(void);
-
-#endif /* tic_h */
diff --git a/tic2json.c b/tic2json.c
deleted file mode 100644 (file)
index a540b96..0000000
--- a/tic2json.c
+++ /dev/null
@@ -1,487 +0,0 @@
-//
-// tic2json.c
-// A tool to turn ENEDIS TIC data into pure JSON
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * Outputs as JSON a series of frames formatted as a list of fields or a dictionary.
- * - for list mode, fields are { "label": "xxx", "data": "xxx", horodate: "xxx", "desc": "xxx", "unit": "xxx" }
- * - for dict mode, the keys are the label, followed by { "data": "xxx", "horodate": "xxx", "desc": "xxx", "unit": "xxx" }
- * with horodate optional, unit and data optional and possibly empty and data being either quoted string or number.
- *
- * Data errors can result in some/all datasets being omitted in the output frame (e.g. invalid datasets or datasets
- * that did not pass checksum are not emitted): the JSON root object can then be empty but is still emitted.
- * In dictionary mode the parser will report the frame status as "_tvalide" ("trame valide") followed by either 1
- * for a valid frame or 0 for a frame containing errors (including dataset errors).
- *
- * Output JSON is guaranteed to always be valid for each frame. By default only frames are separated with newlines.
- *
- * @note: the program can only parse a single version of the TIC within one execution context.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef BAREBUILD
- #include <unistd.h> // getopt
- #include <err.h>
-#endif
-
-#include "tic.h"
-#ifdef TICV01
- #include "ticv01.tab.h"
- int ticv01yylex_destroy();
-#endif
-#ifdef TICV02
- #include "ticv02.tab.h"
- int ticv02yylex_destroy();
-#endif
-
-#include "tic2json.h"
-
-#define ticprintf(format, ...) printf(format, ## __VA_ARGS__)
-
-#ifdef BAREBUILD
- #warning BAREBUILD currently requires defining only one version of supported TIC and does not provide main()
- #ifdef PRINT2BUF
- static char * ticbuf;
- static size_t ticbufsize, ticbufavail;
- static tic2json_framecb_t ticframecb;
-
- #include <stdarg.h>
- #undef ticprintf
- int ticprintf(const char * restrict format, ...)
- {
- int ret;
- va_list args;
-
- va_start(args, format);
- ret = vsnprintf(ticbuf + (ticbufsize - ticbufavail), ticbufavail, format, args);
- va_end(args);
-
- if (ret >= ticbufavail) {
- fprintf(stderr, "ERROR: output buffer too small!\n");
- return ticbufavail;
- }
- else if (ret < 0)
- return ret;
-
- ticbufavail -= ret;
-
- return (ret);
- }
- #endif /* PRINT2BUF */
-#endif /* BAREBUILD */
-
-#define TIC2JSON_VER "2.1"
-
-extern bool filter_mode;
-extern bool *etiq_en;
-
-/** Global configuration details */
-static struct {
- const char *idtag;
- char framedelims[2];
- char fdelim;
- int optflags;
- unsigned int skipframes, framecount;
- bool ferr;
-} tp;
-
-/** TIC units representation strings */
-static const char * tic_units[] = {
- [U_SANS] = "",
- [U_WH] = "Wh",
- [U_VARH] = "VArh",
- [U_A] = "A",
- [U_V] = "V",
- [U_KVA] = "kVA",
- [U_VA] = "VA",
- [U_W] = "W",
- [U_MIN] = "mn",
- [U_DAL] = "daL",
-};
-
-#ifdef TICV02
-static void print_stge_data(int data)
-{
- const char sep = (tp.optflags & TIC2JSON_OPT_CRFIELD) ? '\n' : ' ';
- uint32_t d = (uint32_t)data;
-
-
- const char *const of[] = {
- "fermé",
- "ouvert",
- };
-
- const char *const coupure[] = {
- "fermé",
- "ouvert sur surpuissance",
- "ouvert sur surtension",
- "ouvert sur délestage",
- "ouvert sur ordre CPL ou Euridis",
- "ouvert sur une surchauffe avec une valeur de courant supérieure au courant de commutation maximal",
- "ouvert sur une surchauffe avec une valeur de courant inférieure au courant de commutation maximal",
- NULL,
- };
-
- const char *const euridis[] = {
- "désactivée",
- "activée sans sécurité",
- NULL,
- "activée avec sécurité",
- };
-
- const char *const cpl[] = {
- "New/Unlock",
- "New/Lock",
- "Registered",
- NULL,
- };
-
- const char *const tempo[] = {
- "Pas d'annonce",
- "Bleu",
- "Blanc",
- "Rouge",
- };
-
- const char *const pm[] = {
- "pas",
- "PM1",
- "PM2",
- "PM3",
- };
-
- ticprintf("{ "
- "\"Contact sec\": \"%s\",%c"
- "\"Organe de coupure\": \"%s\",%c"
- "\"État du cache-bornes distributeur\": \"%s\",%c"
- "\"Surtension sur une des phases\": \"%ssurtension\",%c"
- "\"Dépassement de la puissance de référence\": \"%s\",%c"
- "\"Fonctionnement producteur/consommateur\": \"%s\",%c"
- "\"Sens de l'énergie active\": \"énergie active %s\",%c"
- "\"Tarif en cours sur le contrat fourniture\": \"énergie ventilée sur Index %d\",%c"
- "\"Tarif en cours sur le contrat distributeur\": \"énergie ventilée sur Index %d\",%c"
- "\"Mode dégradé de l'horloge\": \"horloge %s\",%c"
- "\"État de la sortie télé-information\": \"mode %s\",%c"
- "\"État de la sortie communication Euridis\": \"%s\",%c"
- "\"Statut du CPL\": \"%s\",%c"
- "\"Synchronisation CPL\": \"compteur%s synchronisé\",%c"
- "\"Couleur du jour pour le contrat historique tempo\": \"%s\",%c"
- "\"Couleur du lendemain pour le contrat historique tempo\": \"%s\",%c"
- "\"Préavis pointes mobiles\": \"%s en cours\",%c"
- "\"Pointe mobile\": \"%s en cours\" }%c"
- ,
- of[d & 0x01], sep,
- coupure[(d>>1) & 0x07], sep,
- of[(d>>4) & 0x01], sep,
- (d>>6) & 0x01 ? "" : "pas de ", sep,
- (d>>7) & 0x01 ? "dépassement en cours" : "pas de dépassement", sep,
- (d>>8) & 0x01 ? "producteur" : "consommateur", sep,
- (d>>9) & 0x01 ? "négative" : "positive", sep,
- ((d>>10) & 0x0F) + 1, sep,
- ((d>>14) & 0x07) + 1, sep,
- (d>>16) & 0x01 ? "en mode dégradée" : "correcte", sep,
- (d>>17) & 0x01 ? "standard" : "historique", sep,
- euridis[(d>>19) & 0x03], sep,
- cpl[(d>>21) & 0x03], sep,
- (d>>23) & 0x01 ? "" : " non", sep,
- tempo[(d>>24) & 0x03], sep,
- tempo[(d>>26) & 0x03], sep,
- pm[(d>>28) & 0x03], sep,
- pm[(d>>30) & 0x03], sep
- );
-}
-#endif /* TICV02 */
-
-void print_field(const struct tic_field *field)
-{
- const char fdictout[] = "%c \"%.8s\": { \"data\": ";
- const char flistout[] = "%c{ \"label\": \"%.8s\", \"data\": ";
- const char *format;
- uint8_t type;
-
- // filters
- if (tp.framecount ||
- ((tp.optflags & TIC2JSON_OPT_MASKZEROES) && (T_STRING != (field->etiq.unittype & 0xF0)) && (0 == field->data.i)) ||
- (etiq_en && !etiq_en[field->etiq.tok]))
- return;
-
- format = (tp.optflags & TIC2JSON_OPT_DICTOUT) ? fdictout : flistout;
-
- ticprintf(format, tp.fdelim, field->etiq.label);
- switch (field->etiq.unittype & 0x0F) {
- case U_SANS:
- type = field->etiq.unittype & 0xF0;
- if (T_STRING == type) {
- ticprintf("\"%s\"", field->data.s ? field->data.s : "");
- break;
- }
-#ifdef TICV02
- else if ((T_HEX == type) && (tp.optflags & TIC2JSON_OPT_PARSESTGE)) {
- // XXX abuse the fact that STGE is the only U_SANS|T_HEX field
- print_stge_data(field->data.i);
- break;
- }
-#endif /* TICV02 */
- // fallthrough
- default:
- ticprintf("%d", field->data.i);
- break;
- }
-
-#ifdef TICV02
- if (field->horodate) {
- if (tp.optflags & TIC2JSON_OPT_LONGDATE) {
- const char *o, *d = field->horodate;
- switch (d[0]) {
- default:
- case ' ':
- o = "";
- break;
- case 'E':
- case 'e':
- o = "+02:00";
- break;
- case 'H':
- case 'h':
- o = "+01:00";
- break;
- }
- ticprintf(", \"horodate\": \"20%.2s-%.2s-%.2sT%.2s:%.2s:%.2s%s\"", d+1, d+3, d+5, d+7, d+9, d+11, o);
- }
- else
- ticprintf(", \"horodate\": \"%s\"", field->horodate);
- }
-#endif /* TICV02 */
-
- if (tp.optflags & TIC2JSON_OPT_DESCFORM)
- ticprintf(", \"desc\": \"%s\", \"unit\": \"%s\"", field->etiq.desc, tic_units[(field->etiq.unittype & 0x0F)]);
-
- if (tp.idtag)
- ticprintf(", \"id\": \"%s\"", tp.idtag);
-
- ticprintf("}%c", (tp.optflags & TIC2JSON_OPT_CRFIELD) ? '\n': ' ');
-
- tp.fdelim = ',';
-}
-
-void frame_sep(void)
-{
- if (!tp.framecount--) {
- tp.framecount = tp.skipframes;
- if (tp.optflags & TIC2JSON_OPT_DICTOUT)
- ticprintf("%c \"_tvalide\": %d", tp.fdelim, !tp.ferr);
-#ifdef PRINT2BUF
- ticprintf("%c\n", tp.framedelims[1]);
- if (ticframecb)
- ticframecb(ticbuf, ticbufsize-ticbufavail, !tp.ferr);
- ticbufavail = ticbufsize; // reset buffer
- ticprintf("%c", tp.framedelims[0]);
-#else
- ticprintf("%c\n%c", tp.framedelims[1], tp.framedelims[0]);
-#endif
- }
- tp.fdelim = ' ';
- tp.ferr = 0;
-}
-
-void frame_err(void)
-{
- tp.ferr = 1;
-}
-
-static inline void ticinit(void)
-{
- filter_mode = false;
- etiq_en = NULL;
-
- memset(&tp, 0, sizeof(tp));
- tp.framedelims[0] = '['; tp.framedelims[1] = ']';
- tp.fdelim = ' ';
-}
-
-#ifndef BAREBUILD
-static void usage(void)
-{
- printf( "usage: " BINNAME " {-1|-2} [-dhlnruVz] [-e fichier] [-i id] [-s N]\n" // FIXME -1|-2 always shown
-#ifdef TICV01
- " -1\t\t" "Analyse les trames TIC version 01 \"historique\"\n"
-#endif
-#ifdef TICV02
- " -2\t\t" "Analyse les trames TIC version 02 \"standard\"\n"
-#endif
- "\n"
- " -d\t\t" "Émet les trames sous forme de dictionaire plutôt que de liste\n"
- " -e fichier\t" "Utilise <fichier> pour configurer le filtre d'étiquettes\n"
- " -h\t\t" "Montre ce message d'aide et quitte\n"
- " -i id\t\t" "Ajoute une balise \"id\" avec la valeur <id> à chaque groupe\n"
- " -l\t\t" "Ajoute les descriptions longues et les unitées de chaque groupe\n"
- " -n\t\t" "Insère une nouvelle ligne après chaque groupe\n"
- " -r\t\t" "Interprète les horodates en format RFC3339\n"
- " -s N\t\t" "Émet une trame toutes les <N> reçues\n"
- " -u\t\t" "Décode le registre de statut sous forme de dictionnaire\n"
- " -V\t\t" "Montre la version et quitte\n"
- " -z\t\t" "Masque les groupes numériques à zéro\n"
- "\n"
- "Note: le fichier de configuration du filtre d'étiquettes doit commencer par une ligne comportant\n"
- "uniquement la séquence de caractères suivante: `#ticfilter` (sans les apostrophes), suivi à partir de\n"
- "la ligne suivante d'un nombre quelconque d'étiquettes TIC séparées par du blanc (espace, nouvelle ligne, etc).\n"
- "Seuls les groupes dont les étiquettes sont ainsi listées seront alors émis par le programme.\n"
- );
-}
-
-#ifdef TICV01
-void parse_config_v01(const char *filename);
-#endif
-
-#ifdef TICV02
-void parse_config_v02(const char *filename);
-#endif
-
-int main(int argc, char **argv)
-{
- void (*parse_config)(const char *);
- int (*yyparse)(void) = NULL;
- int (*yylex_destroy)(void) = NULL;
-
- const char *fconfig = NULL;
- int ch;
-
- ticinit();
-
- while ((ch = getopt(argc, argv, "12de:hi:lnrs:uVz")) != -1) {
- switch (ch) {
-#ifdef TICV01
- case '1':
- if (yyparse)
- errx(-1, "ERREUR: Une seule version de TIC peut être analysée à la fois");
- parse_config = parse_config_v01;
- yyparse = ticv01yyparse;
- yylex_destroy = ticv01yylex_destroy;
- break;
-#endif
-#ifdef TICV02
- case '2':
- if (yyparse)
- errx(-1, "ERREUR: Une seule version de TIC peut être analysée à la fois");
- parse_config = parse_config_v02;
- yyparse = ticv02yyparse;
- yylex_destroy = ticv02yylex_destroy;
- break;
-#endif
- case 'd':
- tp.optflags |= TIC2JSON_OPT_DICTOUT;
- tp.framedelims[0] = '{'; tp.framedelims[1] = '}';
- break;
- case 'e':
- fconfig = optarg;
- break;
- case 'h':
- usage();
- return 0;
- case 'i':
- tp.idtag = optarg;
- break;
- case 'l':
- tp.optflags |= TIC2JSON_OPT_DESCFORM;
- break;
- case 'n':
- tp.optflags |= TIC2JSON_OPT_CRFIELD;
- break;
- case 'r':
- tp.optflags |= TIC2JSON_OPT_LONGDATE;
- break;
- case 's':
- tp.skipframes = (unsigned int)strtol(optarg, NULL, 10);
- break;
- case 'u':
- tp.optflags |= TIC2JSON_OPT_PARSESTGE;
- break;
- case 'V':
- printf( BINNAME " version " TIC2JSON_VER "\n"
- "License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl-2.0.html>.\n"
- "Copyright (C) 2021 Thibaut Varène.\n");
- return 0;
- case 'z':
- tp.optflags |= TIC2JSON_OPT_MASKZEROES;
- break;
- default:
- usage();
- exit(-1);
- }
- }
- argc -= optind;
- argv += optind;
-
- if (!yyparse)
- errx(-1, "ERREUR: version TIC non spécifiée");
-
- if (fconfig)
- parse_config(fconfig);
-
- putchar(tp.framedelims[0]);
- yyparse();
- printf("%c\n", tp.framedelims[1]);
- yylex_destroy();
-
- free(etiq_en);
- return 0;
-}
-
-#else /* BAREBUILD */
-
-extern FILE *ticv01yyin;
-extern FILE *ticv02yyin;
-
-#ifdef PRINT2BUF
-/**
- * tic2json_main(), print to buffer variant.
- * @param yyin the FILE to read TIC frames from
- * @param optflags bitfield for tuning parser behavior
- * @param buf an allocated buffer to write JSON data to
- * @param size the size of the buffer
- * @param an optional callback to call after each printed frame, before the buffer content is overwritten.
- */
-void tic2json_main(FILE * yyin, int optflags, char * buf, size_t size, tic2json_framecb_t cb)
-#else
-void tic2json_main(FILE * yyin, int optflags)
-#endif
-{
- ticinit();
- tp.optflags = optflags;
-
- if (tp.optflags & TIC2JSON_OPT_DICTOUT) {
- tp.framedelims[0] = '{';
- tp.framedelims[1] = '}';
- }
-
-#ifdef PRINT2BUF
- ticbuf = buf;
- ticbufavail = ticbufsize = size;
- ticframecb = cb;
-#endif
-
- ticprintf("%c", tp.framedelims[0]);
-
-#if defined(TICV01)
- ticv01yyin = yyin;
- ticv01yyparse();
- ticv01yylex_destroy();
-#elif defined(TICV02)
- ticv02yyin = yyin;
- ticv02yyparse();
- ticv02yylex_destroy();
-#else
- fprintf(stderr, "NO TIC VERSION DEFINED!\n"); // avoid utf-8
-#endif
-
- ticprintf("%c\n", tp.framedelims[1]);
-}
-
-#endif /* !BAREBUILD */
diff --git a/tic2json.h b/tic2json.h
deleted file mode 100644 (file)
index afd70f5..0000000
--- a/tic2json.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// tic2json.h
-//
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * exports for tic2json, mainly useful for embedded applications using tic2json_main().
- */
-
-#ifndef tic2json_h
-#define tic2json_h
-
-/** enum for optflags bitfield */
-enum {
- TIC2JSON_OPT_MASKZEROES = 0x01,
- TIC2JSON_OPT_CRFIELD = 0x02,
- TIC2JSON_OPT_DESCFORM = 0x04,
- TIC2JSON_OPT_DICTOUT = 0x08,
- TIC2JSON_OPT_LONGDATE = 0x10,
- TIC2JSON_OPT_PARSESTGE = 0x20,
-};
-
-/**
- * TIC frame callback
- * @param buf the buffer received from tic2json_main(), filled with JSON data
- * @param size the length of JSON data currently in the buffer
- * @param valid true if TIC frame is valid, false otherwise
- */
-typedef void (*tic2json_framecb_t)(char * buf, size_t size, bool valid);
-
-#endif /* tic2json_h */
diff --git a/ticv01.l b/ticv01.l
deleted file mode 100644 (file)
index a9d3350..0000000
--- a/ticv01.l
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-// ticv01.l
-// A lexer for ENEDIS TIC version 01 protocol
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-*/
-
-/**
- * @file
- * Complete lexer for ENEDIS' TIC protocol version 01.
- * Supports version 01, a.k.a "historique" as found on ENEDIS' Linky and Bleus meters.
- * Ref doc: https://www.enedis.fr/media/2027/download
- *
- * "le champ « étiquette » ne contient aucun caractère ayant une valeur égale à celle
- * du caractère-séparateur utilisé pour la trame (caractère ASCII « espace » ou caractère
- * ASCII « tabulation horizontale » suivant le cas). Par contre, le champ « donnée »
- * contenant l’information fournie par le groupe peut, lui, contenir des caractères ayant
- * une valeur égale à celle du caractère-séparateur utilisé pour la trame."
- *
- * Mais NOI-CPT_54E indique toutefois:
- * "Note : le caractère séparateur des champs "Horizontal Tab" HT (0x09), en mode standard
- * est different du caractère séparateur "Space" SP (0x20) en mode historique.
- * Cette disposition permet d’utiliser le caractère "Space" pour les données."
- *
- * On serait donc fondé à penser qu'en mode historique, ni les étiquettes ni les données
- * ne contiennent 0x20. Du moins en sortie Linky "historique". Cette hypothèse est donc
- * retenue jusqu'à preuve du contraire.
- */
-
-/* noyywrap disables automatic rewinding for the next file to parse. Since we
- always parse only a single string, there's no need to do any wraps. And
- using yywrap requires linking with -lfl, which provides the default yywrap
- implementation that always returns 1 anyway. */
-%option noyywrap
-
-/* nounput simplifies the lexer, by removing support for putting a character
- back into the input stream. We never use such capability anyway. */
-%option nounput
-
-/* we never directly read input */
-%option noinput
-
-/* we don't need the default rule */
-%option nodefault
-
-/* nounistd suppresses inclusion of the non-ANSI header file unistd.h.
- This option is meant to target environments in which unistd.h does not exist.
- Be aware that certain options may cause flex to generate code that relies on
- functions normally found in unistd.h, (e.g. isatty(), read().)
- If you wish to use these functions, you will have to inform your compiler where
- to find them. See option-always-interactive. See option-read. */
-%option nounistd
-
-/* batch means that we'll never use the generated lexer interactively. */
-%option batch
-
-/* we only process 7-bit input */
-%option 7bit
-
-/* Enables debug mode. To see the debug messages, one needs to also set
- yy_flex_debug to 1, then the debug messages will be printed on stderr. */
-%option nodebug
-
-%s FILTER
-%x DATA
-
-DATAC [\x21-\x7e]
-CHKSUM [\x20-\x5f]
-SEP \x20
-
-%{
- #include "tic.h"
- #include "ticv01.tab.h"
-
- extern bool filter_mode;
- static uint8_t checksum;
-
- static void crc_calc(void)
- {
- for (size_t i = 0; i<yyleng; i++)
- checksum += (uint8_t)yytext[i];
- }
-%}
-
-%%
-
- if (filter_mode)
- BEGIN(FILTER);
-
- /* only for filter */
-<FILTER>"#"ticfilter\n { return TICFILTER; }
-<FILTER>[ \t\n]+ /* ignore whitespace */
-
- /* balises */
-<*>\x02 { BEGIN(INITIAL); return TOK_STX; }
-<*>\x03 { BEGIN(INITIAL); return TOK_ETX; }
-<*>\x04 { BEGIN(INITIAL); return TOK_EOT; }
-<*>\x0a { BEGIN(INITIAL); checksum=0; return FIELD_START; }
-<*>{SEP} { checksum += (uint8_t)*yytext; BEGIN(DATA); return TOK_SEP; }
-<*>{CHKSUM}\x0d {
- checksum -= 0x20; // we have one space too many in the checksum: last SEP
- checksum = (checksum & 0x3f) + 0x20;
- if (checksum == (uint8_t)yytext[0]) return FIELD_OK;
- else return FIELD_KO;
- }
-
-
- /* etiquettes - mode historique */
-ADCO { crc_calc(); ticv01yylval.label = "ADCO"; return ET_ADCO; }
-OPTARIF { crc_calc(); ticv01yylval.label = "OPTARIF"; return ET_OPTARIF; }
-ISOUSC { crc_calc(); ticv01yylval.label = "ISOUSC"; return ET_ISOUSC; }
-BASE { crc_calc(); ticv01yylval.label = "BASE"; return ET_BASE; }
-HCHC { crc_calc(); ticv01yylval.label = "HCHC"; return ET_HCHC; }
-HCHP { crc_calc(); ticv01yylval.label = "HCHP"; return ET_HCHP; }
-EJPHN { crc_calc(); ticv01yylval.label = "EJPHN"; return ET_EJPHN; }
-EJPHPM { crc_calc(); ticv01yylval.label = "EJPHPM"; return ET_EJPHPM; }
-BBRHCJB { crc_calc(); ticv01yylval.label = "BBRHCJB"; return ET_BBRHCJB; }
-BBRHPJB { crc_calc(); ticv01yylval.label = "BBRHPJB"; return ET_BBRHPJB; }
-BBRHCJW { crc_calc(); ticv01yylval.label = "BBRHCJW"; return ET_BBRHCJW; }
-BBRHPJW { crc_calc(); ticv01yylval.label = "BBRHPJW"; return ET_BBRHPJW; }
-BBRHCJR { crc_calc(); ticv01yylval.label = "BBRHCJR"; return ET_BBRHCJR; }
-BBRHPJR { crc_calc(); ticv01yylval.label = "BBRHPJR"; return ET_BBRHPJR; }
-PEJP { crc_calc(); ticv01yylval.label = "PEJP"; return ET_PEJP; }
-PTEC { crc_calc(); ticv01yylval.label = "PTEC"; return ET_PTEC; }
-DEMAIN { crc_calc(); ticv01yylval.label = "DEMAIN"; return ET_DEMAIN; }
-IINST { crc_calc(); ticv01yylval.label = "IINST"; return ET_IINST; }
-IINST1 { crc_calc(); ticv01yylval.label = "IINST1"; return ET_IINST1; }
-IINST2 { crc_calc(); ticv01yylval.label = "IINST2"; return ET_IINST2; }
-IINST3 { crc_calc(); ticv01yylval.label = "IINST3"; return ET_IINST3; }
-ADPS { crc_calc(); ticv01yylval.label = "ADPS"; return ET_ADPS; }
-IMAX { crc_calc(); ticv01yylval.label = "IMAX"; return ET_IMAX; }
-IMAX1 { crc_calc(); ticv01yylval.label = "IMAX1"; return ET_IMAX1; }
-IMAX2 { crc_calc(); ticv01yylval.label = "IMAX2"; return ET_IMAX2; }
-IMAX3 { crc_calc(); ticv01yylval.label = "IMAX3"; return ET_IMAX3; }
-PMAX { crc_calc(); ticv01yylval.label = "PMAX"; return ET_PMAX; }
-PAPP { crc_calc(); ticv01yylval.label = "PAPP"; return ET_PAPP; }
-HHPHC { crc_calc(); ticv01yylval.label = "HHPHC"; return ET_HHPHC; }
-MOTDETAT { crc_calc(); ticv01yylval.label = "MOTDETAT"; return ET_MOTDETAT; }
-PPOT { crc_calc(); ticv01yylval.label = "PPOT"; return ET_PPOT; }
-ADIR1 { crc_calc(); ticv01yylval.label = "ADIR1"; return ET_ADIR1; }
-ADIR2 { crc_calc(); ticv01yylval.label = "ADIR2"; return ET_ADIR2; }
-ADIR3 { crc_calc(); ticv01yylval.label = "ADIR3"; return ET_ADIR3; }
-GAZ { crc_calc(); ticv01yylval.label = "GAZ"; return ET_GAZ; }
-AUTRE { crc_calc(); ticv01yylval.label = "AUTRE"; return ET_AUTRE; }
-
-
-<DATA>{DATAC}+ { crc_calc(); ticv01yylval.text = strdup(yytext); return TOK_DATA; }
-
-<*>. { if (yy_flex_debug) pr_err("spurious character 0x%02hhx\n", *yytext); return *yytext; }
-
-<<EOF>> { yyterminate(); }
-%%
diff --git a/ticv01.y b/ticv01.y
deleted file mode 100644 (file)
index f773c80..0000000
--- a/ticv01.y
+++ /dev/null
@@ -1,181 +0,0 @@
-//
-// ticv01.y
-// A parser for ENEDIS TIC version 01 protocol
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * This parser implements a complete grammar that supports TIC version 01
- * as specified in Enedis-NOI-CPT_02E.pdf version 6.
- *
- * This parser does not allocate memory, except if a filter configuration is used in
- * which case the etiq_en array will be allocated (it's a few hundred bytes).
- * A left-recursion grammar has been implemented to keep the memory usage to the bare
- * minimum as well. As a tradeoff, valid datasets are always emitted regardless of the
- * overall status of the containing frame.
- *
- * Compteurs supportés:
- * - Linky (mode historique)
- * - Bleu monophasé (CBEMM / CBEMM ICC)
- * - Bleu triphasé (CBETM)
- * - Concentrateur de téléreport
- *
- * Pas de support des compteurs Jaune, Emeraude, PME-PMI ou Saphir.
- */
-
-%{
-#include <stdlib.h>
-#include "tic.h"
-
-int ticv01yylex();
-int ticv01yylex_destroy();
-extern FILE *ticv01yyin;
-static void yyerror(const char *);
-
-extern bool filter_mode;
-extern bool *etiq_en;
-%}
-
-%union {
- char *text;
- const char *label;
- struct tic_etiquette etiq;
- struct tic_field field;
-}
-
-%verbose
-
-%token TOK_STX TOK_ETX TOK_EOT TOK_SEP
-%token FIELD_START FIELD_OK FIELD_KO
-%token TICFILTER
-
-%token <text> TOK_DATA
-
-%token <label> ET_ADCO ET_OPTARIF ET_ISOUSC ET_BASE ET_HCHC ET_HCHP ET_EJPHN ET_EJPHPM
-%token <label> ET_BBRHCJB ET_BBRHPJB ET_BBRHCJW ET_BBRHPJW ET_BBRHCJR ET_BBRHPJR
-%token <label> ET_PEJP ET_PTEC ET_DEMAIN ET_IINST ET_IINST1 ET_IINST2 ET_IINST3 ET_ADPS ET_IMAX ET_IMAX1 ET_IMAX2 ET_IMAX3
-%token <label> ET_PMAX ET_PAPP ET_HHPHC ET_MOTDETAT ET_PPOT ET_ADIR1 ET_ADIR2 ET_ADIR3
-%token <label> ET_GAZ ET_AUTRE
-
-%type <etiq> etiquette
-%type <field> field
-
-%destructor { free($$); } <text>
-%destructor { free_field(&$$); } <field>
-%destructor { } <> <*>
-
-%%
-
-start: filter | frames ;
-
-/* filter config only */
-filter:
- TICFILTER etiquettes
-;
-
-etiquettes:
- etiquette { etiq_en[1ドル.tok]=1; }
- | etiquettes etiquette { etiq_en[2ドル.tok]=1; }
- | error { YYABORT; }
-;
-
-/* stream processing */
-frames:
- frame
- | frames frame
-;
-
-frame:
- TOK_STX datasets TOK_ETX { frame_sep(); }
- | TOK_STX datasets TOK_EOT { /*mark error but don't print*/ frame_err(); frame_sep(); }
- | error TOK_ETX { frame_err(); frame_sep(); pr_err("frame error\n"); yyerrok; }
-;
-
-datasets:
- error { frame_err(); pr_err("dataset error\n"); }
- | dataset
- | datasets dataset
-;
-
-dataset:
- FIELD_START field FIELD_OK { print_field(&2ドル); free_field(&2ドル); }
- | FIELD_START field FIELD_KO { frame_err(); pr_err("dataset invalid checksum\n"); free_field(&2ドル); }
- | FIELD_START error FIELD_OK { /*not a frame error*/ pr_err("unrecognized dataset\n"); yyerrok; }
-;
-
-field:
- etiquette TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル NULL, 3ドル); }
-;
-
-etiquette:
- ET_ADCO { $$.tok=yytranslate[ET_ADCO]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Adresse du compteur"; }
- | ET_OPTARIF { $$.tok=yytranslate[ET_OPTARIF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Option tarifaire choisie"; }
- | ET_ISOUSC { $$.tok=yytranslate[ET_ISOUSC]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité souscrite"; }
- | ET_BASE { $$.tok=yytranslate[ET_BASE]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Base"; }
- | ET_HCHC { $$.tok=yytranslate[ET_HCHC]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Heures Creuses: Heures Creuses"; }
- | ET_HCHP { $$.tok=yytranslate[ET_HCHP]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Heures Creuses: Heures Pleines"; }
- | ET_EJPHN { $$.tok=yytranslate[ET_EJPHN]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option EJP: Heures Normales"; }
- | ET_EJPHPM { $$.tok=yytranslate[ET_EJPHPM]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option EJP: Heures de Pointe Mobile"; }
- | ET_BBRHCJB { $$.tok=yytranslate[ET_BBRHCJB]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Bleus"; }
- | ET_BBRHPJB { $$.tok=yytranslate[ET_BBRHPJB]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Bleus"; }
- | ET_BBRHCJW { $$.tok=yytranslate[ET_BBRHCJW]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Blancs"; }
- | ET_BBRHPJW { $$.tok=yytranslate[ET_BBRHPJW]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Blancs"; }
- | ET_BBRHCJR { $$.tok=yytranslate[ET_BBRHCJR]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Creuses Jours Rouges"; }
- | ET_BBRHPJR { $$.tok=yytranslate[ET_BBRHPJR]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Index option Tempo: Heures Pleines Jours Rouges"; }
- | ET_PEJP { $$.tok=yytranslate[ET_PEJP]; $$.unittype=U_MIN; $$.label=1ドル; $$.desc="Préavis Début EJP (30 min)"; }
- | ET_PTEC { $$.tok=yytranslate[ET_PTEC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Période Tarifaire en cours"; }
- | ET_DEMAIN { $$.tok=yytranslate[ET_DEMAIN]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Couleur du lendemain"; }
- | ET_IINST { $$.tok=yytranslate[ET_IINST]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée"; }
- | ET_IINST1 { $$.tok=yytranslate[ET_IINST1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 1"; }
- | ET_IINST2 { $$.tok=yytranslate[ET_IINST2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 2"; }
- | ET_IINST3 { $$.tok=yytranslate[ET_IINST3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité Instantanée phase 3"; }
- | ET_ADPS { $$.tok=yytranslate[ET_ADPS]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement De Puissance Souscrite"; }
- | ET_IMAX { $$.tok=yytranslate[ET_IMAX]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale"; }
- | ET_IMAX1 { $$.tok=yytranslate[ET_IMAX1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 1"; }
- | ET_IMAX2 { $$.tok=yytranslate[ET_IMAX2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 2"; }
- | ET_IMAX3 { $$.tok=yytranslate[ET_IMAX3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Intensité maximale phase 3"; }
- | ET_PMAX { $$.tok=yytranslate[ET_PMAX]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Puissance maximale atteinte"; }
- | ET_PAPP { $$.tok=yytranslate[ET_PAPP]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance apparente soutirée"; }
- | ET_HHPHC { $$.tok=yytranslate[ET_HHPHC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Horaire Heures Pleines Heures Creuses"; }
- | ET_MOTDETAT { $$.tok=yytranslate[ET_MOTDETAT]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Mot d'état du compteur"; }
- | ET_PPOT { $$.tok=yytranslate[ET_PPOT]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Présence des potentiels"; }
- | ET_ADIR1 { $$.tok=yytranslate[ET_ADIR1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 1"; }
- | ET_ADIR2 { $$.tok=yytranslate[ET_ADIR2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 2"; }
- | ET_ADIR3 { $$.tok=yytranslate[ET_ADIR3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Avertissement de Dépassement d'intensité de réglage phase 3"; }
- | ET_GAZ { $$.tok=yytranslate[ET_GAZ]; $$.unittype=U_DAL; $$.label=1ドル; $$.desc="Index gaz"; }
- | ET_AUTRE { $$.tok=yytranslate[ET_AUTRE]; $$.unittype=U_DAL; $$.label=1ドル; $$.desc="Index du troisième compteur"; }
-;
-
-%%
-
-#ifndef BAREBUILD
-void parse_config_v01(const char *filename)
-{
- if (!(ticv01yyin = fopen(filename, "r"))) {
- perror(filename);
- exit(-1);
- }
-
- etiq_en = calloc(YYNTOKENS, sizeof(*etiq_en));
- if (!etiq_en)
- abort(); // OOM
-
- filter_mode = true;
- if (ticv01yyparse()) {
- pr_err("%s: filter config error!\n", filename);
- exit(-1);
- }
-
- fclose(ticv01yyin);
- ticv01yylex_destroy();
- ticv01yyin = stdin;
- filter_mode = false;
-}
-#endif /* !BAREBUILD */
-
-static void yyerror(const char * s)
-{
-}
diff --git a/ticv02.l b/ticv02.l
deleted file mode 100644 (file)
index 492bdbf..0000000
--- a/ticv02.l
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
-// ticv02.l
-// A lexer for ENEDIS TIC version 02 protocol
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-*/
-
-/**
- * @file
- * Complete lexer for ENEDIS' TIC protocol version 02.
- * Supports version 02, a.k.a "standard" as found on ENEDIS' Linky smart meter.
- * Ref doc: https://www.enedis.fr/media/2035/download
- */
-
-/* noyywrap disables automatic rewinding for the next file to parse. Since we
- always parse only a single string, there's no need to do any wraps. And
- using yywrap requires linking with -lfl, which provides the default yywrap
- implementation that always returns 1 anyway. */
-%option noyywrap
-
-/* nounput simplifies the lexer, by removing support for putting a character
- back into the input stream. We never use such capability anyway. */
-%option nounput
-
-/* we never directly read input */
-%option noinput
-
-/* we don't need the default rule */
-%option nodefault
-
-/* nounistd suppresses inclusion of the non-ANSI header file unistd.h.
- This option is meant to target environments in which unistd.h does not exist.
- Be aware that certain options may cause flex to generate code that relies on
- functions normally found in unistd.h, (e.g. isatty(), read().)
- If you wish to use these functions, you will have to inform your compiler where
- to find them. See option-always-interactive. See option-read. */
-%option nounistd
-
-/* batch means that we'll never use the generated lexer interactively. */
-%option batch
-
-/* we only process 7-bit input */
-%option 7bit
-
-/* Enables debug mode. To see the debug messages, one needs to also set
- yy_flex_debug to 1, then the debug messages will be printed on stderr. */
-%option nodebug
-
-%s FILTER
-%x DATA
-
-HORODATE [ EeHh][0-9]{12}
-DATAC [\x20-\x7e]
-CHKSUM [\x20-\x5f]
-SEP \x09
-
-%{
- #include "tic.h"
- #include "ticv02.tab.h"
-
- extern bool filter_mode;
- static uint8_t checksum;
-
- static void crc_calc(void)
- {
- for (size_t i = 0; i<yyleng; i++)
- checksum += (uint8_t)yytext[i];
- }
-%}
-
-%%
-
- if (filter_mode)
- BEGIN(FILTER);
-
- /* only for filter */
-<FILTER>"#"ticfilter\n { return TICFILTER; }
-<FILTER>[ \t\n]+ /* ignore whitespace */
-
- /* balises */
-<*>\x02 { BEGIN(INITIAL); return TOK_STX; }
-<*>\x03 { BEGIN(INITIAL); return TOK_ETX; }
-<*>\x0a { BEGIN(INITIAL); checksum=0; return FIELD_START; }
-<*>{SEP} { checksum += (uint8_t)*yytext; BEGIN(DATA); return TOK_SEP; }
-<*>{CHKSUM}\x0d {
- checksum = (checksum & 0x3f) + 0x20;
- if (checksum == (uint8_t)yytext[0]) return FIELD_OK;
- else return FIELD_KO;
- }
-
-
- /* etiquettes - mode standard */
-ADSC { crc_calc(); ticv02yylval.label = "ADSC"; return ET_ADSC; }
-VTIC { crc_calc(); ticv02yylval.label = "VTIC"; return ET_VTIC; }
-DATE { crc_calc(); ticv02yylval.label = "DATE"; return ET_DATE; }
-NGTF { crc_calc(); ticv02yylval.label = "NGTF"; return ET_NGTF; }
-LTARF { crc_calc(); ticv02yylval.label = "LTARF"; return ET_LTARF; }
-EAST { crc_calc(); ticv02yylval.label = "EAST"; return ET_EAST; }
-EASF01 { crc_calc(); ticv02yylval.label = "EASF01"; return ET_EASF01; }
-EASF02 { crc_calc(); ticv02yylval.label = "EASF02"; return ET_EASF02; }
-EASF03 { crc_calc(); ticv02yylval.label = "EASF03"; return ET_EASF03; }
-EASF04 { crc_calc(); ticv02yylval.label = "EASF04"; return ET_EASF04; }
-EASF05 { crc_calc(); ticv02yylval.label = "EASF05"; return ET_EASF05; }
-EASF06 { crc_calc(); ticv02yylval.label = "EASF06"; return ET_EASF06; }
-EASF07 { crc_calc(); ticv02yylval.label = "EASF07"; return ET_EASF07; }
-EASF08 { crc_calc(); ticv02yylval.label = "EASF08"; return ET_EASF08; }
-EASF09 { crc_calc(); ticv02yylval.label = "EASF09"; return ET_EASF09; }
-EASF10 { crc_calc(); ticv02yylval.label = "EASF10"; return ET_EASF10; }
-EASD01 { crc_calc(); ticv02yylval.label = "EASD01"; return ET_EASD01; }
-EASD02 { crc_calc(); ticv02yylval.label = "EASD02"; return ET_EASD02; }
-EASD03 { crc_calc(); ticv02yylval.label = "EASD03"; return ET_EASD03; }
-EASD04 { crc_calc(); ticv02yylval.label = "EASD04"; return ET_EASD04; }
-EAIT { crc_calc(); ticv02yylval.label = "EAIT"; return ET_EAIT; }
-ERQ1 { crc_calc(); ticv02yylval.label = "ERQ1"; return ET_ERQ1; }
-ERQ2 { crc_calc(); ticv02yylval.label = "ERQ2"; return ET_ERQ2; }
-ERQ3 { crc_calc(); ticv02yylval.label = "ERQ3"; return ET_ERQ3; }
-ERQ4 { crc_calc(); ticv02yylval.label = "ERQ4"; return ET_ERQ4; }
-IRMS1 { crc_calc(); ticv02yylval.label = "IRMS1"; return ET_IRMS1; }
-IRMS2 { crc_calc(); ticv02yylval.label = "IRMS2"; return ET_IRMS2; }
-IRMS3 { crc_calc(); ticv02yylval.label = "IRMS3"; return ET_IRMS3; }
-URMS1 { crc_calc(); ticv02yylval.label = "URMS1"; return ET_URMS1; }
-URMS2 { crc_calc(); ticv02yylval.label = "URMS2"; return ET_URMS2; }
-URMS3 { crc_calc(); ticv02yylval.label = "URMS3"; return ET_URMS3; }
-PREF { crc_calc(); ticv02yylval.label = "PREF"; return ET_PREF; }
-PCOUP { crc_calc(); ticv02yylval.label = "PCOUP"; return ET_PCOUP; }
-SINSTS { crc_calc(); ticv02yylval.label = "SINSTS"; return ET_SINSTS; }
-SINSTS1 { crc_calc(); ticv02yylval.label = "SINSTS1"; return ET_SINSTS1; }
-SINSTS2 { crc_calc(); ticv02yylval.label = "SINSTS2"; return ET_SINSTS2; }
-SINSTS3 { crc_calc(); ticv02yylval.label = "SINSTS3"; return ET_SINSTS3; }
-SMAXSN { crc_calc(); ticv02yylval.label = "SMAXSN"; return ET_SMAXSN; }
-SMAXSN1 { crc_calc(); ticv02yylval.label = "SMAXSN1"; return ET_SMAXSN1; }
-SMAXSN2 { crc_calc(); ticv02yylval.label = "SMAXSN2"; return ET_SMAXSN2; }
-SMAXSN3 { crc_calc(); ticv02yylval.label = "SMAXSN3"; return ET_SMAXSN3; }
-SMAXSN-1 { crc_calc(); ticv02yylval.label = "SMAXSN-1"; return ET_SMAXSNM1; }
-SMAXSN1-1 { crc_calc(); ticv02yylval.label = "SMAXSN1-1"; return ET_SMAXSN1M1; }
-SMAXSN2-1 { crc_calc(); ticv02yylval.label = "SMAXSN2-1"; return ET_SMAXSN2M1; }
-SMAXSN3-1 { crc_calc(); ticv02yylval.label = "SMAXSN3-1"; return ET_SMAXSN3M1; }
-SINSTI { crc_calc(); ticv02yylval.label = "SINSTI"; return ET_SINSTI; }
-SMAXIN { crc_calc(); ticv02yylval.label = "SMAXIN"; return ET_SMAXIN; }
-SMAXIN-1 { crc_calc(); ticv02yylval.label = "SMAXIN-1"; return ET_SMAXINM1; }
-CCASN { crc_calc(); ticv02yylval.label = "CCASN"; return ET_CCASN; }
-CCASN-1 { crc_calc(); ticv02yylval.label = "CCASN-1"; return ET_CCASNM1; }
-CCAIN { crc_calc(); ticv02yylval.label = "CCAIN"; return ET_CCAIN; }
-CCAIN-1 { crc_calc(); ticv02yylval.label = "CCAIN-1"; return ET_CCAINM1; }
-UMOY1 { crc_calc(); ticv02yylval.label = "UMOY1"; return ET_UMOY1; }
-UMOY2 { crc_calc(); ticv02yylval.label = "UMOY2"; return ET_UMOY2; }
-UMOY3 { crc_calc(); ticv02yylval.label = "UMOY3"; return ET_UMOY3; }
-STGE { crc_calc(); ticv02yylval.label = "STGE"; return ET_STGE; }
-DPM1 { crc_calc(); ticv02yylval.label = "DPM1"; return ET_DPM1; }
-FPM1 { crc_calc(); ticv02yylval.label = "FPM1"; return ET_FPM1; }
-DPM2 { crc_calc(); ticv02yylval.label = "DPM2"; return ET_DPM2; }
-FPM2 { crc_calc(); ticv02yylval.label = "FPM2"; return ET_FPM2; }
-DPM3 { crc_calc(); ticv02yylval.label = "DPM3"; return ET_DPM3; }
-FPM3 { crc_calc(); ticv02yylval.label = "FPM3"; return ET_FPM3; }
-MSG1 { crc_calc(); ticv02yylval.label = "MSG1"; return ET_MSG1; }
-MSG2 { crc_calc(); ticv02yylval.label = "MSG2"; return ET_MSG2; }
-PRM { crc_calc(); ticv02yylval.label = "PRM"; return ET_PRM; }
-RELAIS { crc_calc(); ticv02yylval.label = "RELAIS"; return ET_RELAIS; }
-NTARF { crc_calc(); ticv02yylval.label = "NTARF"; return ET_NTARF; }
-NJOURF { crc_calc(); ticv02yylval.label = "NJOURF"; return ET_NJOURF; }
-NJOURF\+1 { crc_calc(); ticv02yylval.label = "NJOURF+1"; return ET_NJOURFP1; }
-PJOURF\+1 { crc_calc(); ticv02yylval.label = "PJOURF+1"; return ET_PJOURFP1; }
-PPOINTE { crc_calc(); ticv02yylval.label = "PPOINTE"; return ET_PPOINTE; }
-
-<DATA>{HORODATE} { crc_calc(); ticv02yylval.text = strdup(yytext); return TOK_HDATE; }
-<DATA>{DATAC}+ { crc_calc(); ticv02yylval.text = strdup(yytext); return TOK_DATA; }
-
-<*>. { if (yy_flex_debug) pr_err("spurious character 0x%02hhx\n", *yytext); return *yytext; }
-
-<<EOF>> { yyterminate(); }
-%%
diff --git a/ticv02.y b/ticv02.y
deleted file mode 100644 (file)
index 6bfccca..0000000
--- a/ticv02.y
+++ /dev/null
@@ -1,228 +0,0 @@
-//
-// ticv02.y
-// A parser for ENEDIS TIC version 02 protocol
-//
-// (C) 2021 Thibaut VARENE
-// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
-//
-
-/**
- * @file
- * This parser implements a complete grammar that supports TIC version 02
- * as specified in Enedis-NOI-CPT_54E.pdf version 3.
- *
- * This parser does not allocate memory, except if a filter configuration is used in
- * which case the etiq_en array will be allocated (it's a few hundred bytes).
- * A left-recursion grammar has been implemented to keep the memory usage to the bare
- * minimum as well. As a tradeoff, valid datasets are always emitted regardless of the
- * overall status of the containing frame.
- */
-
-%{
-#include <stdlib.h>
-#include "tic.h"
-
-int ticv02yylex();
-int ticv02yylex_destroy();
-extern FILE *ticv02yyin;
-static void yyerror(const char *);
-
-extern bool filter_mode;
-extern bool *etiq_en;
-%}
-
-%union {
- char *text;
- const char *label;
- struct tic_etiquette etiq;
- struct tic_field field;
-}
-
-%verbose
-
-%token TOK_STX TOK_ETX TOK_SEP
-%token FIELD_START FIELD_OK FIELD_KO
-%token TICFILTER
-
-%token <text> TOK_HDATE TOK_DATA
-
-%token <label> ET_ADSC ET_VTIC ET_DATE ET_NGTF ET_LTARF
-%token <label> ET_EAST ET_EASF01 ET_EASF02 ET_EASF03 ET_EASF04 ET_EASF05 ET_EASF06 ET_EASF07 ET_EASF08 ET_EASF09 ET_EASF10
-%token <label> ET_EASD01 ET_EASD02 ET_EASD03 ET_EASD04 ET_EAIT ET_ERQ1 ET_ERQ2 ET_ERQ3 ET_ERQ4
-%token <label> ET_IRMS1 ET_IRMS2 ET_IRMS3 ET_URMS1 ET_URMS2 ET_URMS3 ET_PREF ET_PCOUP
-%token <label> ET_SINSTS ET_SINSTS1 ET_SINSTS2 ET_SINSTS3 ET_SMAXSN ET_SMAXSN1 ET_SMAXSN2 ET_SMAXSN3
-%token <label> ET_SMAXSNM1 ET_SMAXSN1M1 ET_SMAXSN2M1 ET_SMAXSN3M1 ET_SINSTI ET_SMAXIN ET_SMAXINM1
-%token <label> ET_CCASN ET_CCASNM1 ET_CCAIN ET_CCAINM1 ET_UMOY1 ET_UMOY2 ET_UMOY3 ET_STGE ET_DPM1 ET_FPM1 ET_DPM2 ET_FPM2 ET_DPM3 ET_FPM3
-%token <label> ET_MSG1 ET_MSG2 ET_PRM ET_RELAIS ET_NTARF ET_NJOURF ET_NJOURFP1 ET_PJOURFP1 ET_PPOINTE
-
-%type <etiq> etiquette etiquette_horodate etiquette_nodate
-%type <field> field field_horodate field_nodate
-
-%destructor { free($$); } <text>
-%destructor { free_field(&$$); } <field>
-%destructor { } <> <*>
-
-%%
-
-start: filter | frames ;
-
-/* filter config only */
-filter:
- TICFILTER etiquettes
-;
-
-etiquettes:
- etiquette { etiq_en[1ドル.tok]=1; }
- | etiquettes etiquette { etiq_en[2ドル.tok]=1; }
- | error { YYABORT; }
-;
-
-etiquette:
- etiquette_horodate
- | etiquette_nodate
-;
-
-/* stream processing */
-frames:
- frame
- | frames frame
-;
-
-frame:
- TOK_STX datasets TOK_ETX { frame_sep(); }
- | error TOK_ETX { frame_err(); frame_sep(); pr_err("frame error\n"); yyerrok; }
-;
-
-datasets:
- error { frame_err(); pr_err("dataset error\n"); }
- | dataset
- | datasets dataset
-;
-
-dataset:
- FIELD_START field FIELD_OK { print_field(&2ドル); free_field(&2ドル); }
- | FIELD_START field FIELD_KO { frame_err(); pr_err("dataset invalid checksum\n"); free_field(&2ドル); }
- | FIELD_START error FIELD_OK { /*not a frame error*/ pr_err("unrecognized dataset\n"); yyerrok; }
-;
-
-field: field_horodate
- | field_nodate
-;
-
-field_horodate:
- etiquette_horodate TOK_SEP TOK_HDATE TOK_SEP TOK_SEP { make_field(&$,ドル &1,ドル 3,ドル NULL); }
- | etiquette_horodate TOK_SEP TOK_HDATE TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル 3,ドル 5ドル); }
-;
-
-field_nodate:
- etiquette_nodate TOK_SEP TOK_DATA TOK_SEP { make_field(&$,ドル &1,ドル NULL, 3ドル); }
-;
-
-etiquette_horodate:
- ET_DATE { $$.tok=yytranslate[ET_DATE]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Date et heure courante"; }
- | ET_SMAXSN { $$.tok=yytranslate[ET_SMAXSN]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n"; }
- | ET_SMAXSN1 { $$.tok=yytranslate[ET_SMAXSN1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 1"; }
- | ET_SMAXSN2 { $$.tok=yytranslate[ET_SMAXSN2]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 2"; }
- | ET_SMAXSN3 { $$.tok=yytranslate[ET_SMAXSN3]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n phase 3"; }
- | ET_SMAXSNM1 { $$.tok=yytranslate[ET_SMAXSNM1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1"; }
- | ET_SMAXSN1M1 { $$.tok=yytranslate[ET_SMAXSN1M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 1"; }
- | ET_SMAXSN2M1 { $$.tok=yytranslate[ET_SMAXSN2M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 2"; }
- | ET_SMAXSN3M1 { $$.tok=yytranslate[ET_SMAXSN3M1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max soutirée n-1 phase 3"; }
- | ET_SMAXIN { $$.tok=yytranslate[ET_SMAXIN]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max injectée n"; }
- | ET_SMAXINM1 { $$.tok=yytranslate[ET_SMAXINM1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. max injectée n-1"; }
- | ET_CCASN { $$.tok=yytranslate[ET_CCASN]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n de la courbe de charge active soutirée"; }
- | ET_CCASNM1 { $$.tok=yytranslate[ET_CCASNM1]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n-1 de la courbe de charge active soutirée"; }
- | ET_CCAIN { $$.tok=yytranslate[ET_CCAIN]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n de la courbe de charge active injectée"; }
- | ET_CCAINM1 { $$.tok=yytranslate[ET_CCAINM1]; $$.unittype=U_W; $$.label=1ドル; $$.desc="Point n-1 de la courbe de charge active injectée"; }
- | ET_UMOY1 { $$.tok=yytranslate[ET_UMOY1]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 1"; }
- | ET_UMOY2 { $$.tok=yytranslate[ET_UMOY2]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 2"; }
- | ET_UMOY3 { $$.tok=yytranslate[ET_UMOY3]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension moy. ph. 3"; }
- | ET_DPM1 { $$.tok=yytranslate[ET_DPM1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 1"; }
- | ET_FPM1 { $$.tok=yytranslate[ET_FPM1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 1"; }
- | ET_DPM2 { $$.tok=yytranslate[ET_DPM2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 2"; }
- | ET_FPM2 { $$.tok=yytranslate[ET_FPM2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 2"; }
- | ET_DPM3 { $$.tok=yytranslate[ET_DPM3]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Début Pointe Mobile 3"; }
- | ET_FPM3 { $$.tok=yytranslate[ET_FPM3]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Fin Pointe Mobile 3"; }
-;
-
-etiquette_nodate:
- ET_ADSC { $$.tok=yytranslate[ET_ADSC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Adresse Secondaire du Compteur"; }
- | ET_VTIC { $$.tok=yytranslate[ET_VTIC]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Version de la TIC"; }
- | ET_NGTF { $$.tok=yytranslate[ET_NGTF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Nom du calendrier tarifaire fournisseur"; }
- | ET_LTARF { $$.tok=yytranslate[ET_LTARF]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Libellé tarif fournisseur en cours"; }
- | ET_EAST { $$.tok=yytranslate[ET_EAST]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée totale"; }
- | ET_EASF01 { $$.tok=yytranslate[ET_EASF01]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 01"; }
- | ET_EASF02 { $$.tok=yytranslate[ET_EASF02]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 02"; }
- | ET_EASF03 { $$.tok=yytranslate[ET_EASF03]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 03"; }
- | ET_EASF04 { $$.tok=yytranslate[ET_EASF04]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 04"; }
- | ET_EASF05 { $$.tok=yytranslate[ET_EASF05]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 05"; }
- | ET_EASF06 { $$.tok=yytranslate[ET_EASF06]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 06"; }
- | ET_EASF07 { $$.tok=yytranslate[ET_EASF07]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 07"; }
- | ET_EASF08 { $$.tok=yytranslate[ET_EASF08]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 08"; }
- | ET_EASF09 { $$.tok=yytranslate[ET_EASF09]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 09"; }
- | ET_EASF10 { $$.tok=yytranslate[ET_EASF10]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Fournisseur, index 10"; }
- | ET_EASD01 { $$.tok=yytranslate[ET_EASD01]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 01"; }
- | ET_EASD02 { $$.tok=yytranslate[ET_EASD02]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 02"; }
- | ET_EASD03 { $$.tok=yytranslate[ET_EASD03]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 03"; }
- | ET_EASD04 { $$.tok=yytranslate[ET_EASD04]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active soutirée Distributeur, index 04"; }
- | ET_EAIT { $$.tok=yytranslate[ET_EAIT]; $$.unittype=U_WH; $$.label=1ドル; $$.desc="Energie active injectée totale"; }
- | ET_ERQ1 { $$.tok=yytranslate[ET_ERQ1]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q1 totale"; }
- | ET_ERQ2 { $$.tok=yytranslate[ET_ERQ2]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q2 totale"; }
- | ET_ERQ3 { $$.tok=yytranslate[ET_ERQ3]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q3 totale"; }
- | ET_ERQ4 { $$.tok=yytranslate[ET_ERQ4]; $$.unittype=U_VARH; $$.label=1ドル; $$.desc="Energie réactive Q4 totale"; }
- | ET_IRMS1 { $$.tok=yytranslate[ET_IRMS1]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 1"; }
- | ET_IRMS2 { $$.tok=yytranslate[ET_IRMS2]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 2"; }
- | ET_IRMS3 { $$.tok=yytranslate[ET_IRMS3]; $$.unittype=U_A; $$.label=1ドル; $$.desc="Courant efficace, phase 3"; }
- | ET_URMS1 { $$.tok=yytranslate[ET_URMS1]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 1"; }
- | ET_URMS2 { $$.tok=yytranslate[ET_URMS2]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 2"; }
- | ET_URMS3 { $$.tok=yytranslate[ET_URMS3]; $$.unittype=U_V; $$.label=1ドル; $$.desc="Tension efficace, phase 3"; }
- | ET_PREF { $$.tok=yytranslate[ET_PREF]; $$.unittype=U_KVA; $$.label=1ドル; $$.desc="Puissance app. de référence (PREF)"; }
- | ET_PCOUP { $$.tok=yytranslate[ET_PCOUP]; $$.unittype=U_KVA; $$.label=1ドル; $$.desc="Puissance app. de coupure (PCOUP)"; }
- | ET_SINSTS { $$.tok=yytranslate[ET_SINSTS]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée"; }
- | ET_SINSTS1 { $$.tok=yytranslate[ET_SINSTS1]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 1"; }
- | ET_SINSTS2 { $$.tok=yytranslate[ET_SINSTS2]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 2"; }
- | ET_SINSTS3 { $$.tok=yytranslate[ET_SINSTS3]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée soutirée phase 3"; }
- | ET_SINSTI { $$.tok=yytranslate[ET_SINSTI]; $$.unittype=U_VA; $$.label=1ドル; $$.desc="Puissance app. Instantannée injectée"; }
- | ET_STGE { $$.tok=yytranslate[ET_STGE]; $$.unittype=U_SANS|T_HEX; $$.label=1ドル; $$.desc="Registre de Statuts"; }
- | ET_MSG1 { $$.tok=yytranslate[ET_MSG1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Message court"; }
- | ET_MSG2 { $$.tok=yytranslate[ET_MSG2]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Message Ultra court"; }
- | ET_PRM { $$.tok=yytranslate[ET_PRM]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="PRM"; }
- | ET_RELAIS { $$.tok=yytranslate[ET_RELAIS]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Relais"; }
- | ET_NTARF { $$.tok=yytranslate[ET_NTARF]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro de l'index tarifaire en cours"; }
- | ET_NJOURF { $$.tok=yytranslate[ET_NJOURF]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro du jour en cours calendrier fournisseur"; }
- | ET_NJOURFP1 { $$.tok=yytranslate[ET_NJOURFP1]; $$.unittype=U_SANS; $$.label=1ドル; $$.desc="Numéro du prochain jour calendrier fournisseur"; }
- | ET_PJOURFP1 { $$.tok=yytranslate[ET_PJOURFP1]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Profil du prochain jour calendrier fournisseur"; }
- | ET_PPOINTE { $$.tok=yytranslate[ET_PPOINTE]; $$.unittype=U_SANS|T_STRING; $$.label=1ドル; $$.desc="Profil du prochain jour de pointe"; }
-;
-
-
-%%
-
-#ifndef BAREBUILD
-void parse_config_v02(const char *filename)
-{
- if (!(ticv02yyin = fopen(filename, "r"))) {
- perror(filename);
- exit(-1);
- }
-
- etiq_en = calloc(YYNTOKENS, sizeof(*etiq_en));
- if (!etiq_en)
- abort(); // OOM
-
- filter_mode = true;
- if (ticv02yyparse()) {
- pr_err("%s: filter config error!\n", filename);
- exit(-1);
- }
-
- fclose(ticv02yyin);
- ticv02yylex_destroy();
- ticv02yyin = stdin;
- filter_mode = false;
-}
-#endif /* !BAREBUILD */
-
-static void yyerror(const char * s)
-{
-}
tic2json TIC parser/converter
RSS Atom

AltStyle によって変換されたページ (->オリジナル) /