Aller au contenu

Script Perl Fig


Messages recommandés

Salut,

Pour monitorer des parametres de ma piscine, j'ai un automate, qui envoie des données a un convertisseur RS232 vers Wifi. J'accede aux données en envoyant une requete UDP de facon reguliere, et je les stocke sur une db mySQL (clef primaire, date et heure). J'ai voulu faire un script qui demarre et tourne en boucle sur le synology. J'utilise du Perl.

Le script et sa procedure de lancement et d'arret semblent bien fonctionner. Le probleme c'est que

Voila le script mapiscine.pl pour info (c'est pour communiquer avec un Crouzet Millenium, si ca vous interesse) :

#!/opt/bin/perl
use strict;

use POSIX qw(strftime);
use IO::Socket::INET;
use DBI;
use sigtrap 'handler' => &sigtrap, 'HUP', 'INT','ABRT','QUIT','TERM';

my ($socket,$data,$dbh,$t_dt,$query,$sth,$temp_air,$temp_eau,$press_filtr,$tps_filtr,$tps_robot,$filtr_state,@data_table,$flags);
open(FH,">>logmapiscine");
print FH "debutn";
sleep(60);

#  Creation du socket UDP
$socket=new IO::Socket::INET (
PeerAddr=>'192.168.1.254:55555',
LocalPort => '55555',
Proto=>'udp'
) or die "Erreur creation du socket UDPn";
print FH "socket creen";

#Connexion a la DB
$dbh = DBI->connect("DBI:mysql:database=db_mapiscine;host=localhost", "root", "pouet", {AutoCommit => 1}) or die "Connexion impossible a la BDDn";
$dbh->{mysql_auto_reconnect} = 1;
print FH "db connecteen";

#init Millenium
$data=":010300006D00018Ern";
$socket->send($data);
$data=<$socket>;
print FH "mill initn";

#preparation requete insertion donnee
$query = <<"SQL";
INSERT INTO `db_mapiscine`.`table_mapiscine` (`date_time`, `temp_air`, `temp_eau`, `press_filtr`, `tps_filtr`, `tps_robot`, `filtr_state`) VALUES (?,?,?,?,?,?,?);
SQL


$sth = $dbh->prepare($query) or die "Erreur preparation";
print FH "req prepareen";

while(1)
{
#request data
$data=":04030000FF1810D2rn";
$socket->send($data);
print FH "data envoyeesn";

#read operation
$data=<$socket>;
print FH "data recup $datan";

#Parse data
$data=~ s/^.......//;
$data=~ s/...$//;
$data =~s/(.)/sprintf("%X ",ord($1) & 0x7F)/eg;
$data =~ s/([a-fA-F0-9][a-fA-F0-9])/chr(hex($1))/eg;
@data_table = split(/ /,$data);

$t_dt=strftime('%Y-%m-%d %H:%M:%S',localtime);
$temp_eau=hex($data_table[0].$data_table[1].$data_table[2].$data_table[3])/10;
$temp_air=hex($data_table[8].$data_table[9].$data_table[10].$data_table[11])/10;
$press_filtr=hex($data_table[16].$data_table[17].$data_table[18].$data_table[19])/100;
$tps_filtr=hex($data_table[4].$data_table[5].$data_table[6].$data_table[7]);
$tps_robot=hex($data_table[12].$data_table[13].$data_table[14].$data_table[15])/100;
$flags=$data_table[20].$data_table[21].$data_table[22].$data_table[23];
$filtr_state=($flags & 0x1) ? 1 : 0;

print FH "$t_dt $temp_airn";

$sth->execute($t_dt,$temp_air,$temp_eau,$press_filtr,$tps_filtr,$tps_robot,$filtr_state) or die "Erreur execution";
print FH "requete executeen";

sleep(30);
print FH "fin bouclen";
}

sub sigtrap(){
print FH "inter detectn";
 $dbh->disconnect();
 $socket->close();
 close(FH);
print FH "tout ferme intern";
 exit(1);
}

Voila le script lancemapiscine.sh dans /usr/local/etc/rc.d qui assure le lancement du script :

#!/bin/sh
case "$1" in
start)
    ((perl /usr/local/etc/mapiscine.pl) & echo $! > /var/run/mapiscine.pid &)
    ;;
stop)
    kill `cat /var/run/mapiscine.pid`
    rm -f /var/run/mapiscine.pid
    ;;
*)
    echo "Usage: `basename $0` {start|stop}" >&2
    exit 64
    ;;
esac
exit 0

Mon premier souci est que je n'arrive pas a logger quoi que ce soit. J'ai tenté de mettre exec >/tmp/monscript.log 2>&1 au debut de mon script shell... Niet! Idem comme vous pouvez le voir dans le script perl (ecriture dans un fichier des print). En fait çà amrche quand je lance le script manuellement via putty, mais quand je redemarre le NAS (pour simuler le fonctionnement reel) le script ne cree pas les fichiers de log...

