IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Lire un device sous Linux et ne pas bloquer [PHP 7]


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Futur Membre du Club
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Août 2015
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Finance

    Informations forums :
    Inscription : Août 2015
    Messages : 3
    Par défaut Lire un device sous Linux et ne pas bloquer
    Bonjour,
    Sur un Rapsberry Pi, je dois lire périodiquement les données fournies sur un device : /dev/ttyAMA0.
    Le problème c'est qu'il y a des moments où rien n'est disponible pendant plusieurs heures. Il faut donc que ma lecture ne soit pas bloquante.

    J'ouvre le device comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $inputDevice = '/dev/ttyAMA0';
    $handle = fopen($inputDevice, 'r');
    Et je lis les données comme ceci, caractère par caractère.
    J'ai essayé d'utiliser la fonction feof($handle), mais ça ne fonctionne pas (le programme reste bloqué).

    Il y a sûrement une solution simple, mais comme je débute en php, je ne l'ai pas encore trouvée.

    Merci de votre aide !
    Philippe

  2. #2
    Membre émérite Avatar de Geoffrey74
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mars 2007
    Messages
    515
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2007
    Messages : 515
    Par défaut
    Salut,

    tu as la fonction set_time_limit( int $sec); qui fixe le temps maximum d'exécution d'un script (http://php.net/manual/fr/function.set-time-limit.php)


    Cela va te produire une erreur si le temps est dépassé, et le reste du script ne sera pas exécuté... L'idéal serait que la fonction de lecture se fasse dans un tâche cron, afin qu'elle ne bloque en rien le reste du script, et donc dans ton script tu lis les données de la BDD qui sera renseigner par ta tâche cron.

  3. #3
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 693
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 693
    Par défaut
    Tu peux définir un timeout sur un stream via stream_set_timeout tu peux également définir un stream comme bloquant ou non : via stream_set_blocking

    Donc quelque chose comme ça (non testé , extrait de la doc php):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $handle = fopen($inputDevice, 'r');
    stream_set_timeout($handle , 2);
    $res = fread($handle, 1);
    $info = stream_get_meta_data($handle);
    fclose($fp);
    if ($info['timed_out']) {
         echo 'Délai de connexion dépassé !';
    } else {
         echo $res;
    }
    devrait t'aider
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Futur Membre du Club
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Août 2015
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Finance

    Informations forums :
    Inscription : Août 2015
    Messages : 3
    Par défaut
    Citation Envoyé par grunk Voir le message
    Tu peux définir un timeout sur un stream via stream_set_timeout
    Merci, j'ai mis en oeuvre le timeout sur le stream et ça a l'air de fonctionner comme souhaité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Accès au périphérique de lecture
        $inputDevice = '/dev/ttyAMA0';
        $handle = fopen($inputDevice, 'r');
        // Timeout pour ne pas bloquer si aucune donnée n'est disponible
        stream_set_blocking($handle, true);
        stream_set_timeout($handle, 5);
        $info = stream_get_meta_data($handle);
     
        // On attend la fin d'une trame pour commencer avec la trame suivante
        $char = '';
        while (!$info['timed_out'] && $char != chr(2)) {
            $char = fread($handle, 1);
        }

  5. #5
    Invité de passage
    Homme Profil pro
    Chef de projet
    Inscrit en
    Janvier 2020
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Chef de projet

    Informations forums :
    Inscription : Janvier 2020
    Messages : 1
    Par défaut
    Citation Envoyé par Philou_69 Voir le message
    Merci, j'ai mis en oeuvre le timeout sur le stream et ça a l'air de fonctionner comme souhaité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Accès au périphérique de lecture
        $inputDevice = '/dev/ttyAMA0';
        $handle = fopen($inputDevice, 'r');
        // Timeout pour ne pas bloquer si aucune donnée n'est disponible
        stream_set_blocking($handle, true);
        stream_set_timeout($handle, 5);
        $info = stream_get_meta_data($handle);
     
        // On attend la fin d'une trame pour commencer avec la trame suivante
        $char = '';
        while (!$info['timed_out'] && $char != chr(2)) {
            $char = fread($handle, 1);
        }
    Philou,

    Merci pour le partage.

    Ca ne fonctionne pas pour moi si aucun charactère n'arrive sur /dev/ttyAMA0. Je reste bloqué sur fread().
    Est-ce que tu as testé ce cas là (qui était l'objectif du timeout, j'imagine)?

    Merci par avance pour ton retour.

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Août 2015
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 66
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Finance

    Informations forums :
    Inscription : Août 2015
    Messages : 3
    Par défaut
    Citation Envoyé par Arnøø Voir le message
    Est-ce que tu as testé ce cas là (qui était l'objectif du timeout, j'imagine)?
    Cela fait un moment, je ne me souviens plus si j'ai testé , sans doute.

    Mais mon code a évolué, j'ai encore rencontré un blocage, notamment avec des "nul" et j'ai mis en place un système de surveillance qui a prouvé son efficacité : plus de blocage ou de plantage depuis plus d'un an et demi.
    A chaque vacation, on exécute une lecture de trame qui peut aboutir... ou pas. Dans ce cas, c'est pas grave, ça marchera à la vacation suivante.
    Comme je suis équipé de 2 "linky" et que la communication ne se fait pas à la même vitesse pour la conso et la production, à chaque lecture je configure le port série du PI. Au bout d'un certain nombre de fois qui est très variable (entre quelques minutes et plusieurs jours), il en a marre et la lecture du device est bloquée, mais mon programme ne bloque pas. Je consigne l'erreur, et après 3 erreurs de suite, je reboote le PI. La situation est débloquée, imparable.

    Voici en quelques lignes mon code actuel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        // On bloque jusqu'à ce qu'une donnée soit disponible
        stream_set_blocking($handle, true);
        // Mais on n'attend que 15s
        stream_set_timeout($handle, 15);
     
        // Lecture d'une trame
        $trame = readTrame($handle, $readMode);
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
     
    /**
     * Lit une trame de téléinformation
     * @param integer $handle : handle du device en entrée
     * @param integer $readMode : mode de lecture des données (READ_MODE_HISTORIC | READ_MODE_STANDARD)
     * @return array : Le tableau de données pour une trame, null si lecture incorrecte.
     */
    function readTrame($handle, $readMode) {
    	// On attend la fin d'une trame pour commencer avec la trame suivante
    	$foundTrame = false;
    	$char = '';
    	Tools::writeLog("Positionnement en début de trame.");
    	do {
    		if (($char = fread($handle, 1)) === false) {
    			// On sort si on rencontre une erreur de lecture
    			handleReadError($handle);
    			return null;
    		}
    	} while ($char != chr(2) && $char != chr(0));
     
    	// On peut bloquer sur la lecture de caractères null. On force la sortie dans ce cas
    	if ($char === chr(0)) {
    		Tools::writeLog("Lecture impossible - caractère null reçu.");
    		return null;
    	}
     
    	// On est en début de trame
    	Tools::writeLog("Début de trame");
    	// On vérifie en lisant la 1ère étiquette, soit les 6c suivants
    	if (($trame = fread($handle, 6)) === false) {
    		// On sort si on rencontre une erreur de lecture
    		handleReadError($handle);
    		return null;
    	}
    	$firstTag = substr($trame, 1, 4);
    	Tools::writeLog(sprintf("Vérification avec le 1er tag : %s", $firstTag));
    	$foundTrame = (($readMode === READ_MODE_HISTORIC && $firstTag == "ADCO") ||
    			($readMode === READ_MODE_STANDARD && $firstTag == "ADSC"));
     
    	// Si le 1er tag n'est pas celui souhaité, on sort
    	if (!$foundTrame) {
    		Tools::writeLog("Le TAG trouvé n'est pas celui attendu.");
    		return null;
    	}
     
    	// On a lu le 1er tag d'une nouvelle trame et c'est bien celui attendu. On lit toute la trame
    	Tools::writeLog("Lecture d'une nouvelle trame.");
    	// On lit tous les caractères jusqu'a la fin de la trame ou jusqu'à ce qu'on détecte une interruption d'émission
    	do {
    		if (($char = fread($handle, 1)) === false) {
    			// On sort si on rencontre une erreur de lecture
    			handleReadError($handle);
    			return null;
    		}
    		// Le flux peut -être stoppé. Un EOT est envoyé pour prévenir.
    		if ($char == chr(4)) {
    			Tools::writeLog("L'émission est interrompue. On stoppe la lecture.");
    			return null;
    		}
    		if ($char != chr(3)) $trame .= $char;
    	} while ($char != chr(3));
     
    	Tools::writeLog("Analyse de la trame");
    	// on supprime le 1er caractère de début de trame, soit le chr(10) de la 1ère étiquette
    	$trame = substr($trame, 1);
    	// on sépare les messages de la trame
    	$informations = explode(chr(10), $trame);
    	// Le caractère séparateur entre étiquette et donnée dépend du mode de lecture
    	// Le nombre de caractères à exclure de la checksum aussi 
    	if ($readMode === READ_MODE_HISTORIC) {
    		$sep = chr(0x20);
    		$extraCars = 2;
    	} else {
    		$sep = chr(0x09);
    		$extraCars = 1;
    	}
     
    	// On analyse et on récupère chaque information de la trame
    	foreach ($informations as $key => $information) {
    		// Une info se termine par 0x0d. On supprime ce caractère de contrôle.
    		if ($information[strlen($information)-1] == chr(0x0d))
    			$information = substr($information, 0, -1);
    		else 
    		{
    			Tools::writeLog(sprintf("Structure de donnée incomplète : %s", $information));
    			return null;
    		}
    		// Somme de contrôle du groupe d'info = dernier caractère
    		$ctrlCar = ord(substr($information, -1));
    		// Vérification
    		$ctrlCheck = 0;
    		$checkZoneLength = strlen($information) - $extraCars;
    		for ($c = 0; $c < $checkZoneLength ; $c++) {
    			$ctrlCheck += ord($information[$c]);
    		}
    		$ctrlCheck = ($ctrlCheck & 0x3f) + 0x20;
    		if ($ctrlCheck != $ctrlCar) {
    			Tools::writeLog(sprintf("Echec du controle de checksum pour l'information %s", $information));
    			return null;
    		}
     
    		// Suppression du dernier caractère séparateur et du caractère de contrôle
    		$information = substr($information, 0, -2);
    		// On construit un tableau comportant l'étiquette, et la ou les valeurs
    		$information = explode($sep, $information);
    		if (! empty($information[0]) && ! empty($information[1])) {
    			$etiquette = $information[0];
    			$valeur = "";
    			// Il peut y avoir un horodatage. Dans ce cas, il y a 2 valeurs : horodatage + valeur de la données
    			switch (count($information)) {
    				case 2:
    					$valeur = $information[1];
    					break;
    				case 3:
    					$valeur = (strlen($information[2]) > 0) ? $information[2] : $information[1];
    					break;
    				default:
    					Tools::writeLog(sprintf("La valeur de %s ne possède pas le nombre de champs attendu.", $etiquette));
    					Tools::writeLog(print_r($information));
    			}
    			if (!empty($valeur))
    				$datas[$etiquette] = $valeur; // on stocke les étiquettes et les valeurs de l'array datas
    			else {
    				Tools::WriteLog(sprintf("L'étiquette %s possède une valeur mais n'a pas le format attendu.", $etiquette));
    				Tools::WriteLog(print_r($information));
    			}
    		}
    	}
    	print_r($datas);
    	return $datas;
    }
     
    /**
     * Log un message en cas d'erreur de lecture
     * @param int $handle : handle du fichier
     */
    function handleReadError($handle) {
    	$info = stream_get_meta_data($handle);
    	if ($info['timed_out'])
    		Tools::writeLog("Timeout. Lecture abandonnée.");
    	else
    		Tools::writeLog("Erreur de lecture.");
    }
    Voilà, j'espère que ça peut t'aider.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 16/04/2008, 23h22
  2. Outlook ne marche pas dans mon réseau sous linux
    Par Germain123 dans le forum Applications et environnements graphiques
    Réponses: 3
    Dernier message: 25/03/2006, 23h15
  3. [Systeme] Process marche sous windows, pas sous linux
    Par Zapan dans le forum Général Java
    Réponses: 12
    Dernier message: 14/01/2006, 14h06
  4. Lire des fichiers iso sous linux
    Par wodel dans le forum Applications et environnements graphiques
    Réponses: 3
    Dernier message: 28/11/2005, 11h17
  5. Lire un fichier txt par http (C sous Linux)
    Par sleg dans le forum Réseau
    Réponses: 4
    Dernier message: 18/10/2005, 12h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo