je vais tout casser ?

This commit is contained in:
tTh 2019-04-01 20:49:13 +02:00
commit 975e501d9b
62 changed files with 2672 additions and 159 deletions

18
.gitignore vendored
View File

@ -2,9 +2,25 @@ a.out
*.o *.o
fake-values fake-values
essai essai
foo.dat
serial/t 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

26
BUILD.txt Normal file
View File

@ -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.

View File

@ -2,19 +2,20 @@
# must be run with gnu make # must be run with gnu make
# #
CC = gcc CC = gcc
CCOPT = -Wall -g
CCOPT = -Wall -g CLIB = core/libdd2m-core.a viz/libdd2m-viz.a
all: essai fake-values 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 essai: essai.c Makefile $(CLIB)
gcc ${CCOPT} -c $< $(CC) ${CCOPT} $< $(CLIB) ${O} -lncurses -o $@
fake-values: fake-values.c funcs.o Makefile fake-values: fake-values.c Makefile $(CLIB)
gcc ${CCOPT} $< funcs.o -o $@ $(CC) ${CCOPT} $< $(CLIB) -o $@
# ---------------------------------------------

View File

@ -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 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 automate de contrôle de l'enceinte thermostatée. Nous devrons
<<<<<<< HEAD
surveiller température et humidité du dd2, et température du surveiller température et humidité du dd2, et température du
confinement biologique. 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 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 `#tetalab` sur le réseau Freenode et/ou les rencontres du mercredi
soir au DD2, à Mixart-Myrys. 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

36
core/Makefile Normal file
View File

@ -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 > $@

24
core/config.h Normal file
View File

@ -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);
/* ---------------------------------------------------------------- */

16
core/dd2-monitor.conf Normal file
View File

@ -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
# --------------------------------------------------

17
core/lut1024.h Normal file
View File

@ -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);
/* ---------------------------------------------------------------- */

70
core/lut1024f.c Normal file
View File

@ -0,0 +1,70 @@
/*
* LUT 1024 -> FLOAT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}
/* ---------------------------------------------------------------- */

10
core/mklut.pl Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/perl
my $foo;
print "LUT1024F\n";
for ($foo=0; $foo<1024; $foo++) {
print rand()*3.30, "\n";
}
0;

112
core/parseconf.c Normal file
View File

@ -0,0 +1,112 @@
/*
* core/parseconf.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}
/* ---------------------------------------------------------------- */

38
core/sysmetrics.c Normal file
View File

@ -0,0 +1,38 @@
/*
* core/sysmetrics.c
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}
/* --------------------------------------------------------------- */

4
core/sysmetrics.h Normal file
View File

@ -0,0 +1,4 @@
int get_loadavg(float *where);

73
core/t.c Normal file
View File

@ -0,0 +1,73 @@
/*
* main de test des core functions
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#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;
}
/* ---------------------------------------------------------------- */

View File

