PHP Lecture du port série avec Event et DIO


Exemple

Les flux DIO ne sont actuellement pas reconnus par l'extension d' événement . Il n'y a pas de moyen propre d'obtenir le descripteur de fichier encapsulé dans la ressource DIO. Mais il existe une solution de contournement:

  • ouvrir le flux pour le port avec fopen() ;
  • rendre le flux non bloquant avec stream_set_blocking() ;
  • obtenir le descripteur de fichier numérique du flux avec EventUtil::getSocketFd() ;
  • transmettez le descripteur de fichier numérique à dio_fdopen() (actuellement non documenté) et récupérez la ressource DIO;
  • ajouter un Event avec un rappel pour écouter les événements de lecture sur le descripteur de fichier;
  • dans le rappel, videz les données disponibles et traitez-les selon la logique de votre application.

dio.php

<?php
class Scanner {
  protected $port; // port path, e.g. /dev/pts/5
  protected $fd; // numeric file descriptor
  protected $base; // EventBase
  protected $dio; // dio resource
  protected $e_open; // Event
  protected $e_read; // Event

  public function __construct ($port) {
    $this->port = $port;
    $this->base = new EventBase();
  }

  public function __destruct() {
    $this->base->exit();

    if ($this->e_open)
      $this->e_open->free();
    if ($this->e_read)
      $this->e_read->free();
    if ($this->dio)
      dio_close($this->dio);
  }

  public function run() {
    $stream = fopen($this->port, 'rb');
    stream_set_blocking($stream, false);

    $this->fd = EventUtil::getSocketFd($stream);
    if ($this->fd < 0) {
      fprintf(STDERR, "Failed attach to port, events: %d\n", $events);
      return;
    }

    $this->e_open = new Event($this->base, $this->fd, Event::WRITE, [$this, '_onOpen']);
    $this->e_open->add();
    $this->base->dispatch();

    fclose($stream);
  }

  public function _onOpen($fd, $events) {
    $this->e_open->del();

    $this->dio = dio_fdopen($this->fd);
    // Call other dio functions here, e.g.
    dio_tcsetattr($this->dio, [
      'baud' => 9600,
      'bits' => 8,
      'stop'  => 1,
      'parity' => 0
    ]);

    $this->e_read = new Event($this->base, $this->fd, Event::READ | Event::PERSIST,
      [$this, '_onRead']);
    $this->e_read->add();
  }

  public function _onRead($fd, $events) {
    while ($data = dio_read($this->dio, 1)) {
      var_dump($data);
    }
  }
}

// Change the port argument
$scanner = new Scanner('/dev/pts/5');
$scanner->run();

Essai

Exécutez la commande suivante dans le terminal A:

$ socat -d -d pty,raw,echo=0 pty,raw,echo=0
2016/12/01 18:04:06 socat[16750] N PTY is /dev/pts/5
2016/12/01 18:04:06 socat[16750] N PTY is /dev/pts/8
2016/12/01 18:04:06 socat[16750] N starting data transfer loop with FDs [5,5] and [7,7]

La sortie peut être différente. Utilisez les PTY des deux premières lignes ( /dev/pts/5 et /dev/pts/8 en particulier).

Dans le terminal B, exécutez le script susmentionné. Vous pouvez avoir besoin des privilèges root:

$ sudo php dio.php

Au terminal C, envoyez une chaîne au premier PTY:

$ echo test > /dev/pts/8

Sortie

string(1) "t"
string(1) "e"
string(1) "s"
string(1) "t"
string(1) "
"