Je sais a quelle heure ca plante uniquement a cause de la derniere entrée de la database MySQL. Quand le script plante, il reste malgré tout dans top, mais la db ne s'increment plus.

merci pour votre aide.

Lien vers le commentaire
Partager sur d’autres sites

Lancé au démarrage (tu ne nous a pas dit comment mais ça ne change fondamentalement pas grand chose) l’environnement est minimum, et c'est le perl syno ("/usr/bin/perl", qui m'a souvent provoqué des problèmes louches) et pas celui d'optware qui va être utilisé.

Pour forcer l'utilisation du perl optware:

  • mettre le bon PATH *dans* le script (PATH=/opt/bin:/bin:/usr/bin)
  • S'assurer aussi que le script est lancé *apres* que le "mount -bind" de "/opt" soit effectué (démarrage optware).

Egalement, il est fort possible que la base de données ne soit pas encore démarrée lorsque le script est lancé au boot.

Sinon, pour comprendre pourquoi le log ne se créée pas, faudrait que tu explique quelle méthode tu utilises pour le démarrage auto du script.

Modifié par CoolRaoul
Lien vers le commentaire
Partager sur d’autres sites

merci pour ta reponse.

Ben pour lancer mon script j'utilise le script lancemapiscine.sh présenté au dessus et mis dans /usr/local/etc/rc.d .

Le script se lance bien au demarrage, je le vois bien incrementer ma BDD. Il ne doit donc pas se lancer trop tot (grace au sleep(60) dans le perl). Le probleme c'est qu'il semble ne plus tourner au bout de quelques heures... Et du coup j'aimerai logger çà.

Lien vers le commentaire
Partager sur d’autres sites

merci pour ta reponse.

Ben pour lancer mon script j'utilise le script lancemapiscine.sh présenté au dessus et mis dans /usr/local/etc/rc.d .

Le script se lance bien au demarrage, je le vois bien incrementer ma BDD. Il ne doit donc pas se lancer trop tot (grace au sleep(60) dans le perl). Le probleme c'est qu'il semble ne plus tourner au bout de quelques heures... Et du coup j'aimerai logger çà.

En tout cas commence par mettre la ligne PATH en début du script shell pour utiliser le perl optware et pas le perl syno (comme j'ai déja dit, celui-la m'a fait des trucs franchement étranges).

Autre chose, je trouve la ligne:

((perl /usr/local/etc/mapiscine.pl) & echo $! > /var/run/mapiscine.pid &) 

un peu tarabiscotée avec tout ces "&" et ces sous-shells imbriqués.

Je ferai comme cela plutôt (et ainsi plus besoin de "/opt/bin" dans le PATH):

/usr/local/etc/mapiscine.pl <&- & # pourquoi pas /usr/local/bin ?
echo $! > /var/run/mapiscine.pid 

(Pas oublier le chmod +x de "/usr/local/etc/mapiscine.pl")

Dernière remarque, dans le perl tu fais:

open(FH,">>logmapiscine");

Sans savoir quel est le répertoire courant, puisque ni le script shell ni le script perl de fait de "cd",

ajouter un "cd /tmp" dans le shell ne peut pas faire de mal sinon tu n'est pas maitre de la localisation du fichier "logmapiscine"

***EDIT***

et essaie de remettre la ligne "exec" pour voir

Modifié par CoolRaoul
Lien vers le commentaire
Partager sur d’autres sites

#!/bin/sh
PATH=/opt/bin:/bin:/usr/bin
cd /tmp
exec >/tmp/monscript.log 2>&1
case "$1" in
start)
    /usr/local/bin/mapiscine.pl <&- &
    echo $! > /var/run/mapiscine.pid
    ;;
stop)
    kill `cat /var/run/mapiscine.pid`
    rm -f /var/run/mapiscine.pid
    ;;
*)
    echo "Usage: `basename $0` {start|stop}" >&2
    exit 64
    ;;
esac
exit 0

Voila le script modifié selon tes conseils!

Je redemarre le NAS et je te dis ce qu'il me fais.

En tous cas merci de tes precieuses infos. franchement j'apprend des trucs!!

Lien vers le commentaire
Partager sur d’autres sites

non non je l'ai bien déplacé, en fait je ne savais pas trop ou le mettre, merci de m'avoir aiguillé!

Alors le script se lance bien je le vois dans top (8850 1 root S 9012 1.7 0.0 /opt/bin/perl /usr/local/bin/mapiscine.pl) , les fichiers logs sont bien créées dans le /tmp (logmapiscine et monscript.log). Ma db s'increment bien.