@ -1,5 +1,5 @@
/* /*
* funcs.c * core/utils.c
*/ */
#include <stdio.h> #include <stdio.h>
@ -9,8 +9,6 @@
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include "funcs.h"
extern int verbosity; extern int verbosity;
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
@ -25,9 +23,11 @@ return v1 ^ v2;
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
int random1000(int type) int random1000(int type)
{ {
int value; int value, foo;
if (verbosity)
fprintf(stderr, ">>> %s(%d)\n", __func__, type); #if DEBUG_LEVEL > 1
fprintf(stderr, ">>> %s(%d)\n", __func__, type);
#endif
switch (type) { switch (type) {
case 0: case 0:
@ -36,6 +36,12 @@ switch (type) {
case 1: case 1:
value = (rand()%1000 + rand()%1000) / 2; value = (rand()%1000 + rand()%1000) / 2;
break; break;
case 4:
value = 0;
for (foo=0; foo<4; foo++)
value += rand() % 1000;
value /= 4;
break;
default: default:
value = -1; value = -1;
break; break;
@ -43,9 +49,7 @@ switch (type) {
return value; return value;
} }
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */
double dtime(void) double dtime(void)
{ {
struct timeval tv; 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; 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;
}
/* --------------------------------------------------------------- */ /* --------------------------------------------------------------- */

8
core/utils.h Normal file
View File

@ -0,0 +1,8 @@
/*
* core/utils.h
*/
int seed_my_rand(int foo);
int random1000(int type);
double dtime(void);

7
core/wtf.txt Normal file
View File

@ -0,0 +1,7 @@
Il est temps de se reprendre en main

81
doc/automate.tex Normal file
View File

@ -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.
% -------------------------------------------------------------------

View File

@ -1,5 +1,4 @@
\documentclass[a4paper,11pt]{article}
\documentclass[a4paper,12pt]{article}
% \listfiles % pour le debug % \listfiles % pour le debug
@ -8,13 +7,14 @@
\usepackage[T1]{fontenc} \usepackage[T1]{fontenc}
% XXX \usepackage{lipsum} % XXX \usepackage{lipsum}
\usepackage{makeidx} \usepackage{makeidx}
\usepackage{listings}
% \usepackage{color} % \usepackage{color}
% \usepackage{url} % \usepackage{url}
\usepackage{xspace} \usepackage{xspace}
\usepackage[verbose]{layout} \usepackage[verbose]{layout}
\makeindex \makeindex
\setlength{\parskip}{0.25cm plus 0.25cm} % \setlength{\parskip}{0.16cm plus 0.16cm}
% ------------------------------------------------------------------- % -------------------------------------------------------------------
\title{DD2 Monitoring} \title{DD2 Monitoring}
@ -25,6 +25,12 @@
\pagebreak \pagebreak
% \setlength{\parskip}{0.01cm plus 0.01cm}
\tableofcontents
% \setlength{\parskip}{0.16cm plus 0.16cm}
\pagebreak
% ------------------------------------------------------------------- % -------------------------------------------------------------------
\section{Introduction} \section{Introduction}
@ -41,45 +47,85 @@ arrivons parfois à le faire.
La première étape sera donc la mise au point d'un \textsl{cadriciel} 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 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 Les valeurs à mesurer étant de diverses natures, nous aurons l'occasion
de découvrir plein de capteurs différents : température, humidité, de découvrir plein de capteurs différents : température, humidité,
\texttt{pH}, transparence, lumière, vibrations\dots \texttt{pH}, transparence, lumière, vibrations\dots
% -------------------------------------------------------------------
\vspace{4cm}
Bonne lecture.
% -------------------------------------------------------------------
% ===================================================================
\pagebreak
\section{Présentation générale} \section{Présentation générale}
\subsection{Capteurs} \index{capteurs} \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 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 Par la suite, nous pourrons récupérer
en provenance de l'automate de contrôle de l'enceinte. Notre dd2monitor (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, 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} \subsection{Stockage}
Pour entreposer toutes ces valeurs numériques, il existe plusieurs Pour entreposer toutes ces valeurs numériques, il existe plusieurs
choix, et nous allons en évaluer quelques uns : choix, et nous allons en évaluer quelques uns~:
\texttt{rrdb}\index{rrdb},
\texttt{influxdb}\index{influxdb} (page \pageref{influxdb}), \texttt{flatfile}\index{flatfile} (page \pageref{flatfile}),
\texttt{gnocchi}\index{gnocchi}... \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} \subsection{Affichage}
Nous allons laisser un petit bac-à-sable pour Fred Fermion\index{nodejs}, 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 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} \section{Outils}
\subsection{Simulations} \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 C'est pour ça qu'il y a déja un générateur de \textit{fake-values} qui
ne demande qu'à grandir. 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} \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} \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 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{<schmod777> 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. Pour commencer presque simple, un exemple d'affichage avec
La première, que nous allons juste entrevoir, passera par le port Gnuplot\index{gnuplot} d'un fichier plat
USB de la carte Arduino. La seconde passera par un port série de température (décrit page \pageref{foo.dat}) :
auxiliaire\footnote{Il y en a 4 sur le 2560} de celle-ci.
\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}
% ------------------------------------------------------------------- % -------------------------------------------------------------------
\input{storages}
\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...
% ------------------------------------------------------------------- % -------------------------------------------------------------------
\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 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} \setlength{\parskip}{0.05cm plus 0.05cm}
\pagebreak \tableofcontents % \pagebreak \tableofcontents
\printindex \printindex

29
doc/detournements.tex Normal file
View File

@ -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.

8
doc/mkdoc.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
pdflatex dd2-monitoring.tex
makeindex dd2-monitoring
pdflatex dd2-monitoring.tex

59
doc/serial.tex Normal file
View File

@ -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}
% ===================================================================

