diff --git a/.gitignore b/.gitignore index 8ed37e6..3480d38 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,25 @@ a.out *.o fake-values essai +foo.dat serial/t -doc/*.toc doc/*.log doc/*.aux doc/*.pdf +core/t +core/*.a +doc/*.toc +doc/*.log +doc/*.aux +doc/*.pdf +doc/*.idx +doc/*.ilg +doc/*.ind +*/*.dat +rrdb/*.png +serial/*.png + +viz/curses/t +viz/gnuplot/*.png +viz/*.a diff --git a/BUILD.txt b/BUILD.txt new file mode 100644 index 0000000..cb4ef81 --- /dev/null +++ b/BUILD.txt @@ -0,0 +1,26 @@ + +--------------------------------+ + | how to build the dd2-monitor ? | + +--------------------------------+ + +First step, build some parts of the core library : + + $ cd core + $ make t + $ ./t -v + $ cd .. + +Then you must have datas for working on. One source of datas +is the four values who came from the Arduino over serial line. +At this time, you must look at the configuration file. + + $ cd serial + $ make t + +Have a look at the 'essai.sh' file for an example of simple +usage of the serial input. For generating all that numbers, +you have to run some code on the Arduino mega. Sources are +in the simulator/ folder. + + + + diff --git a/Makefile b/Makefile index 0fe9533..fd68c20 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,20 @@ # must be run with gnu make # -CC = gcc - -CCOPT = -Wall -g +CC = gcc +CCOPT = -Wall -g +CLIB = core/libdd2m-core.a viz/libdd2m-viz.a all: essai fake-values -essai: essai.c funcs.o Makefile - gcc ${CCOPT} $< funcs.o -o $@ +# --------------------------------------------- +O = serial/serial.o serial/funcs.o -funcs.o: funcs.c funcs.h Makefile - gcc ${CCOPT} -c $< +essai: essai.c Makefile $(CLIB) + $(CC) ${CCOPT} $< $(CLIB) ${O} -lncurses -o $@ -fake-values: fake-values.c funcs.o Makefile - gcc ${CCOPT} $< funcs.o -o $@ +fake-values: fake-values.c Makefile $(CLIB) + $(CC) ${CCOPT} $< $(CLIB) -o $@ +# --------------------------------------------- diff --git a/README.md b/README.md index 315e02f..aeb68b7 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,35 @@ de _monitoring_ pour le futur Phytotron du Tetalab. Le but premier de ce système est de faciliter la mise au point d'un automate de contrôle de l'enceinte thermostatée. Nous devrons +<<<<<<< HEAD surveiller température et humidité du dd2, et température du confinement biologique. +======= +surveiller température et humidité du dd2, +éclairage et température du confinement biologique. +>>>>>>> b47e467d21cec6ee688b4407d3ec54fa33e67ba6 Pour en savoir plus sur ce passionnant projet, il y a le canal IRC `#tetalab` sur le réseau Freenode et/ou les rencontres du mercredi soir au DD2, à Mixart-Myrys. +<<<<<<< HEAD +======= +Et aussi https://pad.tetalab.org/p/dd2-monitoring + +# WTF status + +Seriazl : Le `read` bloquant ne bloque pas. Ça craint grave. La recherche +du workaround avance. + +Premier essai RRD : ça marchote... + + + + + + +>>>>>>> b47e467d21cec6ee688b4407d3ec54fa33e67ba6 diff --git a/core/Makefile b/core/Makefile new file mode 100644 index 0000000..778f052 --- /dev/null +++ b/core/Makefile @@ -0,0 +1,36 @@ +# +# dd2 monitoring +# +# build the core functions, use with care +# + + +COPT = -Wall -fpic -g -DDEBUG_LEVEL=0 +OBJS = lut1024f.o parseconf.o utils.o sysmetrics.o +DEPS = Makefile +ALIB = libdd2m-core.a +# --------------------------------------------------- + +${ALIB}: ${OBJS} + ar r $@ $? + +lut1024f.o: lut1024f.c lut1024.h ${DEPS} + gcc $(COPT) -c $< + +parseconf.o: parseconf.c config.h ${DEPS} + gcc $(COPT) -c $< + +utils.o: utils.c utils.h ${DEPS} + gcc $(COPT) -c $< + +sysmetrics.o: sysmetrics.c ${DEPS} + gcc $(COPT) -c $< + +# --------------------------------------------------- + +t: t.c ${ALIB} lut1024.h config.h utils.h ${DEPS} + gcc -Wall $< ${ALIB} -o $@ + +foo.lut1024f: mklut.pl Makefile + ./mklut.pl quux > $@ + diff --git a/core/config.h b/core/config.h new file mode 100644 index 0000000..25fdbe9 --- /dev/null +++ b/core/config.h @@ -0,0 +1,24 @@ +/* + * config.h + */ + +#define SZ_STRINGS 200 + +typedef struct { + + int valid; + + char *input_device; + int input_speed; + + char *eyecandy_banner; + + } Configuration; + +/* ---------------------------------------------------------------- */ + +int set_default_config(Configuration *cfg); +int parse_config(char *fname, int flags); +int show_config(char *title); + +/* ---------------------------------------------------------------- */ diff --git a/core/dd2-monitor.conf b/core/dd2-monitor.conf new file mode 100644 index 0000000..494e4a3 --- /dev/null +++ b/core/dd2-monitor.conf @@ -0,0 +1,16 @@ +# +# experimental config file +# + +# -------------------------------------------------- +# serial input from the control cpu + +input_device s /dev/ttyACM0 +input_speed i 9600 + +# -------------------------------------------------- +# some values for the eyecandy displays + +eyecandy_banner s hacked by tTh + +# -------------------------------------------------- diff --git a/core/lut1024.h b/core/lut1024.h new file mode 100644 index 0000000..f271685 --- /dev/null +++ b/core/lut1024.h @@ -0,0 +1,17 @@ +/* + * LUT 1024 - DEALING WITH DISCRETE VALUES + */ + + +typedef struct { + int flags; + float vals[1024]; + } Lut1024f; + +/* ---------------------------------------------------------------- */ + +int slurp_lut1024f(FILE *fp, Lut1024f *where, int notused); +int load_lut1024f(char *fname, Lut1024f *where, int notused); + +/* ---------------------------------------------------------------- */ + diff --git a/core/lut1024f.c b/core/lut1024f.c new file mode 100644 index 0000000..59dadb2 --- /dev/null +++ b/core/lut1024f.c @@ -0,0 +1,70 @@ +/* + * LUT 1024 -> FLOAT + */ + +#include +#include +#include + +#include "lut1024.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ + +int slurp_lut1024f(FILE *fp, Lut1024f *where, int notused) +{ +int count, foo; + +for(count=0; count<1024; count++) { + foo = fscanf(fp, "%f", &where->vals[foo]); + if (1 != foo) { + fprintf(stderr, "%s: bad read %d\n", __func__, foo); + return -4; + } + } + +return 0; +} + +/* ---------------------------------------------------------------- */ + +int load_lut1024f(char *fname, Lut1024f *where, int notused) +{ +FILE *fplut; +char firstline[100]; +char label[] = "LUT1024F"; +int foo; + +#if DEBUG_LEVEL +fprintg(stderr, ">>> %s ( '%s' %p %d )\n", __func__, + fname, where, notused); +#endif + +if (NULL==(fplut=fopen(fname, "r"))) { + perror(fname); + return -2; + } + +fprintf(stderr, "%s: getting first line\n", __func__); + +if (NULL==fgets(firstline, 20, fplut)) { + fprintf(stderr, "%s: nothing to read from %s\n", + __func__, fname); + return -3; + } + +foo = strncmp(label, firstline, sizeof(label)-1); +if (foo) { + fprintf(stderr, "%s: bad label [%s]\n", __func__, firstline); + exit(5); + } + +foo = slurp_lut1024f(fplut, where, 0); + +fclose(fplut); + +return -1; +} + +/* ---------------------------------------------------------------- */ diff --git a/core/mklut.pl b/core/mklut.pl new file mode 100755 index 0000000..d0f1892 --- /dev/null +++ b/core/mklut.pl @@ -0,0 +1,10 @@ +#!/usr/bin/perl + +my $foo; + +print "LUT1024F\n"; + +for ($foo=0; $foo<1024; $foo++) { + print rand()*3.30, "\n"; + } +0; \ No newline at end of file diff --git a/core/parseconf.c b/core/parseconf.c new file mode 100644 index 0000000..bce5c44 --- /dev/null +++ b/core/parseconf.c @@ -0,0 +1,112 @@ +/* + * core/parseconf.c + */ + +#include +#include +#include + +#include "config.h" + +extern int verbosity; +extern Configuration config; + +#define CMP(a) (!strcmp(keyptr, a)) + +/* ---------------------------------------------------------------- */ +int parse_config(char *fname, int flags) +{ +FILE *fp; +char line[SZ_STRINGS+1], + *keyptr, *typeptr, *cptr; +int numligne; + +#if DEBUG_LEVEL +fprintf(stderr, ">>> %s ( '%s' $%x )\n", fname, flags); +#endif + +config.valid = 49; + +if (NULL==(fp=fopen(fname, "r"))) { + perror(fname); + return -2; + } + +numligne = 0; + +while (fgets(line, SZ_STRINGS, fp)) + { + numligne++; + if ('\0'==line[0]) { + fprintf(stderr, "%s : short read line %d\n", + fname, numligne); + fclose(fp); + return -1; + } + + /* massage the end of line */ + line[strlen(line)-1] = '\0'; /* kill EOL */ + if (verbosity) { + fprintf(stderr, "%3d :\t%s\n", numligne, line); + } + + /* seek for the first token in this line */ + if (NULL==(keyptr = strtok(line, " \t"))) { + /* Got an empty line */ + continue; + } + /* skip comments */ + if ('#'==*keyptr) continue; + /* seek for the type field */ + if (NULL==(typeptr = strtok(NULL, " \t"))) { + /* we can(t get a type flag ? wtf ? */ + fprintf(stderr, "ERROR line %d : no type\n", numligne); + continue; + } + + if(verbosity) + fprintf(stderr, "[%s] type %s\n", keyptr, typeptr); + + if CMP("abort") { + fprintf(stderr, "abort in config file\n"); + } + + if (CMP("input_device")) { + config.input_device = strdup(strtok(NULL, " \t")); + continue; + } + + if (CMP("input_speed")) { + config.input_speed = atoi(strtok(NULL, " \t")); +#if DEBUG_LEVEL + fprintf(stderr, "input speed = %d\n", config.input_speed); +#endif + } + + if (CMP("eyecandy_banner")) { + config.eyecandy_banner = strdup(strtok(NULL, " \t")); + continue; + } + + } + +fclose(fp); + +return 0; +} +/* ---------------------------------------------------------------- */ +int show_config(char *title) +{ + +if (verbosity) { + printf("********** %s **********\n", title); + } + +printf("valid : %d\n", config.valid); +printf("input device : %s\n", config.input_device); +printf("input speed : %d\n", config.input_speed); + +puts(""); +return 0; +} +/* ---------------------------------------------------------------- */ diff --git a/core/sysmetrics.c b/core/sysmetrics.c new file mode 100644 index 0000000..032da13 --- /dev/null +++ b/core/sysmetrics.c @@ -0,0 +1,38 @@ +/* + * core/sysmetrics.c + */ + +#include +#include +#include +#include +#include + +#include "sysmetrics.h" + +extern int verbosity; + +/* --------------------------------------------------------------- */ + +int get_loadavg(float *where) +{ +FILE *fp; +float loads[3]; +int foo; + +if ( ! (fp=fopen("/proc/loadavg", "r")) ) { + perror("read loadavg"); + return -1; + } + +foo = fscanf(fp, "%f %f %f", loads, loads+1, loads+2); +if (3 != foo) fprintf(stderr, "%s : read %d vals\n", __func__, foo); + +memcpy(where, loads, 3 * sizeof(float)); + +fclose(fp); + +return 0; +} + +/* --------------------------------------------------------------- */ diff --git a/core/sysmetrics.h b/core/sysmetrics.h new file mode 100644 index 0000000..bb99b15 --- /dev/null +++ b/core/sysmetrics.h @@ -0,0 +1,4 @@ + + +int get_loadavg(float *where); + diff --git a/core/t.c b/core/t.c new file mode 100644 index 0000000..5fbeda1 --- /dev/null +++ b/core/t.c @@ -0,0 +1,73 @@ +/* + * main de test des core functions + */ + +#include +#include +#include + +#include "lut1024.h" +#include "config.h" +#include "sysmetrics.h" + +int verbosity; + +Configuration config; + + +/* ---------------------------------------------------------------- */ +int essai_sysmetrics(int k) +{ +float fvalues3[3]; +int foo; + +foo = get_loadavg(fvalues3); +if (foo) { + fprintf(stderr, "err get load avg %d\n", foo); + return -1; + } + +printf("load avg %f %f %f\n", fvalues3[0], fvalues3[1], fvalues3[2]); + +return 0; +} +/* ---------------------------------------------------------------- */ + +int main (int argc, char *argv[]) +{ +int foo, opt; +char *conffile = "dd2-monitor.conf"; + +fprintf(stderr, "+\n+ DD2 MONITOR\n+\n"); + +/* set some default values */ +verbosity = 0; + + +while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': verbosity++; break; + + default: + fprintf(stderr, "%s : uh ?", argv[0]); + exit(1); + break; + } + + } + + +foo = parse_config(conffile, 0); +fprintf(stderr, "parse_config(%s) -> %d\n\n", conffile, foo); +show_config("foo"); + +essai_sysmetrics(0); +/* +foo = load_lut1024f("foo.lut1024f", NULL, 1); +fprintf(stderr, "chargement de la lut --> %d\n\n", foo); +*/ + +return 0; +} + +/* ---------------------------------------------------------------- */ diff --git a/funcs.c b/core/utils.c similarity index 64% rename from funcs.c rename to core/utils.c index df4af6a..7eef636 100644 --- a/funcs.c +++ b/core/utils.c @@ -1,5 +1,5 @@ /* - * funcs.c + * core/utils.c */ #include @@ -9,8 +9,6 @@ #include #include -#include "funcs.h" - extern int verbosity; /* --------------------------------------------------------------- */ @@ -25,9 +23,11 @@ return v1 ^ v2; /* --------------------------------------------------------------- */ int random1000(int type) { -int value; -if (verbosity) - fprintf(stderr, ">>> %s(%d)\n", __func__, type); +int value, foo; + +#if DEBUG_LEVEL > 1 +fprintf(stderr, ">>> %s(%d)\n", __func__, type); +#endif switch (type) { case 0: @@ -36,6 +36,12 @@ switch (type) { case 1: value = (rand()%1000 + rand()%1000) / 2; break; + case 4: + value = 0; + for (foo=0; foo<4; foo++) + value += rand() % 1000; + value /= 4; + break; default: value = -1; break; @@ -43,9 +49,7 @@ switch (type) { return value; } - /* --------------------------------------------------------------- */ - double dtime(void) { struct timeval tv; @@ -56,28 +60,4 @@ if (foo) fprintf(stderr, "got %d in %s\n", foo, __func__); return (double)tv.tv_sec + (double)tv.tv_usec / 1e6; } - -/* --------------------------------------------------------------- */ - -int get_loadavg(double *where) -{ -FILE *fp; -double loads[3]; -int foo; - -if ( ! (fp=fopen("/proc/loadavg", "r")) ) { - perror("read loadavg"); - return -1; - } - -foo = fscanf(fp, "%lf %lf %lf", loads, loads+1, loads+2); -if (3 != foo) fprintf(stderr, "%s : read %d vals\n", __func__, foo); - -memcpy(where, loads, 3 * sizeof(double)); - -fclose(fp); - -return 0; -} - /* --------------------------------------------------------------- */ diff --git a/core/utils.h b/core/utils.h new file mode 100644 index 0000000..b71651c --- /dev/null +++ b/core/utils.h @@ -0,0 +1,8 @@ +/* + * core/utils.h + */ + +int seed_my_rand(int foo); +int random1000(int type); + +double dtime(void); diff --git a/core/wtf.txt b/core/wtf.txt new file mode 100644 index 0000000..64f950a --- /dev/null +++ b/core/wtf.txt @@ -0,0 +1,7 @@ + + + +Il est temps de se reprendre en main + + + diff --git a/doc/automate.tex b/doc/automate.tex new file mode 100644 index 0000000..b7882c8 --- /dev/null +++ b/doc/automate.tex @@ -0,0 +1,81 @@ +\section{Automate} \index{automate} \label{automate} + +L'automate qui contrôle les fonctions vitales des petites +\textit{Pyrocystis Fusiformis} est basé sur un Arduino Mega\index{mega}, +qui a été choisi pour sa profusion d'entrées sorties\index{i/o}. +Il va remplir plusieurs fonctions mises au point les unes et les autres. + +Dans un premier temps, le logiciel sera développé avec l'\textsl{IDE} +standard de l'Arduiono, malgré ses limitations. +Il existe des solutions alternatives à base de \texttt{makefile}, que +nous découvrirons un jour, si le besoin s'en fait sentir. + +Ce lo + +% ------------------------------------------------------------------- +\subsection{Dialogue} + +Le but étant d'avoir un système +\textsl{standalone}\footnote{lowcost and easytouse ?}, le dialogue +avec les humains extérieur sera plus que limité en fonctionnement +courant. + +Pour la configuration, nous allons créer une \textsc{CLI}\index{cli} +rudimentaire qui sera accessible par le port USB et un émulateur +de terminal\footnote{Minicom, Putty...}. + +% ------------------------------------------------------------------- +\subsection{Température} \index{temperature} + +Les capteurs utilisés sont des LM35\index{LM35}, à sortie analogique +entre 0 et 5v pour une gamme de température de 0 à ??? degrés Celsius. + +Le convertisseur \small{A/D} a une résolution de 10 bits, qu'il +est possible d'augmenter en changeant son Vref, soit pour une +référence externe, soit pour une référence interne à 1.1 V. + +% ------------------------------------------------------------------- +\subsection{Voyants} + +Il faut \textbf{toujours} intégrer dans un projet une gestion avancée +des \textsc{led}\index{LED} qui clignotent avec entrain pour raconter +la vie interne de la machinerie. + +Une paire Rouge/Bleue indiquera les sorties de l'intervalle +de température pré-programmé. +Une \textsc{led} orange clignotante indiquera une erreur +en s'exprimant en code Morse. + +Quand à la \textsc{led} verte, elle sera là pour ne rien dire de précis, +mais avec brio. + +% ------------------------------------------------------------------- +\subsection{Chauffage} \index{chauffage} + +En première approche, un choix simple a été fait~: nous allons +utiliser un chauffage intégré d'aquarium en de basant d'abord +sur son thermostat intégré. + +Si ce n'est pas satisfaisant, pour diverses raisons (matériel +chinois à 3 balles, par exemple), +nous serons obligés de commander nous-même ce chauffage. + +C'est à ce moment que les choses deviennent sérieuses, il va +falloir commuter du 230v, tension assez mortelle dans les faits. +Une solution à base d'opto-coupleur semble s'imposer. + +% ------------------------------------------------------------------- +\subsection{Refroidissement}. + +Puisque l'enceinte choisi est un frigorifique de +récupération\footnote{Merci DomDom :)}, nous pourrons utiliser son +groupe froid. Comme pour le chauffage, nous sommes sur du 230v. + +% ------------------------------------------------------------------- + + + + + + + diff --git a/doc/dd2-monitoring.tex b/doc/dd2-monitoring.tex index be5ec16..dce648c 100644 --- a/doc/dd2-monitoring.tex +++ b/doc/dd2-monitoring.tex @@ -1,5 +1,4 @@ - -\documentclass[a4paper,12pt]{article} +\documentclass[a4paper,11pt]{article} % \listfiles % pour le debug @@ -8,13 +7,14 @@ \usepackage[T1]{fontenc} % XXX \usepackage{lipsum} \usepackage{makeidx} +\usepackage{listings} % \usepackage{color} % \usepackage{url} \usepackage{xspace} \usepackage[verbose]{layout} \makeindex -\setlength{\parskip}{0.25cm plus 0.25cm} +% \setlength{\parskip}{0.16cm plus 0.16cm} % ------------------------------------------------------------------- \title{DD2 Monitoring} @@ -25,6 +25,12 @@ \pagebreak +% \setlength{\parskip}{0.01cm plus 0.01cm} +\tableofcontents +% \setlength{\parskip}{0.16cm plus 0.16cm} + +\pagebreak + % ------------------------------------------------------------------- \section{Introduction} @@ -41,45 +47,85 @@ arrivons parfois à le faire. La première étape sera donc la mise au point d'un \textsl{cadriciel} permettant d'essayer diverses options en se basant sur une conception -modulaire de l'ensemble. +modulaire de l'ensemble. Une grosse partie sera écrite en C\index{C}, +avec d'éventuels modules en langages de script : +Perl\index{Perl}, Awk\index{Awk}, Bash\index{Bash}\dots Les valeurs à mesurer étant de diverses natures, nous aurons l'occasion de découvrir plein de capteurs différents : température, humidité, \texttt{pH}, transparence, lumière, vibrations\dots -% ------------------------------------------------------------------- +\vspace{4cm} + +Bonne lecture. + +% ------------------------------------------------------------------- +% =================================================================== + +\pagebreak \section{Présentation générale} \subsection{Capteurs} \index{capteurs} -Actuellement nous disposons d'un \texttt{RDing TEMPERHUM1V1.2}{} qui +Actuellement nous disposons d'un capteur température et +humidité, le +\texttt{RDing} \texttt{TEMPERHUM1V1.2}{} qui semble un peu étrange à interpeller, et qui sera destiné à mesurer -l'ambiance météo du DD2\footnote{Pas de mauvais esprit, merci...}. +l'ambiance météo du Double Dragon\footnote{Pas de mauvais esprit, merci...}, +l'influence humaine n'étant pas à négliger en cas d'afflux du public.. -Par la suite, nous pourrons récupérer (par liaison série ?) des données -en provenance de l'automate de contrôle de l'enceinte. Notre dd2monitor +Par la suite, nous pourrons récupérer +(par liaison série, cf page \pageref{serial}) +des données diverses en provenance de l'automate de contrôle de l'enceinte. +Cet automate\index{automate} est décrit page \pageref{automate}. + +Le premier capteur de température sélectionné est le \textsc{LM35}\index{LM35} +qui fournit en sortie une tension linéairement proportionnle à +la température. Ils seront connectés sur l'automate qui s'en +servira pour la régulation thermostatique. + +Il nous reste à choisir d'autres capteurs pour d'autres métriques : +humidité, lumière, vibrations, perturbations psychiques\dots + +Notre dd2monitor devrait donc aussi être capable d'envoyer des alertes en cas de souci, -par exemple si des algues\footnote{ou des pleurotes.} tentent de s'échapper. +par exemple si des algues\footnote{Ou des pleurotes, ou des morilles...} +tentent de s'échapper. \subsection{Stockage} Pour entreposer toutes ces valeurs numériques, il existe plusieurs -choix, et nous allons en évaluer quelques uns : -\texttt{rrdb}\index{rrdb}, -\texttt{influxdb}\index{influxdb} (page \pageref{influxdb}), -\texttt{gnocchi}\index{gnocchi}... +choix, et nous allons en évaluer quelques uns~: + +\texttt{flatfile}\index{flatfile} (page \pageref{flatfile}), +\texttt{rrdb}\index{rrdb} (page \pageref{rrdb}), +\texttt{influxdb}\index{Influxdb} (page \pageref{influxdb}), +\texttt{gnocchi}\index{Gnocchi} (page \pageref{gnocchi}) +\texttt{Sqlite}\index{sqlite} (page \pageref{sqlite}) + +La représentation interne des valeurs reste à définir pour +la plupart d'entre elles. \subsection{Affichage} Nous allons laisser un petit bac-à-sable pour Fred Fermion\index{nodejs}, -qui nous tartine les oreilles depuis trop longtemps avec son machinjs. +qui nous tartine les oreilles depuis bien trop longtemps avec son machin.js. Mais sachez déja que l'automate sera équipé d'un minitel, pourquoi ne -pas en mettre un second sur le monitoring ? +pas en mettre un second sur le monitoring ? Après tout, un peu +d'eyecandy\index{eyecandy} ne peut pas faire de mal. +C'est expliqué à la page \pageref{eyecandy}\index{curses}. % ------------------------------------------------------------------- +% =================================================================== +% nouveau 2019-02-22 + + \input{automate} + +% =================================================================== + \section{Outils} \subsection{Simulations} @@ -90,63 +136,174 @@ phytotron pose un problème : d'où viennent les premières mesures ? C'est pour ça qu'il y a déja un générateur de \textit{fake-values} qui ne demande qu'à grandir. +Nous avons également un générateur de nombres divers et incohérents +qui envoie des quadruplets d'entiers 10 bits précédés d'un caractere +de bonne efficacité. + +% ------------------------------------------------------------------- \subsection{Exemples} -Promis, on va en mettre ! +\textsf{Promis, on va en mettre ! Dès que ça marche\dots} + +Un premier exemple avec rrdb en page \pageref{rrdb}. +Le second cause des premiers essais du LM35\index{LM35}, +capteur de +température analogique branché sur un Arduino Mega et relié +par un port série (page \pageref{serialcode}). + +Pour continuer dans une démarche disruptive, des outils +avancés de visualisation sont proposés dans la rubrique +\ref{eyecandy} qui parle de \texttt{vt100}\label{vt100}. + +% ------------------------------------------------------------------- \subsection{Analyses} -Bla bla bla\dots Corrélations, toussa\dots +\textsf{Bla bla bla\dots Corrélations, Gnuplot\index{gnuplot}, toussa\dots} Peut-être demander à Schmod777 des références de -documents bourbakistes ? +documents bourbakistes ? J'ai entendu parler dans \textsc{irc} +de choses étranges, comme ça : +\textsl{ s/booz/booze pour la courbe qui majore les autres en moyenne serait +d'une criante justesse scientifique ;)} +Ce qui donne quand même à réfléchir. À se demander quel savoir +allons-nous pouvoir déduire de ces\footnote{non, c'est pas du bigdata.} +chiffres improbables. +(Re-)Découvrir les lois de l'inertie thermique ? +Générer des formes d'ondes spatialisables ? +Déplacer des petites \textit{bubulles} colorées ? +Une histoire pour la section \pageref{detournements} ? % ------------------------------------------------------------------- -\section{InfluxDB} \label{influxdb} +\subsection{Archivage} \index{archivage} \label{archivage} -Au programme : écriture d'un injecteur en Perl\index{Perl}. +\textsf{Conserver la mémoire de nos échecs.} +La création de cet outil de surveillance est un long parcours +pavé d'essais et d'erreurs. Tout cela peut générer beaucoup +de données. À titre d'exemple, la capture de température des +premier essais sort environ 800 Ko par jour. +99.99\% de ces chiffres sont inutiles, mais il peut arriver +qu'on désire conserver l'historique d'une +expérience réussie\footnote{En fait, c'est comme ça que la science existe} +ou d'un +\textit{epicfail\footnote{En fait, c'est comme ça que la science avance}}. + +Nous devons donc rencontrer quelqu'un qui maitrise cette partie +de la mouvance détournementale de l'espionnage. +Je pense que Yaya\index{Yaya} pourra nous éclairer de ses lumières, +si on lui demande gentiment. % ------------------------------------------------------------------- -\section{Serial coms} \index{serial} +\subsection{Affichage}\index{affichage} -Il va y avoir deux liaisons série entre l'automate et le monitoring. -La première, que nous allons juste entrevoir, passera par le port -USB de la carte Arduino. La seconde passera par un port série -auxiliaire\footnote{Il y en a 4 sur le 2560} de celle-ci. +Pour commencer presque simple, un exemple d'affichage avec +Gnuplot\index{gnuplot} d'un fichier plat +de température (décrit page \pageref{foo.dat}) : +\begin{verbatim} +DATAFILE="foo.dat" +IMAGE="graphe.png" + +gnuplot << __EOC__ +set term png size 4200,640 +set output "${IMAGE}" +set grid +set title "Temperature dans le Double Dragon 2" +set xdata time +set timefmt "%s" +set format x "%d, %H:%M:%S" +set yrange [ 0.0 : 30.0] +plot "${DATAFILE}" using 1:2 title " foo" with lines, \ + "${DATAFILE}" using 1:3 title " bar" with lines, \ + "${DATAFILE}" using 1:4 title "quux" with lines, \ + "${DATAFILE}" using 1:5 title "booz" with lines +__EOC__ + +\end{verbatim} + +Prochainement, dès que le premier prototype matériel fournira des +données, une tentative de visualisation animée sera faite avec +POVray\index{POV}. + + +% =================================================================== + +\section{Configuration} \index{configuration} \label{configuration} + +Pour adapter cet outil de surveillance aux variations du monde réel, +nous devons nous-même lui décrire ce monde. Une description qui se +fera avec des lignes de la forme '\texttt{input\_device s /dev/ttyACM0}' +qui représentent des tuples +\textit{clef-type-valeur}\footnote{Laissons les canards tranquilles.} +de choses diverses. + +La syntaxe n'est pas encore vraiment fixée, mais un fichier exemple est +disponible pour des explication plus récentes, donc plus en +rapport avec la réalité du code. En voici un extrait : + +\begin{verbatim} +# serial input from the control cpu +input_device s /dev/ttyACM0 +input_speed i 9600 +# -------------------------------------------------- +# some values for the eyecandy displays +eyecandy_banner s hacked by tTh +\end{verbatim} + +Certains de ces paramètres pourront être surchargé par des options +de la ligne de commande\index{cli} ou des variables d'environnement. + +% =================================================================== + +\section{Flatfile} \label{flatfile} + +Parfois, un fichier à plat est bien pratique, parce qu'il est facilement +machinable avec des outils comme Awk\index{Awk}. +Après tout, un \textsl{timestamp}\index{timestamp} et quelques valeurs +numériques peuvent suffire à beaucoup de \textsl{usecases} de la +vie courante. + +Voici le premier exemple, un format\label{foo.dat} d'enregistrement de +température facilement exploitable avec Awk ou Gnuplot : + +\begin{verbatim} +tth@phytotron:~/DD2-monitor/doc$ tail -3 ../serial/foo.dat +1550673785 20.215054 20.107527 20.107527 20.215054 +1550673811 20.215054 20.215054 20.215054 20.215054 +1550673836 20.215054 20.107527 20.215054 20.215054 +\end{verbatim} % ------------------------------------------------------------------- - -\section{Détournements} - -Dans le contexte myryssien, il est évident que l'aspect artistique -doit être dès le départ pris en compte. -Les possibilités ne seront limitées que par votre manque d'imagination. - -Mais si vous voulez un petit exemple, imaginez des courbes de température -qui pilotent un \texttt{uGen} de Chuck\index{chuck} ou des algues dont la lumière -envoie du \texttt{cv/gate} en temps réel... - - + \input{storages} % ------------------------------------------------------------------- -\section{Conclusion}\label{conclusion}\index{conclusion} + \input{serial} + \input{detournements} +% ------------------------------------------------------------------- + +\section{Conclusion} \label{conclusion} \index{conclusion} En fait, tout reste à faire. Mais ça peut être un beau projet -aux implications et usages multiples. +aux implications et usages multiples. À condition de bien +faire les choses. + +Un couteau suisse de la capture du monde réel, un point pivot +de nos diverses interprétations de +l'univers\footnote{non, la terre n'est pas plate} et des interactions +étranges entre des paramètres sans relation clairement definie. % ------------------------------------------------------------------- \setlength{\parskip}{0.05cm plus 0.05cm} -\pagebreak \tableofcontents +% \pagebreak \tableofcontents \printindex diff --git a/doc/detournements.tex b/doc/detournements.tex new file mode 100644 index 0000000..d68bd45 --- /dev/null +++ b/doc/detournements.tex @@ -0,0 +1,29 @@ +% ------------------------------------------------------------------- + +\section{Détournements} \label{detournements} + +Dans le contexte myryssien\index{Myrys}, il est évident que l'aspect +artistique\index{Art} +doit être dès le départ pris en compte. +Les possibilités ne seront limitées que par votre manque d'imagination. + +Si vous voulez un petit exemple, imaginez des courbes de température +qui pilotent un \texttt{uGen} de Chuck\index{Chuck} ou des algues dont +les pulses lumineux envoie du \texttt{cv/gate} en temps réel. + +\subsection{Variante sonore} + +Un peu de \texttt{awk} pipé dans du \texttt{sox} ? +Voire même l'occasion (ou un bon prétexte) de résoudre ce problème +de saut de phase qui me tracasse depuis des mois ? + +\subsection{Eye candy}\index{eyecandy}\label{eyecandy} + +Pour maximiser l'impact visuel, il sera convenant de sortir du +cadre pseudo-moderne des omniprésents écrans de ces +smartphones\footnote{Quand les téléphones étaient attachés par +un fil, les humains étaient libres.} qui nous lavent le cerveau. +Sortons donc du contexte pixeliste et revenons aux fondamentaux : +le caractère\index{ncurses} blanc sur fond noir, +avec toute la simplicité de son concept sémantique. + diff --git a/doc/mkdoc.sh b/doc/mkdoc.sh new file mode 100755 index 0000000..031711f --- /dev/null +++ b/doc/mkdoc.sh @@ -0,0 +1,8 @@ +#!/bin/bash + + +pdflatex dd2-monitoring.tex + +makeindex dd2-monitoring + +pdflatex dd2-monitoring.tex \ No newline at end of file diff --git a/doc/serial.tex b/doc/serial.tex new file mode 100644 index 0000000..287ed20 --- /dev/null +++ b/doc/serial.tex @@ -0,0 +1,59 @@ +% =================================================================== + +\section{Serial coms} \index{serial} \label{serial} + +Il va y avoir deux liaisons série entre l'automate et le monitoring. +La première, que nous allons juste entrevoir, passera par le port +USB de la carte Arduino. La seconde passera par un port série +auxiliaire\footnote{Il y en a 4 sur le mega 2560} de celle-ci. + +% ------------------------------------------------------------------- +\subsection{Un gros souci} + +\begin{lstlisting} +tcgetattr(uart0, &options); +options.c_cflag = baudbits | CS8 | CLOCAL | CREAD; +options.c_iflag = IGNPAR; +options.c_oflag = 0; +options.c_lflag = 0; +tcflush(uart0, TCIFLUSH); +tcsetattr(uart0, TCSANOW, &options); +\end{lstlisting} + +Et en fait, le \texttt{read} sur le \textsl{fd} du serial device +n'est pas bloquant, \texttt{perror} annonce \emph{success}, mais +rien ne marche. L'année 2019 va commencer sur du vaudou programming% +\footnote {aka shotgun debugging.}. + +\textit{29 décembre 2018} : le petit grain de magie\index{magie} +est très simple à mettre en oeuvre, mais +très difficle à spotter dans le gazillion d'options. Il semblerait +que mettre \texttt{options.c\_cc[VMIN]} à 1 permet d'avancer vers +l'étape suivante. + +Laquelle étape est une tentative d'utilisation de \texttt{select(2)}, +dans l'objectif de pouvoir gérer nous-même le \textit{timeout}, +laquelle tentative n'est pas du tout concluante. + +% ------------------------------------------------------------------- +\subsection{Protocole} \index{protocole} \label{serialprotocol} + +L'automate va avoir plusieurs types de données à envoyer. +Nous allons donc transférer ces valeurs sous forme de ligne +de texte commençant par un caractère clef +(par exemple \texttt{T} pour les températures) +et se terminant par un \textit{newline}. + + +% ------------------------------------------------------------------- +\subsection{Un peu de code} \label{serialcode} + +\begin{lstlisting} +main() +{ + while(fork()); +} +\end{lstlisting} + + +% =================================================================== diff --git a/doc/storages.tex b/doc/storages.tex new file mode 100644 index 0000000..9be6d84 --- /dev/null +++ b/doc/storages.tex @@ -0,0 +1,111 @@ +% ============================================ +% +% Various storages systems +% +% ============================================ + +\section{RRDB} \label{rrdb} + +Première tentative d'utilisation le lendemain du premier apéro 2019 +du Tetalab\footnote{Jean-Yves, je vous demande de vous calmer !}. + +Je suis parti sur quelques scripts shell, pour créer, mettre à jour et +analyser les enregistrements d'une valeur de type \textsc{gauge}. + +\subsection{Create} + +\begin{lstlisting} +#!/bin/bash + +source ./commun.sh +starttime=$(date +'%s') +echo creating $RRDB at ${starttime}s since epoch +rrdtool create $RRDB \ + --start $starttime \ + --step 60 \ + DS:value:GAUGE:150:0:10 \ + RRA:AVERAGE:0.5:1:60 +\end{lstlisting} + +\subsection{Update} \index{rrdtool} + +Une fois la base créée, il faut bien la remplir. +Dans cet exemple, nous allons utiliser le \textit{load} +de notre Linux. + +\begin{lstlisting} +#!/bin/bash + +source ./commun.sh +ctime=$(date +'%s') +value=$(cut -d ' ' -f 1 /proc/loadavg) + +# inject value in the rrdb file +rrdtool update $RRDB ${ctime}:${value} +\end{lstlisting} + +\subsection{Analyze} + +Nous allons essayer d'exploiter les données dûrement acquises +pendant les heures qui précèdent\dots + +\begin{lstlisting} +#!/bin/bash + +source ./commun.sh +tmpf="somevalues.dat" +rrdtool fetch $RRDB LAST | +tr -d ':' | +awk ' + (!/nan/ && NF==2) { print $1, $2 } + ' \ + > ${tmpf} + +# as an example, we are gnuploting our datas +gnuplot << __EOC__ +set term png size 800,600 +set output "graphe.png" +set grid +plot "${tmpf}" with lines +__EOC__ + +rm ${tmpf} +\end{lstlisting} + +Il semble bien que l'utilisation de \texttt{fetch} ne soit pas +vraiment prévue pour ça, donc j'en arrive à la conclusion que +quelque chose m'échappe. + +On va laisser ça en suspens pour le moment. + +% ------------------------------------------------------------------- + +\section{InfluxDB} \label{influxdb} + +La communication avec la bédédé se fait \textit{over HTTP}, un peu +comme tous ces trucs de d'jeunz d'aujourd'hui\dots + +Au programme : écriture d'un injecteur en Perl\index{Perl}, en suivant +plus ou moins l'exemple de rrdb.. + +Ceci dit, en Debian stable, on n'a que la version 1.0, qui ne +correspond plus trop à l'actualité. Et la \textit{current} semble +trop fatiguante à compiler pour ce soir, ni même pour ce week-end. + +% ------------------------------------------------------------------- + +\section{Gnocchi} \label{gnocchi} \index{Gnocchi} + +\texttt{pip install gnocchi[postgresql,ceph,keystone]}, finalement, +çe ne me donne pas trop envie. C'est du genre \textit{usinagaz}. + +% ------------------------------------------------------------------- +\section{Sqlite} \index{sqlite} \label{sqlite} + +\textsf{À regarder de près} + +Est-il possible de traiter des \textit{time series} en SQL\index{SQL} ? +Peut-on utiliser Sqlite depuis un programme en Perl\index{Perl} ? +Faut-il commencer à trouver un \textit{usecase} crédible ? +NodeJs\index{nodejs} peut-il lire du Sqlite ? + diff --git a/essai.c b/essai.c index 0ad7c09..4ccb172 100644 --- a/essai.c +++ b/essai.c @@ -2,35 +2,101 @@ * essai.c */ -#include -#include -#include +#include +#include +#include +#include +#include +#include -#include "funcs.h" +#include "core/utils.h" +#include "core/sysmetrics.h" +#include "serial/serial.h" +#include "viz/curses/ecran.h" -int verbosity; +int verbosity; +/* --------------------------------------------------------------- */ +int affiche_valeurs(int sfd, int nbloops) +{ +int idx, foo; +char ligne[200], buff[200]; +float datas[4]; + +for (idx=0; idx %d\n", foo); +#endif + + foo = parse4values(ligne, 'T', datas); +#if DEBUG_LEVEL + if (foo) fprintf(stderr, "parse -> %d\n", foo); +#endif + + values2temperature(datas); + + foo = aff7segs_float(stdscr, 3, 5, datas[0]); + foo = aff7segs_float(stdscr, 14, 5, datas[1]); + foo = aff7segs_float(stdscr, 25, 5, datas[2]); + + } +return 0; +} +/* --------------------------------------------------------------- */ +static void finish(int signal) +{ +endwin(); +fprintf(stderr, "end of pid %d\n", getpid()); +exit(0); +} +/* --------------------------------------------------------------- */ +void help(int k) +{ +puts("options : "); +puts("\t-d\tserial device to read."); +puts("\t-v\tincrease verbosity."); +exit(0); +} /* --------------------------------------------------------------- */ int main(int argc, char *argv[]) { int opt, foo; -int type = 0; -double loads[3]; +int serial_in; +char *device = "/dev/ttyACM0"; -while ((opt = getopt(argc, argv, "vst:")) != -1) { +while ((opt = getopt(argc, argv, "d:hv")) != -1) { switch (opt) { + case 'd': device = optarg; break; + case 'h': help(0); break; case 'v': verbosity++; break; - case 's': srand(getpid()); break; - case 't': type = atoi(optarg); break; default: break; } } -foo = get_loadavg(loads); -if (foo) fprintf(stderr, "get loadavg -> %d\n", foo); -printf("%f %f %f %f\n", dtime(), loads[0], loads[1], loads[2]); +serial_in = prepare_UART(device, 9600); +if (serial_in < 0) { + fprintf(stderr, "%s : open device : error %d on %s\n", + argv[0], serial_in, device); + exit(1); + } + + +initscr(); +nonl(); cbreak(); noecho(); +keypad(stdscr, TRUE); /* acces aux touches 'curseur' */ +fond_ecran(" Demonstrator "); + +affiche_valeurs(serial_in, 10000); + +/* + * plop, on a fini, il faut restaurer la console + */ +finish(0); + return 0; } diff --git a/exemple.sh b/exemple.sh index d3cafd4..e0dbd7a 100755 --- a/exemple.sh +++ b/exemple.sh @@ -6,21 +6,28 @@ DATAFILE=/tmp/fake-datafile > ${DATAFILE} for s in $(seq 1 1000) do - v=$(./fake-values -s -t 1 2> /dev/null) + v=$(./fake-values -s -t 4) echo $s $v >> ${DATAFILE} done +tail -5 ${DATAFILE} + #----- do dome useless computations awk ' - NR==1 { debut = $2 } + NR==1 { + debut = $2 + } + { # print $2-debut, $3 - v = int($3/25); + v = int($3/35); bucket[v]++; } + END { for (v=0; v<40; v++) { - for (foo=0; foo #include #include -#include "funcs.h" +#include "core/utils.h" int verbosity; @@ -30,7 +32,7 @@ if (verbosity > 1) { fprintf(stderr, "fake values - %s %s\n", __DATE__, __TIME__); } -printf("%f %d\n", dtime(), random1000(type)); +printf("%.3f %4d\n", dtime(), random1000(type)); return 0; } diff --git a/funcs.h b/funcs.h deleted file mode 100644 index 057594b..0000000 --- a/funcs.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * funcs.c - */ - -/* return an in random value in [0.999] */ -int random1000(int mode); - -/* get the 'timeofday' as a double float */ -double dtime(void); - -/* only usable on standard Linux ! */ -int get_loadavg(double where[]); - diff --git a/gnocchi/README.md b/gnocchi/README.md index 643e7ed..5152bab 100644 --- a/gnocchi/README.md +++ b/gnocchi/README.md @@ -1,11 +1,22 @@ -## Gnocchi +# Gnocchi -# blabla commercial +## blabla commercial +<<<<<<< HEAD _The problem that [Gnocchi](https://gnocchi.xyz/) solves is the storage and indexing of time series data and resources at a large scale. +======= +_The problem that [Gnocchi](https://gnocchi.xyz/) solves is the storage and +indexing of time series data and resources at a large scale. +>>>>>>> b47e467d21cec6ee688b4407d3ec54fa33e67ba6 This is useful in modern cloud platforms which are not only huge but also are dynamic and potentially multi-tenant. Gnocchi takes all of that into account._ + +Bref, il faut essayer ce truc. Un de ces jours... + +`pip install gnocchi[postgresql,ceph,keystone]` +bon, on va peut-être attendre :) + diff --git a/influxdb/README.md b/influxdb/README.md index 8a603ab..f949aa4 100644 --- a/influxdb/README.md +++ b/influxdb/README.md @@ -12,7 +12,10 @@ purpose-built platform that InfluxData provides._ # On essaye ? -Ok, c'est parti. On va écrire un injecteur en Perl. Puis enchainer sur +Ok, c'est parti. Premier souci, la documentation est assez légère. + + +On va tenter d'écrire un injecteur en Perl. Puis enchainer sur une visualisation dynamique des données en lancer de rayon. Projet ambitieux ? Non, la suite sera bien pire. diff --git a/influxdb/create.sh b/influxdb/create.sh new file mode 100755 index 0000000..40c6481 --- /dev/null +++ b/influxdb/create.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# +# create an inflix databasse +# + +influx -host localhost -port 8086 << __EOC__ + +settings +CREATE DATABASE tests +__EOC__ + +lynx -head -dump http://localhost:8086/ping?verbose=true \ No newline at end of file diff --git a/influxdb/injecteur.pl b/influxdb/injecteur.pl index 0c54195..7a09954 100755 --- a/influxdb/injecteur.pl +++ b/influxdb/injecteur.pl @@ -2,6 +2,9 @@ use strict; +my $host = "localhost"; +my $port = 8086; + print "injecteur v 0\n"; 0; diff --git a/rrdb/README.md b/rrdb/README.md index 2c69ae5..d0c003e 100644 --- a/rrdb/README.md +++ b/rrdb/README.md @@ -1,3 +1,39 @@ +<<<<<<< HEAD ## Round Robin Database +======= +# Round Robin Database + +Un grand classique du genre. Délicat à comprendre au début. +Les principes sous-jacents sont assez pointus, et leur mise en oeuvre +loin d'être évidente pour les newbies. + +La lecture de la manpage `rrdtutorial` est indispensable. + +https://oss.oetiker.ch/rrdtool/tut/rrd-beginners.en.html + +## premier exemple + +Un petit peu de code fabriqué à la rache. + +- `create.sh` +- `update.sh` +- `getvalues.sh` +- `mkgraph.sh` + +Suffisant pour comprendre le principe général, mais très flou +sur les détails. + +## et après ? + +Trouver une interface en C pour faciliter la vie des gens. + + + + + + + + + +>>>>>>> b47e467d21cec6ee688b4407d3ec54fa33e67ba6 -Un grand classique du genre. diff --git a/rrdb/commun.sh b/rrdb/commun.sh new file mode 100644 index 0000000..8b72f42 --- /dev/null +++ b/rrdb/commun.sh @@ -0,0 +1,6 @@ +# +# commun definitions for rrdb tests +# + +export RRDB=$HOME/TMP/tests.rrd + diff --git a/rrdb/create.sh b/rrdb/create.sh new file mode 100755 index 0000000..ebd852c --- /dev/null +++ b/rrdb/create.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# +# creating the test database +# + +source ./commun.sh + +starttime=$(date +'%s') + +echo creating $RRDB at ${starttime}s since epoch + +rrdtool create $RRDB \ + --start $starttime \ + --step 60 \ + DS:value:GAUGE:150:0:10 \ + RRA:AVERAGE:0.5:1:60 + + + + \ No newline at end of file diff --git a/rrdb/getvalues.sh b/rrdb/getvalues.sh new file mode 100755 index 0000000..9a28cab --- /dev/null +++ b/rrdb/getvalues.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +source ./commun.sh + +tmpf="somevalues.dat" + +rrdtool fetch $RRDB LAST \ + --start 0 | +tr -d ':' | +awk ' + (!/nan/ && NF==2) { print $1, $2 } + ' \ + > ${tmpf} + +# +# as an example, we are gnuploting our datas +# +gnuplot << __EOC__ +set term png size 1024,512 +set output "graphe.png" +set grid +set xdata time +set timefmt "%s" +set format x "%m/%d\n%H:%M" +plot "${tmpf}" using 1:2 with lines +__EOC__ + +# rm ${tmpf} + diff --git a/rrdb/insert.sh b/rrdb/insert.sh new file mode 100755 index 0000000..ad5dfbc --- /dev/null +++ b/rrdb/insert.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +source ./commun.sh + +ctime=$(date +'%s') +value=$(cut -d ' ' -f 1 /proc/loadavg) + +# display and write value to a file +echo ${ctime} ${value} | tee -a bar.dat + +# inject value in the rrdb file +rrdtool update $RRDB ${ctime}:${value} + + diff --git a/rrdb/mkgraph.sh b/rrdb/mkgraph.sh new file mode 100755 index 0000000..5354f9c --- /dev/null +++ b/rrdb/mkgraph.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +source ./commun.sh + +rrdtool graph value.png \ + --start 0 --end now \ + -w 800 -h 600 \ + DEF:value=${RRDB}:value:LAST \ + LINE1:value#0000FF + + diff --git a/serial/Makefile b/serial/Makefile index 984dabe..8ceb6ee 100644 --- a/serial/Makefile +++ b/serial/Makefile @@ -1,9 +1,16 @@ -OPT = -Wall -DDEBUG_LEVEL=1 +OPT = -Wall -DDEBUG_LEVEL=0 +OBJS = serial.o funcs.o +# --------------------------------------------------- serial.o: serial.c serial.h Makefile gcc ${OPT} -c $< -t: t.c serial.o Makefile - gcc ${OPT} $< serial.o -o $@ +funcs.o: funcs.c serial.h Makefile + gcc ${OPT} -c $< + +# --------------------------------------------------- + +t: t.c Makefile ${OBJS} + gcc ${OPT} $< ${OBJS} -o $@ diff --git a/serial/README.md b/serial/README.md index 98293fc..7d76e1e 100644 --- a/serial/README.md +++ b/serial/README.md @@ -1,14 +1,45 @@ - # Serial Input But premier de ce module : recevoir les données fournies par l'automate de contrôle du phytotron. -Ayant déja pratiqué ce genre de chose pour un déja ancien -[projet](http://art.dinorama.fr/bdf/) +Ayant déja pratiqué ce genre de chose (recevoir des données par rs232) +pour un déja ancien +[projet artsitique](http://art.dinorama.fr/bdf/) conçu par et avec _MadPhoenix_, je me propose de reprendre quelques parties de ce code, de le remettre au gout du jour et de le tester dès que possible. +## principe général + +Pour écouter plusieurs lignes simultanément, chaque port sera traité +par un _thread_ séparé, et les diverses données reçues seront pré-traitées +par celui-ci. Les flux de données seront alors agrégées par +le célèbre *synthétiseur d'évènement* mis au point il y a très longtemps +par le professeur Cispeo. + +## À venir... + +Un petit exemple ? + +Oui, voilà. À ce jour (20 déc. 2018), on va dire que ça ne marche pas. +Il faut dire que les `serial devices` ont toujours étés un peu le +domaine de la magie noire. Mais quand même, coincer sur un `read` qui +ne bloque pas, c'est un peu ironique. + +Après un peu plus d'investigation, j'en arrive à conclure qu'il y a +plein de subtilités entre les diverses variantes d'Arduino. Mais pas que. +Je pense que les quatre ports série supplémentaires de l'Arduino Mega +seront moins capricieux. + + + + + + + + + + diff --git a/serial/essai.sh b/serial/essai.sh new file mode 100755 index 0000000..f40b101 --- /dev/null +++ b/serial/essai.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +DEVICE="/dev/ttyACM0" +DATAFILE="foo.dat" +TMPFILE="/tmp/dd2data" + +IMAGE="graphe.png" +NB_READ=10 + +./t -n ${NB_READ} -d ${DEVICE} | tee -a ${DATAFILE} + +gnuplot << __EOC__ +set term png size 1600,640 +set output "${IMAGE}" +set grid +set title "Temperature dans le Double Dragon 2" +set xdata time +set timefmt "%s" +set format x "%d, %H:%M" +set yrange [ 0.0 : 30.0 ] +plot "${DATAFILE}" using 1:2 title " foo" with lines, \ + "${DATAFILE}" using 1:3 title " bar" with lines, \ + "${DATAFILE}" using 1:4 title "quux" with lines, \ + "${DATAFILE}" using 1:5 title "booz" with lines +__EOC__ + +# display ${IMAGE} + + diff --git a/serial/funcs.c b/serial/funcs.c new file mode 100644 index 0000000..d670534 --- /dev/null +++ b/serial/funcs.c @@ -0,0 +1,88 @@ + +#include +#include + +#include "serial.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +/* + * compute the integer mean value of a four values + * tagged lines. + */ +int parseXvalue(char *line, char cflag) +{ +int value, foo; +int vrd[4]; + +value=0; + +if ( cflag != *line ) { + if (verbosity) { + fprintf(stderr, "%s : line[0] 0x%x bad\n", + __func__, *line); + } + return -777; + } + +foo = sscanf(line+1, "%d %d %d %d", vrd, vrd+1, vrd+2, vrd+3); +#if DEBUG_LEVEL +fprintf(stderr, "%s : sscanf -> %d\n", __func__, foo); +#endif +if (4 != foo) { + return -666; + } +for (foo=0; foo<4; foo++) { + value += vrd[foo]; + } + +value /= 4; + +return value; +} +/* ---------------------------------------------------------------- */ +/* + * this fonction is specific to the LM35 thermo-sensor + * connected to a ADC pin of an Arduino Mega + * + * WARNING ! + * this function _must_ be modofied if you change the + * Vref of the Analog to Digital converter on the + * Arduino ! + * + */ +int values2temperature(float array[4]) +{ +int foo; +for (foo=0; foo<4; foo++) { + array[foo] *= (1.1 / 1023.0 * 100.0); + } +return 0; +} +/* ---------------------------------------------------------------- */ +int parse4values(char *line, char cflag, float array[4]) +{ +float ftmp[4]; +int foo; + +if ( cflag != *line ) { + if (verbosity) { + fprintf(stderr, "%s : line[0] 0x%x bad\n", + __func__, *line); + } + return -777; + } + +foo = sscanf(line+1, "%f %f %f %f", ftmp, ftmp+1, ftmp+2, ftmp+3); +if (4 != foo) { + fprintf(stderr, "%s : sscanf -> %d\n", __func__, foo); + return -666; + } +// fprintf(stderr, "\tV %f\n", ftmp[0]); + +memcpy(array, ftmp, 4*sizeof(float)); + +return 4; +} +/* ---------------------------------------------------------------- */ diff --git a/serial/serial.c b/serial/serial.c index f086c13..34c79f5 100644 --- a/serial/serial.c +++ b/serial/serial.c @@ -1,7 +1,9 @@ #include #include +#include #include +#include #include //Used for UART #include //Used for UART #include //Used for UART @@ -39,7 +41,7 @@ int baudbits; struct termios options; #if DEBUG_LEVEL -fprintf(stderr, "%s ( %s %d )\n", __func__, port, baudrate); +fprintf(stderr, ">>> %s ( %s %d )\n", __func__, port, baudrate); #endif // OPEN THE UART // The flags (defined in fcntl.h): @@ -60,8 +62,8 @@ fprintf(stderr, "%s ( %s %d )\n", __func__, port, baudrate); // shall not cause the terminal device to become the controlling terminal // for the process. -uart0 = open(port, O_RDWR | O_NOCTTY); -if (uart0== -1) +uart0 = open(port, O_RDONLY | O_NOCTTY); +if (uart0 < 0) { perror("unable to open uart "); return -1; @@ -84,32 +86,44 @@ if (uart0== -1) baudbits = baudrate2const(baudrate); -#if DEBUG_LEVEL +#if DEBUG_LEVEL > 1 fprintf(stderr, "%d -> 0x%04x\n", baudrate, baudbits); #endif + +memset(&options, 0, sizeof(options)); + tcgetattr(uart0, &options); -options.c_cflag = baudbits | CS8 | CLOCAL | CREAD; // 1 +fprintf(stderr, "timeout %6d is %4ld.%6ld\n", to_ms, timeout.tv_sec, timeout.tv_usec); #endif @@ -136,37 +149,91 @@ FD_ZERO (&rfds); FD_SET (fd, &rfds); retval = select(1, &rfds, NULL, NULL, &timeout); - #if DEBUG_LEVEL -fprintf(stderr, "%s : select -> %d\n", __func__, retval); +fprintf(stderr, "%s : select on fd %d -> %d\n", __func__, fd, retval); #endif -retval = retval < 0 ? -1 : retval; - switch (retval) { case -1: fprintf(stderr, "omg ?\n"); - byte = -1; + retval = -1; break; case 0: - fprintf(stderr, "timeout\n"); - byte = -99; + fprintf(stderr, "timeout %ld.%ld\n", + timeout.tv_sec, timeout.tv_usec); + retval = -99; break; default: + fprintf(stderr, "%s default -> %d\n", __func__, retval); if (retval==fd) { read(fd, &byte, 1); fprintf(stderr, "got 0x%02x\n", byte); + retval = byte; } else { fprintf(stderr, "%d bad fd ?\n", retval); - byte = -3; + retval = -3; } break; } -return byte; +return retval; } /* -------------------------------------------------------------------- */ +/* timeout is in milliseconds */ + +int getline_to(int fd, char *where, int szm, int to_ms) +{ +int curpos, byte, retval; + +#if DEBUG_LEVEL +fprintf(stderr, ">>> %s ( %d %p %d %d )\n", __func__, + fd, where, szm, to_ms); +#endif + +curpos = 0; +retval = -7; /* magic number powa */ +where[0] = '\0'; /* erase all the bs */ + +for(;;) { + if (to_ms) { + byte = getbyte_to (fd, to_ms); + } + else { + byte = getbyte(fd); + } + + if (byte < 0) { + fprintf(stderr, "%s : something is wrong %d\n", + __func__, byte); + retval = byte; + break; + } + + if ('\n' == byte) { /* got an EOL ? */ + where[curpos] = '\0'; + retval = curpos; + break; + } + + if (curpos < szm) { /* ya de la place */ + where[curpos] = byte; + curpos++; + } + else { /* oups overflow */ + retval = -6; + break; + } + + } + +#if DEBUG_LEVEL +fprintf(stderr, "%s -> '%s'\n", __func__, where); +#endif +return retval; +} +/* -------------------------------------------------------------------- */ + diff --git a/serial/serial.h b/serial/serial.h index 2bd7cb5..bf6977c 100644 --- a/serial/serial.h +++ b/serial/serial.h @@ -6,10 +6,19 @@ int prepare_UART(char *port, int bauds); -int getbyte(int fd); +int getbyte(int fd); /* really brotched func */ /* timeout is exprimed in milliseconds. */ int getbyte_to (int fd, int to_ms); +int getline_to(int fd, char *where, int szm, int to_ms); + +/* auxiliary and test functions */ + +int parseXvalue(char *asciidatas, char id); +int values2temperature(float array[4]); + +int parse4values(char *line, char cflag, float array[4]); + diff --git a/serial/t.c b/serial/t.c index 8a75043..3eef5fe 100644 --- a/serial/t.c +++ b/serial/t.c @@ -1,29 +1,114 @@ - +/* + * Experiments with the serial input + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ #include +#include +#include +#include #include +#include //Used for UART +#include //Used for UART +#include +#include //Used for UART +#include #include "serial.h" +int verbosity; + +/* ---------------------------------------------------------------- */ +int loop(int sfd, int iters) +{ +int count, foo; +long temps; +char ligne[200], buff[200]; +float datas[4]; +struct tm *p_tms; + +for (count=0; count %d\n", + count, iters, foo); */ + fprintf(stderr, "%s\n", ligne); + } + // + if (verbosity > 1) + { + p_tms = localtime(&temps); + (void)strftime(buff, 19, "%H:%M:%S", p_tms); + fprintf(stderr, "%s %6d / %d\n", buff, count, iters); + } + // + foo = parse4values(ligne, 'T', datas); + // + if (foo >= 0) { + temps = time(NULL); + values2temperature(datas); + printf("%ld %f %f %f %f\n", temps, + datas[0], datas[1], datas[2], datas[3]); + fflush(stdout); + } + else { + fprintf(stderr, "%s: parse -> %d\n", __func__, foo); + } + } +return 0; +} +/* ---------------------------------------------------------------- */ +void help(int k) +{ +puts("options : "); +puts("\t-d\tserial device to read."); +puts("\t-n\tnumber of records to grab."); +puts("\t-v\tincrease verbosity."); +} +/* ---------------------------------------------------------------- */ int main (int argc, char *argv[]) { -int serial_in; -int byte, foo, to; +int serial_in; +char *device = "/dev/ttyACM0"; +int nbre, speed, opt; -serial_in = prepare_UART("/dev/ttyS0", 9600); -fprintf(stderr, "prepare uart -> %d\n", serial_in); +/* set some default values */ +verbosity = 0; +nbre = 25; +speed = 9600; -for (foo=0; foo<20; foo++) { - to = (foo+1) * 666; - byte = getbyte_to(serial_in, to); - if (byte < 0) { - fprintf(stderr, "get byte : err is %d\n", byte); +while ((opt = getopt(argc, argv, "d:n:vh")) != -1) { + switch (opt) { + case 'v': verbosity++; break; + case 'n': nbre = atoi(optarg); break; + case 'd': device = optarg; break; + case 'h': help(0); exit(0); + default: + fprintf(stderr, "%s : uh ?", argv[0]); + exit(1); + break; } - // XXX else { - printf("%9ld %6d %6d %02x\n", - time(NULL), foo, to, byte); - // XXX } + } +if (verbosity) { + fprintf(stderr, "Testing Serial Software - compiled " \ + __DATE__ " " __TIME__ "\n"); + } + +serial_in = prepare_UART(device, speed); +if (serial_in < 0) { + fprintf(stderr, "%s : open device : error %d on %s\n", + argv[0], serial_in, device); + exit(1); + } + +fprintf(stderr, "going to listen on %d\n", serial_in); + +(void)loop(serial_in, nbre); + return 0; } + +/* ---------------------------------------------------------------- */ diff --git a/simulator/rdtemp/rdtemp.ino b/simulator/rdtemp/rdtemp.ino new file mode 100644 index 0000000..81695d7 --- /dev/null +++ b/simulator/rdtemp/rdtemp.ino @@ -0,0 +1,66 @@ +/* + * lecture des capteurs de temperature LM35 + */ +/* -------------------------------------------------- */ + +#define NBVAL 4 +#define DELAI 5000 + +/* -------------------------------------------------- */ +void setup() { + Serial.begin(9600); + pinMode(LED_BUILTIN, OUTPUT); + + Serial.print("\n"); + /* XXX */ + /* changing the voltage reference of the ADC + * greatly increase the prcision on the limited + * range of our temperatures. + */ + analogReference(INTERNAL1V1); // Pour Arduino Mega2560 + + delay(1000); + Serial.print("M running\n"); + +} +/* -------------------------------------------------- */ +/* ================================================== */ +short adc_pins[] = { A0, A1, A2, A4 }; +/* -------------------------------------------------- */ +void updatevalues(short *ptr) +{ + int foo; + digitalWrite(LED_BUILTIN, HIGH); + for (foo=0; foo 1023) { + values[foo] = rand()%5; + } + } +} +/* -------------------------------------------------- */ + +void sendvalues(void) +{ + int foo; + char ligne[100]; + + sprintf(ligne, "X %d %d %d %d", + values[0], values[1],values[2],values[3]); + + Serial.println(ligne); +} +/* -------------------------------------------------- */ + +void loop() { + updatevalues(); + sendvalues(); + delay(DELAI); +} + +/* -------------------------------------------------- */ + diff --git a/storage/Makefile b/storage/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/storage/t.c b/storage/t.c new file mode 100644 index 0000000..e69de29 diff --git a/viz/curses/7segments.c b/viz/curses/7segments.c new file mode 100644 index 0000000..9965a08 --- /dev/null +++ b/viz/curses/7segments.c @@ -0,0 +1,153 @@ +/* + * DD2 Monitoring + * + * ncurses seven segment display + */ + +#include +#include +#include +#include +#include +#include + +#include "ecran.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +int aff7segs_base(WINDOW * win, int lig, int col, int bits, int k) +{ +int numbit, mask; +int foo; + +for (foo=0; foo<9; foo++) { + mvwhline(win, lig+foo, col, '~', 6); + } + +#define KC '+' +#define KS "++++" + +wstandout(win); +for (numbit=0; numbit<8; numbit++) { + mask = 1 << numbit; + switch(mask & bits) { + case 0x01: + mvwaddstr(win, lig, col+1, KS); + break; + case 0x02: + mvwaddch(win, lig+1, col+5, KC); + mvwaddch(win, lig+2, col+5, KC); + mvwaddch(win, lig+3, col+5, KC); + break; + case 0x04: + mvwaddch(win, lig+5, col+5, KC); + mvwaddch(win, lig+6, col+5, KC); + mvwaddch(win, lig+7, col+5, KC); + break; + case 0x08: + mvwaddstr(win, lig+8, col+1, KS); + break; + case 0x10: + mvwaddch(win, lig+5, col, KC); + mvwaddch(win, lig+6, col, KC); + mvwaddch(win, lig+7, col, KC); + break; + case 0x20: + mvwaddch(win, lig+1, col , KC); + mvwaddch(win, lig+2, col , KC); + mvwaddch(win, lig+3, col , KC); + break; + case 0x40: + mvwaddstr(win, lig+4, col+1, KS); + break; + case 0x80: + /* decimal point */ + mvwaddch(win, lig+7, col+2 , KC); + mvwaddch(win, lig+7, col+3 , KC); + mvwaddch(win, lig+8, col+2 , KC); + mvwaddch(win, lig+8, col+3 , KC); + break; + + } + + } +wstandend(win); + +return 0; +} +/* ---------------------------------------------------------------- */ +int aff7segs_digit(WINDOW * win, int lig, int col, char digit) +{ +int bits; + +#if TRACE > 1 +fprintf(stderr, ">>> %s ( %p %d %d '%c' )\n", __func__, + win, lig, col, digit); +#endif + +if (isxdigit(digit)) digit = toupper(digit); + +switch (digit) { + case '0': bits = 0x3f; break; + case '1': bits = 0x06; break; + case '2': bits = 0x5b; break; + case '3': bits = 0x4f; break; + case '4': bits = 0x66; break; + case '5': bits = 0x6d; break; + case '6': bits = 0x7d; break; + case '7': bits = 0x07; break; + case '8': bits = 0x7f; break; + case '9': bits = 0x6f; break; + + /* hexadecimal letters */ + case 'A': bits = 0x77; break; + case 'B': bits = 0x7c; break; + case 'C': bits = 0x39; break; + case 'D': bits = 0x5e; break; + case 'E': bits = 0x79; break; + case 'F': bits = 0x71; break; + + case ' ': bits = 0; break; + case '.': bits = 0x80; break; + case '-': bits = 0x40; break; + + default: bits = 0x49; break; + } + +aff7segs_base(win, lig, col, bits, 0); + +return 0; +} +/* ---------------------------------------------------------------- */ +int aff7segs_short(WINDOW * win, int lig, int col, short value) +{ +char buff[10]; +int idx; + +sprintf(buff, "%6d", value); +// mvwaddstr(win, lig-1, col, buff); +for (idx=0; idx +#include +#include +#include + +#include +#include + + +#include "ecran.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +int message(char *txt) +{ +static int pass = 0; + +#if TRACE +fprintf(stderr, "%s [%s]\n", __func__, txt); +#endif + +standout(); +mvhline(LINES-1, 0, ' ', COLS); +mvaddch(LINES-1, 0, "\\|/-"[(pass++)%4]); +mvaddstr(LINES-1, 2, txt); +standend(); +refresh(); +return 0; +} +/* ---------------------------------------------------------------- */ +void barre_inverse(char c, int ligne) +{ +int foo; +standout(); +for (foo=0; foo %s\n", __func__, buffer); + foo = strlen(buffer); + mvaddstr(0, COLS-2-foo, buffer); + } + +/* get and display hostname */ +foo = uname(&utsn); +if ( !foo ) { + mvaddstr(0, 2+strlen(tp), "on"); + mvaddstr(0, 5+strlen(tp), utsn.nodename); + } + +standend(); +refresh(); + +return 0; +} +/* ---------------------------------------------------------------- */ diff --git a/viz/curses/ecran.h b/viz/curses/ecran.h new file mode 100644 index 0000000..40ae535 --- /dev/null +++ b/viz/curses/ecran.h @@ -0,0 +1,23 @@ +/* + * interface ncurses pour dd2 monitoring + */ + +int fond_ecran(char *titre); +int message(char *); + +int aff7segs_base(WINDOW * win, int lig, int col, int bits, int k); +int aff7segs_digit(WINDOW * win, int lig, int col, char digit); +int aff7segs_short(WINDOW * win, int lig, int col, short value); +int aff7segs_float(WINDOW * win, int lig, int col, float value); + +int minidigit_0(WINDOW *win, int lig, int col, char digit, int k); +int minidigit_HMS(WINDOW *win, int lig, int col, int k); + + +WINDOW * open_waterfall(char *title, int flags); +int plot_waterfall(WINDOW *wf, int flags, float values[4]); +int close_waterfall(WINDOW *wf, int notused); + + +int vumetre_0(WINDOW * win, int lig, int col, float val, int larg); +int vumetre_1(WINDOW * win, int lig, int col, float val, int larg); diff --git a/viz/curses/minidigits.c b/viz/curses/minidigits.c new file mode 100644 index 0000000..0d4ff1a --- /dev/null +++ b/viz/curses/minidigits.c @@ -0,0 +1,107 @@ +/* + * DD2 Monitoring + * + * mini digits + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ecran.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +static void makedot(WINDOW *win, int li, int col, int ch) +{ +if ('_'==ch) { + mvwaddch(win, li, col, ' '); + mvwaddch(win, li, col+1, ' '); + } +else { + wstandout(win); + mvwaddch(win, li, col, '+'); + mvwaddch(win, li, col+1, '+'); + wstandend(win); + } +} +/* ---------------------------------------------------------------- */ + +int minidigit_0(WINDOW *win, int lig, int col, char digit, int k) +{ +static char LX[] = "_X_X_X_X_X_X_X_"; +static char L0[] = "XXXX_XX_XX_XXXX"; +static char L1[] = "_X__X__X__X__X_"; +static char L2[] = "XXX__XXXXX__XXX"; +static char L3[] = "XXX__XXXX__XXXX"; +static char L4[] = "X_XX_XXXX__X__X"; +static char L5[] = "XXXX__XXX__XXXX"; +static char L6[] = "XXXX__XXXX_XXXX"; +static char L7[] = "XXX__X__X__X__X"; +static char L8[] = "XXXX_XXXXX_XXXX"; +static char L9[] = "XXXX_XXXX__XXXX"; + +static char Lsp[] = "_______________"; /* space */ +static char Lmo[] = "______XXX______"; /* moins */ +static char Lco[] = "____X_____X____"; /* colomn */ +static char Ldp[] = "_____________X_"; /* decimal dot */ + +char *cptr; +int l, c; + +switch (digit) { + + case '0': cptr = L0; break; + case '1': cptr = L1; break; + case '2': cptr = L2; break; + case '3': cptr = L3; break; + case '4': cptr = L4; break; + case '5': cptr = L5; break; + case '6': cptr = L6; break; + case '7': cptr = L7; break; + case '8': cptr = L8; break; + case '9': cptr = L9; break; + + case ' ': cptr = Lsp; break; + case '-': cptr = Lmo; break; + case ':': cptr = Lco; break; + case '.': cptr = Ldp; break; + + default: cptr = LX; break; + + } + +for (l=0; l<5; l++) { + for (c=0; c<3; c++) { + + makedot(win, l+lig, (c*2)+col, *cptr++); + + } + } + +wrefresh(win); + +return 0; +} +/* ---------------------------------------------------------------- */ +int minidigit_HMS(WINDOW *win, int lig, int col, int k) +{ +int foo; +char chaine[20]; +struct tm *p_tms; +time_t temps; + +temps = time(NULL); +p_tms = localtime(&temps); +(void)strftime(chaine, 19, "%H:%M:%S", p_tms); +for (foo=0; foo +#include +#include +#include +#include +#include +#include +#include + +#include "ecran.h" + +int verbosity; + +/* ---------------------------------------------------------------- */ +int demo_minidigits(int nbl, int k) +{ +int loop, foo; +char chaine[100]; + +for (loop=0; loop 1023.0) { + rvals[foo] = (float)(rand() % 25); + } + } + + plot_waterfall(water, 1, rvals); + + /* if (rand()%10 < 1) sleep(1); */ + foo = nanosleep(&ts, NULL); + if (foo) { + /* got a signal ? */ + message("err on nanosleep"); + } + } + +close_waterfall(water, 0); + +} +/* ---------------------------------------------------------------- */ +static void finish(int signal) +{ +endwin(); exit(0); +} +/* ---------------------------------------------------------------- */ +int main (int argc, char *argv[]) +{ +int opt; +int demonum = 0; +int nb_loops = 200; + +/* set some default values */ +verbosity = 0; + +while ((opt = getopt(argc, argv, "n:vy:")) != -1) { + switch (opt) { + case 'n': nb_loops = atoi(optarg); break; + + case 'v': verbosity++; break; + + case 'y': demonum = atoi(optarg); break; + + default: + fprintf(stderr, "%s : uh ?", argv[0]); + exit(1); + break; + } + + } + +initscr(); +nonl(); cbreak(); noecho(); + +keypad(stdscr, TRUE); /* acces aux touches 'curseur' */ + +fond_ecran(" Demonstrator "); + +switch (demonum) { + case 0: demo_vumetres(nb_loops, 0); break; + case 1: demo_waterfall(nb_loops, 0); break; + case 2: demo_7segments(nb_loops, 0); break; + case 3: demo_composite(nb_loops, 0); break; + case 4: demo_minidigits(nb_loops, 0); break; + + default: + fprintf(stderr, "eyecandy #%d don't exist\n", demonum); + break; + } + +/* + * plop, on a fini, restaurer la console + */ +finish(0); + +return 0; +} +/* ---------------------------------------------------------------- */ diff --git a/viz/curses/vumetre.c b/viz/curses/vumetre.c new file mode 100644 index 0000000..0fb46f3 --- /dev/null +++ b/viz/curses/vumetre.c @@ -0,0 +1,88 @@ +/* + * DD2 Monitoring + * + * ncurses seven segment display + */ + +#include +#include +#include +#include +#include + +#include "ecran.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +int vumetre_0(WINDOW *win, int lig, int col, float val, int larg) +{ +int foo, posc, c; +char ligne[100]; + +#if DEBUG_LEVEL +fprintf(stderr, ">>> %s ( %p %d %d %f %d )\n", + __func__, win, lig, col, val, larg); +#endif + +posc = (int)(val * (float)(larg-5)); + +sprintf(ligne, "%6.3f", val); +mvwaddstr(win, lig, 0, ligne); + +for (foo=0; foo>> %s ( %p %d %d %f %d )\n", + __func__, win, lig, col, val, larg); +#endif + +posc = (int)(val * (float)larg); + +sprintf(ligne, "%6.3f", val); +mvwaddstr(win, lig, 2, ligne); + +for (foo=0; foo +#include +#include +#include +#include + + +#include "ecran.h" + +extern int verbosity; + +/* ---------------------------------------------------------------- */ +WINDOW *open_waterfall(char *title, int flags) +{ +WINDOW *win; +int l, c, w, h; + +l = 1; c = 1; +w = COLS; h = LINES - 3; + +win = newwin(h, w, l, c); +scrollok(win, 1); +waddstr(win, title); waddch(win, '\n'); +wrefresh(win); + +return win; +} +/* ---------------------------------------------------------------- */ +int plot_waterfall(WINDOW *wf, int mode, float values[4]) +{ +#define TL 1000 +int foo, idx; +char tag, ligne[TL+1]; +float coef_w; +static long iter; + +if (0 == (iter%10)) { + memset(ligne, '.', TL); + } +else { + memset(ligne, ' ', TL); + } +for (foo=0; foo<500; foo+=20) { + ligne[foo] = '.'; + } +ligne[COLS-1] = '\0'; + +iter++; + +coef_w = (float)(COLS-1) / 1024.0; + +#if TRACE > 1 +fprintf(stderr, "COLS = %d, coef_w = %f\n", COLS, coef_w); +#endif + +switch (mode) { + + case 0: default: + sprintf(ligne, "%11.3f %11.3f %11.3f %11.3f", + values[0], values[1], values[2], values[3]); +#if TRACE + fprintf(stderr, "%s [%s]\n", __func__, ligne); +#endif + break; + + case 1: + for(foo=0; foo<4; foo++) { + tag = "ATOX"[foo]; + idx = (int)(values[foo]*coef_w); + ligne[idx] = tag; +#if TRACE + fprintf(stderr, "%c %3d ", tag, idx); +#endif + } + ligne[COLS-1] = '\0'; +#if TRACE + fprintf(stderr, "\n"); fflush(stderr); +#endif + + break; + + } + + + +/* poke the text in the curses window */ +// scroll(wf); +waddstr(wf, ligne); waddch(wf, '\n'); +wrefresh(wf); + +return -1; +} +/* ---------------------------------------------------------------- */ +int close_waterfall(WINDOW *wf, int notused) +{ +int foo; + +if (NULL == wf) { + fprintf(stderr, "%s wf is null\n", __func__); + return -1; + } + +foo = delwin(wf); +if (ERR==foo) { + fprintf(stderr, "%s : err on delwin\n", __func__); + return -1; + } +return 0; +} +/* ---------------------------------------------------------------- */ diff --git a/viz/gnuplot/av4v-h.awk b/viz/gnuplot/av4v-h.awk new file mode 100755 index 0000000..115fd44 --- /dev/null +++ b/viz/gnuplot/av4v-h.awk @@ -0,0 +1,34 @@ +#!/usr/bin/awk -f + +BEGIN { + flag_debut = 1; + lasthour = 0; + cumul = 0.0; + compte = 0; + } + +# iterate over all the input lines + { + if (flag_debut) { + debut = $1 + flag_debut = 0 + } + heures = int(($1-debut) / 3600); + if (heures == lasthour) { + val = ($2 + $3 + $4 + $5); + cumul += val; + compte += 4; + } + else { + val = cumul /compte; + print $1, val; + lasthour = heures; + cumul = 0; + compte = 0; + } + } + + + + + diff --git a/viz/gnuplot/average4v.awk b/viz/gnuplot/average4v.awk new file mode 100755 index 0000000..55d7eaf --- /dev/null +++ b/viz/gnuplot/average4v.awk @@ -0,0 +1,34 @@ +#!/usr/bin/awk -f + +BEGIN { + flag_debut = 1; + lastminute = 0; + cumul = 0.0; + compte = 0; + } + +# iterate over all the input lines + { + if (flag_debut) { + debut = $1 + flag_debut = 0 + } + minutes = int(($1-debut) / 60); + if (minutes == lastminute) { + val = ($2 + $3 + $4 + $5); + cumul += val; + compte += 4; + } + else { + val = cumul /compte; + print minutes, val; + lastminute = minutes; + cumul = 0; + compte = 0; + } + } + + + + + diff --git a/viz/gnuplot/plot-one.sh b/viz/gnuplot/plot-one.sh new file mode 100755 index 0000000..ff987c8 --- /dev/null +++ b/viz/gnuplot/plot-one.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +INFILE="../../serial/foo.dat" +NBLINES=1600 +TMPFILE="/tmp/dd2data.$$" +IMAGE="av4v-m.png" + +tail -${NBLINES} ${INFILE} | ./average4v.awk > ${TMPFILE} + +gnuplot << __EOC__ +set term png size 1280,420 +set output "${IMAGE}" +set grid +set title "Average on the last ${NBLINES} samples" +set xlabel "Minutes" +set ylabel "Température" +set yrange [ 5.0 : 30.0] +plot "${TMPFILE}" with lines +__EOC__ + +tail -20 ${TMPFILE} + diff --git a/viz/gnuplot/plot-two.sh b/viz/gnuplot/plot-two.sh new file mode 100755 index 0000000..b177d1a --- /dev/null +++ b/viz/gnuplot/plot-two.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +INFILE="../../serial/foo.dat" +NBLINES=60000 +TMPFILE="/tmp/dd2data.$$" +IMAGE="av4v-h.png" + +tail -${NBLINES} ${INFILE} | ./av4v-h.awk > ${TMPFILE} + +gnuplot << __EOC__ +set term png size 1280,420 +set output "${IMAGE}" +set grid +set title "Hourly average on the last ${NBLINES} samples" +set xlabel "Heures" +set ylabel "Temperature" +set yrange [ 0.0 : 30.0] + +set xdata time +set timefmt "%s" +set format x "%d, %H:%M" + +plot "${TMPFILE}" using 1:2 title "celcius" with lines +__EOC__ + +cat -n ${TMPFILE} | tail -20 + diff --git a/viz/pg/README.md b/viz/pg/README.md new file mode 100644 index 0000000..68198d4 --- /dev/null +++ b/viz/pg/README.md @@ -0,0 +1,3 @@ +# Visualisation avec PyGame + +Welcome on-board, Claire !