Par contre les 2 fichiers de log font 0 en taille et sont vide quand je fais un cat dessus. Je pense que le log du script sh n'a rien a dire pour le moment, et que celui du perl est actualisé quand je le ferme ( close(FH) dans l'interruption signal). edit : ok verifié, le logmapiscine se rempli bien lorsque je fais un lancemapiscine.sh stop.

Je verrai bien si ca plante et la je pourrais diagnostiquer vu que j'aurais les logs!

Modifié par lollol
Lien vers le commentaire
Partager sur d’autres sites

Voila les logs :


fin boucle
data envoyees
data recup :0´03±0006²000²003²006´0063000¸003·003²±B
2013-04-09 07:46:51 5
requete executee
fin boucle
data envoyees
data recup :0´03±0006²000²003²006´0063000¸003·003²±B
2013-04-09 07:47:22 5
requete executee
fin boucle
data envoyees
data recup :0´03±0006²000²003²006´0063000¸003·003²±B
2013-04-09 07:47:53 5
requete executee
fin boucle
data envoyees   >> dernière étape du script avant plantage
inter detect >> moi qui arrête le script planté

Donc je pense que le script au bout d'un moment envoie les data par protocole UDP, mais ne reçoit jamais de réponse de la part du pont wifi/RS232 (le paquet se perd ou je ne sais quoi, c'est l’inconvénient d'UDP je crois), et donc attend indéfiniment qu'il reçoive quelque chose.

Je pensais modifier mon script perl pour qu'il refasse une requête si il ne reçoit pas les datas au bout de 1 ou 2 secondes... Je ne sais pas par contre comment faire pour passer la ligne $data=<$socket>; si elle ne reçoit pas de données...

Modifié par lollol
Lien vers le commentaire
Partager sur d’autres sites

Je pensais modifier mon script perl pour qu'il refasse une requête si il ne reçoit pas les datas au bout de 1 ou 2 secondes... Je ne sais pas par contre comment faire pour passer la ligne $data=<$socket>; si elle ne reçoit pas de données...

Je t'ai trouvé ça:

http://stackoverflow.com/questions/4517034/perl-set-read-timeout-in-client-socket

il y a peut-être des solutions plus élégantes, mais si ça marche après tout ...

Lien vers le commentaire
Partager sur d’autres sites

ok merci de ton aide, mais la sur le coup ce soir j'ai pas trop capté leur idée de signal... visiblement il y a une fonction timeout mais celle -ci est désactivée dans IO::Socket....... Je m'y pencherai ce WE pour implémentation, ou alors je passerai en TCP.

Modifié par lollol
Lien vers le commentaire
Partager sur d’autres sites

ok merci de ton aide, mais la sur le coup ce soir j'ai pas trop capté leur idée de signal... visiblement il y a une fonction timeout mais celle -ci est désactivée dans IO::Socket....... Je m'y pencherai ce WE pour implémentation, ou alors je passerai en TCP.

Le code est relativement simple, et indépendant des fonctionnalités de IO::Socket.

Le bloc "eval" positionne un "watchdog" local autour de la fonction "sysread"

La sortie du bloc va se faire dès que l'une des conditions suivantes est réalisée:

  • le timer à expiré (valeur en secondes de la variable "timeout")
  • la lecture du socket à réussi
  • la lecture du socket à échoué sur une autre erreur

Suffit donc de faire la meme chose en remplaçant ta ligne "$data=<$socket>;" grosso modo par quelque chose du genre de:

my $timeout=2;                   # secondes
eval {
    local $SIG{ALRM} = sub { die "alarmn" };
    alarm $timeout;
    $data=<$socket>;
    alarm 0;
};
if ($@) {
    die unless $@ eq "alarmn";   # autre erreur
    # sinon ici timeout atteint
    ....
} else {
    # ici lecture socket reussie si $data != ""
    ....
}
Modifié par CoolRaoul
Lien vers le commentaire
Partager sur d’autres sites

Solution, à mon avis plus élégante, utilisant IO::Select

  • Ajouter au début du script:
    use IO::Select;
    my $timeout=2; # secondes
  • puis juste après l'initialisation du socket:
    my $selection = IO::Select->new($socket); 
  • Enfin, code de lecture de la socket avec timeout:
    if (my @handles = $selection->can_read($timeout)) {
        #  des données sont arrivées sur le socket
        my $handle=shift(@handles);
        $data = <$handle>;
    } else {
        # si on arrive ici, alors la lecture du socket 
        # n'a rien reçu avant l'expiration du timeout
        # a toi de décider comment traiter le cas..
    }
Lien vers le commentaire
Partager sur d’autres sites

Si on prend en compte l'ensemble du code demande/réponse on aboutit à

