Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How I (ab)used PHP with the help of ReactPHP

How I (ab)used PHP with the help of ReactPHP

PHP is one of the most accessible and commonly used languages in computer programming. It’s used for building websites, web applications, micro services and loads of other stuff. Normally we would not use it to control hardware. But let that be the showcase of this talk. I’ll show you how I (ab)used PHP to control hardware used during track and field competitions. It involves LED-panels, serial-to-ethernet-­convertors and the use of reactphp, an event-driven non-blocking I/O library.

Stijn Vannieuwenhuyse

January 27, 2017
Tweet

More Decks by Stijn Vannieuwenhuyse

Other Decks in Programming

Transcript

  1. FT01<TR><TD> Long jump men </TD></TR> <TR> <TD CLASS="DAMP"><span class="teamflag"></span></TD> <TD

    CLASS="name">Lambert Romain</TD> <TD CLASS="team">DAMP</TD> </TR> <TR> <TD CLASS="attempt">A6</TD> <TD CLASS="result"> </TD> <TD CLASS="rank">Rank:&nbsp;1</TD> </TR>
  2. public function run() { $this->running = true; while ($this->running) {

    $this->nextTickQueue->tick(); $this->futureTickQueue->tick(); $this->timers->tick(); if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) { $timeout = 0; } elseif ($scheduledAt = $this->timers->getFirst()) { $timeout = $scheduledAt - $this->timers->getTime(); if ($timeout < 0) { $timeout = 0; } else { $timeout *= self::MICROSECONDS_PER_SECOND; } } elseif ($this->readStreams || $this->writeStreams) { $timeout = null; } else { break; } $this->waitForStreamActivity($timeout); } }
  3. TFF000000a2000700100030a012345678910\x0d TRRGGBBXXXXYYYYLLLLHHHHFDDDDDDDDDDDD\x0d T: command name RRGGBB: hex color value XXXX:

    x-position in hex, starting from left YYYY: y-position in hex, starting from top LLLL: length of the text in hex HHHH: heigth of the text in hex F: font-type D: data \x0d: end marker
  4. class Text implements Instruction { private $data; private $x; private

    $y; private $height; private $length; private $colorCode; private $font; public function __construct($data, $x, $y, $height, $length, $colorCode, $font) { $this->data = $data; $this->x = $x; $this->y = $y; $this->height = $height; $this->length = $length; $this->colorCode = $colorCode; $this->font = $font; } public function toData() { return sprintf( "T%s%04X%04X%04X%04X%s%s\x0d", $this->getColor(), //returns hex string of red, green or yellow $this->x, $this->y, $this->length, $this->height, $this->font, $this->data ); } }
  5. BXXXXYYYYLLLLHHHHDDDDDDDDDDD\x0d B: command name XXXX: x-position in hex, starting from

    left YYYY: y-position in hex, starting from top LLLL: length of the text in hex HHHH: heigth of the text in hex D: data \x0d: end marker
  6. . . . . . . . . . .

    . Y Y Y Y Y Y Y . . . . . . . . . . . . . . . . . . . Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y R R R R R R R R R R R Y . . . . . Y G G G G G G G G G G G G G G G G G G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . . . . Y . Y Y Y . Y . . . . . G . . . . . G . . . . . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . R . R R R . Y . . . Y . Y . G . G . G . G G G . G . G G G . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . R . . . R . Y . Y Y Y . Y . G . G . G . G . . . G . G . . . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . R . R R R . Y . . . Y . Y . G G G . G . G G G . G . G G G . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . R . R . . . Y . Y Y Y . Y . . . G . G . . . G . G . G . G . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . R . R R R . Y . . . . . Y . . . G . G . G G G . G . G G G . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . R . . . . . Y . . . . . Y . . . . . G . . . . . G . . . . . G . Y . . . . . . . . . . . . . . . . . . . . . . . . . . Y Y Y Y Y Y Y Y Y Y Y Y . . . . . Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . . . . . . . . . . . . . . . . . . . . . . .
  7. final class AdvancedDotDigital extends Font { protected static $chars =

    [ "0" => "01111110\n11111111\n11000011\n11000011\n11000011\n11000011\n11000011\n11000011\n11000011\n11000011\n11111111\n01111110", "1" => "00011000\n00111000\n01111000\n11011000\n00011000\n00011000\n00011000\n00011000\n00011000\n00011000\n11111111\n11111111", "2" => "01111110\n11111111\n11000011\n00000011\n00000011\n01111111\n11111110\n11000000\n11000000\n11000000\n11111111\n11111111", "3" => "01111110\n11111111\n11000011\n00000011\n00000011\n00111110\n00111110\n00000011\n00000011\n11000011\n11111111\n01111110", "4" => "11000011\n11000011\n11000011\n11000011\n11000011\n11000011\n11111111\n11111111\n00000011\n00000011\n00000011\n00000011", "5" => "11111111\n11111111\n11000000\n11000000\n11111110\n11111111\n00000011\n00000011\n00000011\n11000011\n11111111\n01111110", "6" => "01111110\n11111111\n11000011\n11000000\n11111110\n11111111\n11000011\n11000011\n11000011\n11000011\n11111111\n01111110", "7" => "11111111\n11111111\n11000110\n00000110\n00001100\n00001100\n00011000\n00011000\n00110000\n00110000\n00110000\n00110000", "8" => "01111110\n11111111\n11000011\n11000011\n11000011\n01111110\n01111110\n11000011\n11000011\n11000011\n11111111\n01111110", "9" => "01111110\n11111111\n11000011\n11000011\n11000011\n11000011\n11111111\n01111111\n00000011\n11000011\n11111111\n01111110", "m" => "0000000000\n0000000000\n0000000000\n0000000000\n0000000000\n1111101110\n1111111111\n1100110011\n1100110011\n1100110011\n1100110011\n1100110011\n1100110011", "X" => "1100000000011\n0110000000110\n0011000001100\n0001100011000\n0000110110000\n0000011100000\n0000011100000\n0000110110000\n0001100011000\n0011000001100\n0110000000110\n1100000000011", "O" => "0000111110000\n0011111111100\n0111000001110\n0110000000110\n1100000000011\n1100000000011\n1100000000011\n1100000000011\n0110000000110\n0111000001110\n0011111111100\n0000111110000", ":" => "0000\n0000\n0110\n0110\n0000\n0000\n0000\n0000\n0110\n0110\n0000\n0000", "-" => "0000000000000\n0000000000000\n0000000000000\n0000000000000\n0000000000000\n0011111111100\n0011111111100\n0000000000000\n0000000000000\n0000000000000\n0000000000000\n0000000000000", ]; }
  8. class Watcher { public function __construct($dir, LoopInterface $loop) { $fd

    = inotify_init(); stream_set_blocking(fd, 0); inotify_add_watch($fd, $dir, IN_MODIFY); $loop->addReadStream($fd, function($fd) { foreach(inotify_read($fd) as $events) { //process change } } ) } }
  9. class NextAthlete implements Layout { private $name; private $team; private

    $attempt; private $place = null; private $height = null; public function __construct($name, $team, $attempt, $place = null, $height = null) { $this->name = $name; $this->team = $team; $this->attempt = $attempt; $this->place = $place; $this->height = $height; } /** * @return Instruction[] */ public function toInstructions() { $instructions = []; $name = (new Text(strtoupper($this->name), RingMatrix::class, "Y"))->toBitmap(); $instructions[] = new Bitmap(0, 0, $name); $team = (new Text(strtoupper($this->team), SquareDotDigital::class, "R"))->toBitmap(); $instructions[] = new Bitmap(64 - $team->getLength(), 11, $team); //some other conversion from data to instructions return $instructions; } }
  10. $loop = Factory::create(); $server = new Server($loop); $server->on('connection', function (Connection

    $connection) { echo sprintf("%s came online\n", $connection->getRemoteAddress()); $connection->on('close', function(Stream $stream) use ($connection) { echo sprintf("%s went offline\n", $connection->getRemoteAddress()); }); }); $server->listen('6000', '0.0.0.0'); $loop->run();
  11. $loop = Factory::create(); $deviceServer = new Server($loop); $commandServer = new

    Server($loop); $deviceServer->on('connection', function (Connection $connection) { //do some stuff }); $commandServer->on('connection', function (Connection $connection) { $connection->on('data', function() { //parse json to command //dispatch command }) }); $deviceServer->listen('6000', '0.0.0.0'); $commandServer->listen('6001', '0.0.0.0'); $loop->run();
  12. $socketServer = new Server($loop); $webserver = new React\Http\Server($socketServer); $webserver->on('request', function

    ($request, $response) { //just like a normal controller method //$response is a writable stream }); $socket->listen(8080);
  13. class WebSocketConnector implements MessageComponentInterface { private $clients; public function __construct()

    { $this->clients = new SplObjectStorage; } public function onOpen(ConnectionInterface $connection) { $this->clients->attach($conn); } public function onClose(ConnectionInterface $connection) { $this->clients->detach($connection); } public function onError(ConnectionInterface $connection, \Exception $e) { $connection->close(); } public function onMessage(ConnectionInterface $from, $msg) { //process data } public function sendToClients($data) { foreach($this->clients as $client) { $client->send($data); } } }
  14. var connection = null; var connect = function() { connection

    = new WebSocket('ws://192.168.15.100:8080/field'); connection.onopen = function() { //log or ... }; connection.onmessage = function(message) { //process message.data } connection.onclose = function() { connection = null; setTimeout(connect, 1000); }; } var dispatch = function(data) { if(connection && connection.readyState == WebSocket.OPEN) { connection.send(data); } };
  15. { "commandName":"Nortuni.FieldScoreboardManager.Commands.ShowSlideshow", "payload": { "scoreBoardId":"LED01", "interval":"20", "screens":[ { "layoutName":"MeetingName", "data":

    { "line1":"PHPGENT", "line2":"MEETUP", "line3":"", "line4":"WELCOME" } }, { "layoutName":"MeetingName", "data": { "line1":"", "line2":"TEAMLEADER", "line3":"CRM", "line4":"" } } ] } }