111
doc/storages.tex Normal file
View File

@ -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 ?

92
essai.c
View File

@ -2,35 +2,101 @@
* essai.c * essai.c
*/ */
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <curses.h>
#include <time.h>
#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<nbloops; idx++) {
foo = getline_to(sfd, ligne, 100, 0);
#if DEBUG_LEVEL
if (foo) fprintf(stderr, "get values -> %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 main(int argc, char *argv[])
{ {
int opt, foo; int opt, foo;
int type = 0; int serial_in;
double loads[3]; char *device = "/dev/ttyACM0";
while ((opt = getopt(argc, argv, "vst:")) != -1) { while ((opt = getopt(argc, argv, "d:hv")) != -1) {
switch (opt) { switch (opt) {
case 'd': device = optarg; break;
case 'h': help(0); break;
case 'v': verbosity++; break; case 'v': verbosity++; break;
case 's': srand(getpid()); break;
case 't': type = atoi(optarg); break;
default: 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; return 0;
} }

View File

@ -6,21 +6,28 @@ DATAFILE=/tmp/fake-datafile
> ${DATAFILE} > ${DATAFILE}
for s in $(seq 1 1000) for s in $(seq 1 1000)
do do
v=$(./fake-values -s -t 1 2> /dev/null) v=$(./fake-values -s -t 4)
echo $s $v >> ${DATAFILE} echo $s $v >> ${DATAFILE}
done done
tail -5 ${DATAFILE}
#----- do dome useless computations #----- do dome useless computations
awk ' awk '
NR==1 { debut = $2 } NR==1 {
debut = $2
}
{ {
# print $2-debut, $3 # print $2-debut, $3
v = int($3/25); v = int($3/35);
bucket[v]++; bucket[v]++;
} }
END { END {
for (v=0; v<40; v++) { for (v=0; v<40; v++) {
for (foo=0; foo<bucket[v]; foo++) { printf "%4d ", v;
for (foo=0; foo<bucket[v]/2; foo++) {
printf "-"; printf "-";
} }
print "*" print "*"

View File

@ -1,12 +1,14 @@
/* /*
* fake-values.c * fake-values.c
* -------------
* useless software
*/ */
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include "funcs.h" #include "core/utils.h"
int verbosity; int verbosity;
@ -30,7 +32,7 @@ if (verbosity > 1) {
fprintf(stderr, "fake values - %s %s\n", __DATE__, __TIME__); 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; return 0;
} }

13
funcs.h
View File

@ -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[]);

View File

@ -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 _The problem that [Gnocchi](https://gnocchi.xyz/) solves is the storage and indexing of
time series data and resources at a large scale. 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 This is useful in modern cloud platforms which are not only huge
but also are dynamic and potentially multi-tenant. but also are dynamic and potentially multi-tenant.
Gnocchi takes all of that into account._ 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 :)

View File

@ -12,7 +12,10 @@ purpose-built platform that InfluxData provides._
# On essaye ? # 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. une visualisation dynamique des données en lancer de rayon.
Projet ambitieux ? Non, la suite sera bien pire. Projet ambitieux ? Non, la suite sera bien pire.

13
influxdb/create.sh Executable file
View File

@ -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

View File

@ -2,6 +2,9 @@
use strict; use strict;
my $host = "localhost";
my $port = 8086;
print "injecteur v 0\n"; print "injecteur v 0\n";
0; 0;

View File

@ -1,3 +1,39 @@
<<<<<<< HEAD
## Round Robin Database ## 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.

6
rrdb/commun.sh Normal file
View File

@ -0,0 +1,6 @@
#
# commun definitions for rrdb tests
#
export RRDB=$HOME/TMP/tests.rrd

21
rrdb/create.sh Executable file
View File

@ -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

29
rrdb/getvalues.sh Executable file
View File

@ -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}

14
rrdb/insert.sh Executable file
View File

@ -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}

11
rrdb/mkgraph.sh Executable file
View File

@ -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

View File

@ -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 serial.o: serial.c serial.h Makefile
gcc ${OPT} -c $< gcc ${OPT} -c $<
t: t.c serial.o Makefile funcs.o: funcs.c serial.h Makefile
gcc ${OPT} $< serial.o -o $@ gcc ${OPT} -c $<
# ---------------------------------------------------
t: t.c Makefile ${OBJS}
gcc ${OPT} $< ${OBJS} -o $@

View File

@ -1,14 +1,45 @@
# Serial Input # Serial Input
But premier de ce module : recevoir les données fournies par l'automate But premier de ce module : recevoir les données fournies par l'automate
de contrôle du phytotron. de contrôle du phytotron.
Ayant déja pratiqué ce genre de chose pour un déja ancien Ayant déja pratiqué ce genre de chose (recevoir des données par rs232)
[projet](http://art.dinorama.fr/bdf/) 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, 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. 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.

29
serial/essai.sh Executable file
View File

@ -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}

88
serial/funcs.c Normal file
View File

@ -0,0 +1,88 @@
#include <stdio.h>
#include <string.h>
#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;
}
/* ---------------------------------------------------------------- */

View File

@ -1,7 +1,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h>
#include <sys/select.h> #include <sys/select.h>
#include <string.h>
#include <unistd.h> //Used for UART #include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART #include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART #include <termios.h> //Used for UART
@ -39,7 +41,7 @@ int baudbits;
struct termios options; struct termios options;
#if DEBUG_LEVEL #if DEBUG_LEVEL
fprintf(stderr, "%s ( %s %d )\n", __func__, port, baudrate); fprintf(stderr, ">>> %s ( %s %d )\n", __func__, port, baudrate);
#endif #endif
// OPEN THE UART // OPEN THE UART
// The flags (defined in fcntl.h): // 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 // shall not cause the terminal device to become the controlling terminal
// for the process. // for the process.
uart0 = open(port, O_RDWR | O_NOCTTY); uart0 = open(port, O_RDONLY | O_NOCTTY);
if (uart0== -1) if (uart0 < 0)
{ {
perror("unable to open uart "); perror("unable to open uart ");
return -1; return -1;
@ -84,32 +86,44 @@ if (uart0== -1)
baudbits = baudrate2const(baudrate); baudbits = baudrate2const(baudrate);
#if DEBUG_LEVEL #if DEBUG_LEVEL > 1
fprintf(stderr, "%d -> 0x%04x\n", baudrate, baudbits); fprintf(stderr, "%d -> 0x%04x\n", baudrate, baudbits);
#endif #endif
memset(&options, 0, sizeof(options));
tcgetattr(uart0, &options); tcgetattr(uart0, &options);
options.c_cflag = baudbits | CS8 | CLOCAL | CREAD; //<Set baud rate options.c_cflag = baudbits | CS8 | CLOCAL | CREAD;
options.c_iflag = IGNPAR; options.c_iflag = IGNPAR;
options.c_oflag = 0; options.c_oflag = 0;
options.c_lflag = 0; options.c_lflag = 0;
options.c_cc[VMIN] = 1; /* ask for blocking read */
tcflush(uart0, TCIFLUSH); tcflush(uart0, TCIFLUSH);
tcsetattr(uart0, TCSANOW, &options); tcsetattr(uart0, TCSANOW, &options);
tcflush(uart0, TCIFLUSH); /* do it again, sam */
return uart0; return uart0;
} }
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* /*
* this function have NO timeout ! * this function have NO timeout !
* blocking read is not blocking, wtf ?
*/ */
int getbyte(int fd) int getbyte(int fd)
{ {
unsigned char byte; unsigned char byte;
int foo; int foo;
byte = 0;
foo = read(fd, &byte, 1); foo = read(fd, &byte, 1);
if (1 != foo) if (1 != foo)
{ {
perror("read a byte"); fprintf(stderr, "byte %d rd %d errno %d\n",
byte, foo, errno);
return -1; return -1;
} }
return (int)byte; return (int)byte;
@ -124,11 +138,10 @@ struct timeval timeout;
fd_set rfds; fd_set rfds;
int retval; int retval;
timeout.tv_sec = to_ms / 1000; timeout.tv_sec = to_ms / 1000;
timeout.tv_usec = (to_ms % 1000) * 1000; timeout.tv_usec = (to_ms % 1000) * 1000;
#if DEBUG_LEVEL > 1
#if DEBUG_LEVEL fprintf(stderr, "timeout %6d is %4ld.%6ld\n", to_ms,
fprintf(stderr, "timeout %6d is %4ld %6ld\n", to_ms,
timeout.tv_sec, timeout.tv_usec); timeout.tv_sec, timeout.tv_usec);
#endif #endif
@ -136,37 +149,91 @@ FD_ZERO (&rfds);
FD_SET (fd, &rfds); FD_SET (fd, &rfds);
retval = select(1, &rfds, NULL, NULL, &timeout); retval = select(1, &rfds, NULL, NULL, &timeout);
#if DEBUG_LEVEL #if DEBUG_LEVEL
fprintf(stderr, "%s : select -> %d\n", __func__, retval); fprintf(stderr, "%s : select on fd %d -> %d\n", __func__, fd, retval);
#endif #endif
retval = retval < 0 ? -1 : retval;
switch (retval) { switch (retval) {
case -1: case -1:
fprintf(stderr, "omg ?\n"); fprintf(stderr, "omg ?\n");
byte = -1; retval = -1;
break; break;
case 0: case 0:
fprintf(stderr, "timeout\n"); fprintf(stderr, "timeout %ld.%ld\n",
byte = -99; timeout.tv_sec, timeout.tv_usec);
retval = -99;
break; break;
default: default:
fprintf(stderr, "%s default -> %d\n", __func__, retval);
if (retval==fd) { if (retval==fd) {
read(fd, &byte, 1); read(fd, &byte, 1);
fprintf(stderr, "got 0x%02x\n", byte); fprintf(stderr, "got 0x%02x\n", byte);
retval = byte;
} }
else { else {
fprintf(stderr, "%d bad fd ?\n", retval); fprintf(stderr, "%d bad fd ?\n", retval);
byte = -3; retval = -3;
} }
break; 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;
}
/* -------------------------------------------------------------------- */

View File

@ -6,10 +6,19 @@
int prepare_UART(char *port, int bauds); int prepare_UART(char *port, int bauds);
int getbyte(int fd); int getbyte(int fd); /* really brotched func */
/* timeout is exprimed in milliseconds. */ /* timeout is exprimed in milliseconds. */
int getbyte_to (int fd, int to_ms); 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]);

View File

@ -1,29 +1,114 @@
/*
* Experiments with the serial input
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <time.h> #include <time.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <errno.h>
#include <termios.h> //Used for UART
#include <getopt.h>
#include "serial.h" #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<iters; count++) {
foo = getline_to(sfd, ligne, 100, 0);
//
if (verbosity) {
/* fprintf(stderr, "getline #%d on %d -> %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 main (int argc, char *argv[])
{ {
int serial_in; int serial_in;
int byte, foo, to; char *device = "/dev/ttyACM0";
int nbre, speed, opt;
serial_in = prepare_UART("/dev/ttyS0", 9600); /* set some default values */
fprintf(stderr, "prepare uart -> %d\n", serial_in); verbosity = 0;
nbre = 25;
speed = 9600;
for (foo=0; foo<20; foo++) { while ((opt = getopt(argc, argv, "d:n:vh")) != -1) {
to = (foo+1) * 666; switch (opt) {
byte = getbyte_to(serial_in, to); case 'v': verbosity++; break;
if (byte < 0) { case 'n': nbre = atoi(optarg); break;
fprintf(stderr, "get byte : err is %d\n", byte); 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; return 0;
} }
/* ---------------------------------------------------------------- */

View File

@ -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<NBVAL; foo++) {
ptr[foo] = analogRead(adc_pins[foo]);
delay(100);
}
digitalWrite(LED_BUILTIN, LOW);
}
/* -------------------------------------------------- */
void sendvalues(short *ptr)
{
int foo;
Serial.print("T");
for (foo=0; foo<NBVAL; foo++) {
Serial.print(" ");
Serial.print(ptr[foo]);
}
Serial.print("\n");
}
/* -------------------------------------------------- */
void update_and_send(void)
{
short values[NBVAL];
updatevalues(values);
sendvalues(values);
}
/* ================================================== */
void loop() {
update_and_send();
delay(DELAI);
}
/* -------------------------------------------------- */

View File

@ -0,0 +1,55 @@
/*
* simulateur de telemesure automate
*/
/* -------------------------------------------------- */
#define NBVAL 4
#define DELAI 1789
int values[NBVAL];
/* -------------------------------------------------- */
void setup() {
int foo;
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
for (foo=0; foo<NBVAL; foo++) {
values[foo] = 0;
}
}
/* -------------------------------------------------- */
void updatevalues(void)
{
int foo;
for (foo=0; foo<NBVAL; foo++) {
if (rand()%100<42) {
values[foo] += (foo + 1);
}
if (values[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);
}
/* -------------------------------------------------- */

0
storage/Makefile Normal file
View File

0
storage/t.c Normal file
View File

153
viz/curses/7segments.c Normal file
View File

@ -0,0 +1,153 @@
/*
* DD2 Monitoring
*
* ncurses seven segment display
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <ncurses.h>
#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<strlen(buff); idx++) {
aff7segs_digit(win, lig, col+(idx*9), buff[idx]);
}
wrefresh(win);
return 0;
}
/* ---------------------------------------------------------------- */
int aff7segs_float(WINDOW * win, int lig, int col, float value)
{
char buff[10];
int idx;
sprintf(buff, "%6.2f", value);
// mvwaddstr(win, lig-1, col, buff);
for (idx=0; idx<strlen(buff); idx++) {
aff7segs_digit(win, lig, col+(idx*9), buff[idx]);
}
wrefresh(win);
return 0;
}
/* ---------------------------------------------------------------- */

36
viz/curses/Makefile Normal file
View File

@ -0,0 +1,36 @@
# --------------- ***
COPT = -Wall -g -fpic -DTRACE=0
OBJS = ecran.o 7segments.o waterfall.o vumetre.o \
minidigits.o
ALIB = ../libdd2m-viz.a
# --------------- ***
${ALIB}: ${OBJS}
ar r $@ $?
# --------------- ***
ecran.o: ecran.c Makefile ecran.h
gcc $(COPT) -c $<
7segments.o: 7segments.c Makefile ecran.h
gcc $(COPT) -c $<
waterfall.o: waterfall.c Makefile ecran.h
gcc $(COPT) -c $<
vumetre.o: vumetre.c Makefile ecran.h
gcc $(COPT) -c $<
minidigits.o: minidigits.c Makefile ecran.h
gcc $(COPT) -c $<
# --------------- ***
t: t.c Makefile $(ALIB) ecran.h
gcc $(COPT) $< $(ALIB) -lncurses -o $@
# --------------- ***

2
viz/curses/README.md Normal file
View File

@ -0,0 +1,2 @@
# Dataviz with AsciiArt

76
viz/curses/ecran.c Normal file
View File

@ -0,0 +1,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
#include <getopt.h>
#include <ncurses.h>
#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<COLS; foo++)
mvaddch(ligne, foo, c);
standend();
/* refresh(); */
}
/* ---------------------------------------------------------------- */
/* make display on the standard screen (stdscr) */
int fond_ecran(char *title)
{
char *tp;
struct utsname utsn;
int foo;
char buffer[200];
tp = " DD2 Monitoring by tTh 2019 ";
if (NULL != title) tp = title;
barre_inverse(' ', 0);
standout();
mvaddstr(0, 2, tp);
if (verbosity) {
sprintf(buffer, " ecr: %dx%d ", COLS, LINES);
fprintf(stderr, "%s ==> %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;
}
/* ---------------------------------------------------------------- */

23
viz/curses/ecran.h Normal file
View File

@ -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);

107
viz/curses/minidigits.c Normal file
View File

@ -0,0 +1,107 @@
/*
* DD2 Monitoring
*
* mini digits
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <getopt.h>
#include <ncurses.h>
#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<strlen(chaine); foo++) {
minidigit_0(stdscr, lig, col+foo*8, chaine[foo], 0);
}
return 0;
}
/* ---------------------------------------------------------------- */

240
viz/curses/t.c Normal file
View File

@ -0,0 +1,240 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <getopt.h>
#include <ncurses.h>
#include "ecran.h"
int verbosity;
/* ---------------------------------------------------------------- */
int demo_minidigits(int nbl, int k)
{
int loop, foo;
char chaine[100];
for (loop=0; loop<nbl; loop++) {
sprintf(chaine, "== %06X ==", loop);
message(chaine);
sprintf(chaine, ".%08d.", rand()%1000000);
for (foo=0; foo<10; foo++) {
minidigit_0(stdscr, 5, 2+foo*8, chaine[foo], 0);
}
wrefresh(stdscr);
usleep(350*1000);
for (foo=0; foo<10; foo++) {
minidigit_0(stdscr, 5, 2+foo*8, ' ', 0);
}
minidigit_HMS(stdscr, 15, 9, 0);
wrefresh(stdscr);
usleep(250*1000);
}
return -1;
}
/* ---------------------------------------------------------------- */
int demo_composite(int nbl, int k)
{
int loop, foo;
short sval;
char ligne[120];
float fval;
for (loop=0; loop<nbl; loop++) {
sval = (short)((loop % 1024)-512);
foo = aff7segs_short(stdscr, 5, 3, sval);
if (foo) message("KRKRK aff7 short");
fval = (float)sval / 1024.0;
foo = aff7segs_float(stdscr, 16, 3, fval);
if (foo) message("KRKRK aff7 float");
fval = fabs(fval);
foo = vumetre_0(stdscr, 29, 5, fval, COLS-10);
sprintf(ligne, "%04x", loop);
mvaddstr(2, 1, ligne);
refresh();
usleep(200*1000);
}
return 0;
}
/* ---------------------------------------------------------------- */
void demo_7segments(int nbl, int notused)
{
int loop, idx, foo;
char ligne[120];
time_t temps;
struct tm *p_tms;
for (loop=0; loop<nbl; loop++) {
sprintf(ligne, "%04x", loop);
mvaddstr(2, 1, ligne);
for (idx=0; idx<strlen(ligne); idx++) {
aff7segs_digit(stdscr, 3, 10+(idx*9), ligne[idx]);
}
sprintf(ligne, "%.3f", drand48());
mvaddstr(13, 1, ligne);
for (idx=0; idx<strlen(ligne); idx++) {
aff7segs_digit(stdscr, 14, 10+(idx*9), ligne[idx]);
}
if (verbosity && (loop%2)) {
temps = time(NULL);
p_tms = localtime(&temps);
foo = strftime(ligne, 100, "%F %H:%M", p_tms);
// sprintf(ligne, "%12ld | %s", temps, ctime(&temps));
message(ligne);
}
refresh();
usleep(400*1000);
}
}
/* ---------------------------------------------------------------- */
void demo_vumetres(int nbl, int notused)
{
int loop, idx;
int hpos;
char ligne[100];
float value;
time_t temps;
for (loop=0; loop<nbl; loop++) {
value = (float)loop / (float)nbl;
for (idx=0; idx<8; idx++) {
hpos = 4 * (idx+1);
value = drand48();
if (idx<4) vumetre_0(stdscr, hpos, 12, value, 60);
else vumetre_1(stdscr, hpos, 12, value, 60);
}
if (verbosity && (loop%2)) {
temps = time(NULL);
sprintf(ligne, "%12ld | %s", temps, ctime(&temps));
message(ligne);
}
refresh();
usleep(200*1000);
}
}
/* ---------------------------------------------------------------- */
void demo_waterfall(int nbl, int k)
{
int loop, foo;
char line[100];
WINDOW *water;
static float rvals[4];
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 200 * 1000 * 1000;
water = open_waterfall("premier essai", 0);
for (loop=0; loop<nbl; loop++) {
sprintf(line, " %06X %04X ", loop, rand()&0xffff);
message(line);
wrefresh(stdscr);
for (foo=0; foo<4; foo++) {
if (rand()%100<42) {
rvals[foo] += 4.04 * (foo + 1);
}
if (rvals[foo] > 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;
}
/* ---------------------------------------------------------------- */

88
viz/curses/vumetre.c Normal file
View File

@ -0,0 +1,88 @@
/*
* DD2 Monitoring
*
* ncurses seven segment display
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <getopt.h>
#include <ncurses.h>
#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<larg; foo++) {
c = col + foo + 2;
if (foo<posc) {
wstandout(win);
mvwaddch(win, lig, c, '#');
wstandend(win);
}
else {
mvwaddch(win, lig, c, ' ');
}
if (!(foo%4)) {
mvwaddch(win, lig-1, c, '\\');
mvwaddch(win, lig+1, c, '/');
}
}
wrefresh(win);
return 0;
}
/* ---------------------------------------------------------------- */
int vumetre_1(WINDOW *win, int lig, int col, float val, int larg)
{
int foo, posc;
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);
sprintf(ligne, "%6.3f", val);
mvwaddstr(win, lig, 2, ligne);
for (foo=0; foo<larg; foo++) {
if (foo < posc) {
wstandout(win);
mvwaddch(win, lig, col+foo, '|');
wstandend(win);
}
else {
mvwaddch(win, lig, col+foo, '|');
}
if (foo & 2) {
mvwaddch(win, lig-1, col+foo, '|');
mvwaddch(win, lig+1, col+foo, '|');
}
}
wrefresh(win);
return 0;
}
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */

117
viz/curses/waterfall.c Normal file
View File

@ -0,0 +1,117 @@
/*
* DD2 Monitoring
*
* ncurses waterfall interface
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <ncurses.h>
#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;
}
/* ---------------------------------------------------------------- */

34
viz/gnuplot/av4v-h.awk Executable file
View File

@ -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;
}
}

34
viz/gnuplot/average4v.awk Executable file
View File

@ -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;
}
}

22
viz/gnuplot/plot-one.sh Executable file
View File

@ -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}

27
viz/gnuplot/plot-two.sh Executable file
View File

@ -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

3
viz/pg/README.md Normal file
View File

@ -0,0 +1,3 @@
# Visualisation avec PyGame
Welcome on-board, Claire !