do {
    $query=":04030000FF1810D2rn";
    $socket->send($query);
    print FH "demande envoyéen";    
    if (my @handles = $selection->can_read($timeout)) {
        #  des données sont arrivées sur le socket
        my $handle=shift(@handles);
        $data = <$handle>;
    };
} until ($data);
 

(reste à améliorer le traitement des erreurs, et éventuellement un compte de "retries" pour ne pas partir dans une boucle infinie en cas de défaillance du convertisseur RS232)

Lien vers le commentaire
Partager sur d’autres sites

vraiment très sympa de m'avoir prémâché le boulot je ne m’attendais pas a tant! :D :D

j'ai donc implémenté le script, je redémarre le NAS et je log toute la nuit pour voir.

Ah oui, je viens de désactiver l'hibernation des disques au bout de 30 minutes, même si je pense que vu que le script tournait en boucle ac ne devait rien changer (il plantait au bout de 5 - 6h en plus).

Lien vers le commentaire
Partager sur d’autres sites

bon ben il tourne toujours ce soir, pas de plantages, merci a toi CoolRaoul ;)

Voici la dernière version :

#!/opt/bin/perl
use strict;

use POSIX qw(strftime);
use IO::Socket::INET;
use IO::Select;
use DBI;
use sigtrap 'handler' => &sigtrap, 'HUP', 'INT','ABRT','QUIT','TERM';

my ($selection,$timeout,$socket,$data,$dbh,$t_dt,$query,$sth,$temp_air,$temp_eau,$press_filtr,$tps_filtr,$tps_robot,$filtr_state,@data_table,$flags);

$timeout=2;
open(FH,">>logmapiscine");
print FH "debutn";
sleep(60);


#  Creation du socket UDP
$socket=new IO::Socket::INET (
PeerAddr=>'192.168.1.254:55555',
LocalPort => '55555',
Proto=>'udp'
) or die "Erreur creation du socket UDPn";
print FH "socket creen";

$selection = IO::Select->new($socket);

#Connexion a la DB
$dbh = DBI->connect("DBI:mysql:database=db_mapiscine;host=localhost", "root", "pouet", {AutoCommit => 1}) or die "Connexion impossible a la BDDn";
$dbh->{mysql_auto_reconnect} = 1;
print FH "db connecteen";

#init Millenium
$data=":010300006D00018Ern";
$socket->send($data);
$data=<$socket>;
print FH "mill initn";

#preparation requete insertion donnee
$query = <<"SQL";
INSERT INTO `db_mapiscine`.`table_mapiscine` (`date_time`, `temp_air`, `temp_eau`, `press_filtr`, `tps_filtr`, `tps_robot`, `filtr_state`) VALUES (?,?,?,?,?,?,?);
SQL


$sth = $dbh->prepare($query) or die "Erreur preparation";
print FH "req prepareen";

while(1)
{

do {
    $query=":04030000FF1810D2rn";
    $socket->send($query);
    print FH "demande envoyéen";
    if (my @handles = $selection->can_read($timeout)) {
    #  des données sont arrivées sur le socket
       my $handle=shift(@handles);
       $data = <$handle>;
       };
     } until ($data);



#Parse data
$data=~ s/^.......//;
$data=~ s/...$//;
$data =~s/(.)/sprintf("%X ",ord($1) & 0x7F)/eg;
$data =~ s/([a-fA-F0-9][a-fA-F0-9])/chr(hex($1))/eg;
@data_table = split(/ /,$data);

$t_dt=strftime('%Y-%m-%d %H:%M:%S',localtime);
$temp_eau=hex($data_table[0].$data_table[1].$data_table[2].$data_table[3])/10;
$temp_air=hex($data_table[8].$data_table[9].$data_table[10].$data_table[11])/10;
$press_filtr=hex($data_table[16].$data_table[17].$data_table[18].$data_table[19])/100;
$tps_filtr=hex($data_table[4].$data_table[5].$data_table[6].$data_table[7]);
$tps_robot=hex($data_table[12].$data_table[13].$data_table[14].$data_table[15])/100;
$flags=$data_table[20].$data_table[21].$data_table[22].$data_table[23];
$filtr_state=($flags & 0x1) ? 1 : 0;

print FH "$t_dt $temp_airn";

$sth->execute($t_dt,$temp_air,$temp_eau,$press_filtr,$tps_filtr,$tps_robot,$filtr_state) or die "Erreur execution";
print FH "requete executeen";

sleep(30);
print FH "fin bouclen";
}

sub sigtrap(){
print FH "inter detectn";
 $dbh->disconnect();
 $socket->close();
 close(FH);
print FH "tout ferme intern";
 exit(1);
}

Modifié par lollol
Lien vers le commentaire
Partager sur d’autres sites

Rejoindre la conversation

Vous pouvez publier maintenant et vous inscrire plus tard. Si vous avez un compte, connectez-vous maintenant pour publier avec votre compte.

Invité
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer.