Laravel Day Verona 2018
Applicato all'IIOT
class eppak extends MedioMan implements Veneto
{
public function identity()
{
return [
"Name" => "Alessandro",
"LastName" => "Cappellozza",
"Moniker" => "eppak",
"Email" => "alessandro.cappellozza@gmail.com"
];
}
public function job() {
return $this->r & $this->d;
}
public function company() {
return "SENECA";
}
public function alcohol()
{
return false;
}
public function alpine()
{
return false;
}
}
Applicato all'IIOT
Il Contesto
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
Logica
Locale
(PLC/RTU)
Supervisione
(SCADA)
I/O
e
Sensori
Bus
di
Campo
Datalogger
N:1
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
INTEGRAZIONE/EVOLUZIONE
(Cambio di mentalità es. CSD)
Applicato all'IIOT
Supervisione
Applicato all'IIOT
Applicato all'IIOT
Scelte Tecnologiche
Applicato all'IIOT
Investimento di pochi mesi, il prodotto è di traino delle vendite.
Vanno individuate delle tecnologie...
Applicato all'IIOT
Applicato all'IIOT
L'approccio web solleva problematiche di tempi di esecuzione delle richieste quindi sono necessarie elaborazioni "batch"
Applicato all'IIOT
Metadati
(Configurazioni)
Datastore
(Serie di tempi)
Sistema Operativo/Servizi
UI JS
Laravel
Applicato all'IIOT
/* RICEVO */
class ProtocolController extends Controller
{
use CommandBus;
public function receive(Request $request, DataBus $bus)
{
$data = $request->getContent();
$bus->dispatch(new DeivceData($data));
return response()->json([ 'err' => 0 ]);
}
}
/* RICEVO E SERVO */
class ProtocolController extends Controller
{
use CommandBus;
public function receive(Request $request)
{
$data = $request->getContent();
$deviceData = new DeivceData($data);
$this->dispatch($deviceData);
return response()->json([
'err' => 0,
'act' => CommandQueue::actions($data) /* LOGICA DI ESTRAZIONE */
]);
}
}
Basato su HTTP
Payload JSon
Applicato all'IIOT
class DeivceData {
private $data = null;
public function __construct($data)
{
$this->data = $data;
}
public function data() { return $this->data; }
}
class DeivceDataHandler {
private $data = null;
private $ds = null;
public function __construct(DatastoreManager $ds, DeivceData $data)
{
$this->data = $data;
$this->ds = $ds;
}
public function handle() {
$parsed = new PacketParser($this->data);
$ds->store($parsed);
}
}
class PacketParser {
private $data = null;
public function __construct($data)
{
$this->data = new JSON($data);
}
public function isLog() { return $this->data->has('log'); }
public function isCfg() { return $this->data->has('cfg'); }
public function isEvent() { return $this->data->has('ev'); }
public function isAck() { return $this->data->has('ack'); }
public function uuid() {
return $this->data->has('uuid') ?: $this->data->getUuid;
}
}
Applicato all'IIOT
class ProcessData implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $ds = null;
private $data = null;
public function __construct(DatastoreManager $ds, DevcieData $data)
{
/* .. */
}
public function handle()
{
/* .. */
}
}
Non sempre è stringente per l'rtu "sapere" che il dato è valido, né che è salvato correttamente nell'immediato.
Applicato all'IIOT
/* ..*/
$this->app->bind(
'App\Datastore\Contracts\Collector', 'App\Datastore\DataCollector'
);
namespace App\Datastore;
class DataCollector implements Contracts\Collector {
public function __construct(Alarms $alarms, Datastore $ds) { /* .. */ }
public function store($data) {
if($this->data->isLog()) {
$processed = $logProcessor->parse($data);
$ds->write($processed);
$alarms->dispatch($processed);
}
}
}
namespace App\Datastore\Contracts;
interface Collector {
public function store($data);
}
namespace App\Datastore;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
class LogProcessor {
public function parse($data) { /* LOGICA DEL DATO */ }
}
Scatenare eventi
Calcolare campi virtuali, cambi scala, sommatorie (...)
namespace App\Dispatcher;
class Alarms {
public function dispatch($data) { /* DISPATCH MESSAGGI */ }
}
Applicato all'IIOT
Il prodotto deve essere ripristinabile in qualsiasi stato e non deve avere necessità della ui per la manutenzione base
Le procedure devono girare senza ausili esterni (rete)
Applicato all'IIOT
Viene mutuato dal monto embed il concetto di bootloader che renda indipendente la app da possibili guasti e/o difetti di aggiornamento
# crontab
@reboot sh /var/www/bin/bootloader.sh
#!/bin/bash
sleep 15
/usr/bin/php /var/www/bootloader/upgrade.php
/usr/bin/php /var/www/bootloader/restore.php
// UPGRADE
<?php
$this->loadEnv();
if (file_exists('...') {
$this->stopServices();
$this->upgrade();
$this->migrate();
$this->startServices();
}
// RESTORE
<?php
$this->loadEnv();
if (file_exists('...') {
$this->stopServices();
$this->upgrade();
$this->migrate();
$this->startServices();
}
Applicato all'IIOT
FAI giraRE
HTTPD come
root...
Applicato all'IIOT
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
int
main (int argc, char *argv[])
{
int res = 1;
char buff[1024];
setuid (0);
if (/* SECURITY CHECK */)
{
syslog(LOG_INFO, "RUN SOMETHING");
sprintf(buff, "/SOME/PATH/BIN");
system(buff);
syslog(LOG_INFO, buff);
res = 0;
}
return res;
}
COME BIN
Applicato all'IIOT
class System
{
private $exec = false;
private $jobid = 0;
public function _construct()
{
declare(ticks=1);
pcntl_async_signals(true);
pcntl_signal(SIGUSR1, [$this, 'receivedSignal']);
}
public function apply()
{
$pid = posix_getpid();
$this->exec = false;
$this->jobid = Queue::push(new \App\Jobs\UpdateSystemSettings($pid));
$ts = time();
while(time() - $ts < SYSTEM_SETTINGS_TIMEOUT) {
usleep(250);
if($this->exec) { return true; }
}
return false;
}
private function receivedSignal($signo) {
// CHECK $this->jobid
$this->exec = true;
}
}
COME JOB
Applicato all'IIOT
class Backup implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $clt = null;
private $meta = null;
private $settings = null;
public function __construct(Settings $settings, System $system, Collector $clt, MetaData $meta)
{
$this->settings = $settings;
$this->system = $system;
$this->clt = $clt;
$this->meta = $meta;
}
public function handle()
{
$path= $this->settings->backupDestination();
$this->meta->backup($path);
$this->clt->backup($path);
$zipped = $this->system->zip($path);
$this->system->move($zipped);
}
}
Necessita <<solo>> di privilegi elevati
Applicato all'IIOT
Scelta del Datastore
Applicato all'IIOT
"Big data is like teenage sex: everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it..."
Dan Ariely
http://fb.me/1r54f62oU
Applicato all'IIOT
50 (device) x
100 (tag) x
1440 (minuti/g) x
720(giorni) x
=~ 5.2 x 10^9
Applicato all'IIOT
Applicato all'IIOT
namespace App\Datastore\Contracts;
interface Datastore {
public function read($vars, $tags, $from, $to);
public function last($vars, $tags);
public function history($vars, $tags, $from, $to, $span = null, $agg = null);
public function write($values, $tags);
public function setRetention($days);
public function getSize();
public function getVersion();
}
namespace App\Datastore;
class InfluxDatastore implements Contracts\Datastore {
public function read($vars, $tags, $from, $to) { /* ... */ }
public function last($vars, $tags) { /* ... */ }
public function history($vars, $tags, $from, $to, $span = null, $agg = null) { /* ... */ }
public function write($values, $tags) { /* ... */ }
public function setRetention($days){ /* ... */ }
public function getSize() { /* ... */ }
public function getVersion() { /* ... */ }
}
Test con i3 (2GB)/SSD: 5.2 Mld campioni, estrazione 8 tracce (media) su periodo random fino a due anni
=~ 14sec di media
Applicato all'IIOT
Insito nella procedura di downsampling
Applicato all'IIOT
public class InfluxDatastoreLTTB extends InfluxDatastore {
public function history($vars, $tags, $from, $to, $span = null, $agg = null); {
if ($agg == "LTTB") {
$data = $this->read($vars, $tags, $from, $to);
return DataProcess::LTTB($data, MAX_SAMPLES);
} else {
return parent::history($vars, $tags, $from, $to, $span, $agg);
}
}
}
/* ... */
public function LTTB($data, $thr = MAX_SAMPLES)
{
$downampled = [];
$prev = $data[0];
for($b=1; $b<count($blocks)-1; $b++) {
$rank = 0;
$block = $blocks[$b];
$avg = $this->avg($blocks[$b + 1]);
for($i=0; $i<count($block); $i++) {
$rank = $this->getRank($block[$i], $avg, $prev);
if($downampled[$b] < $rank) {
$downampled[$b] = $rank;
$prev = $rank;
}
}
}
return $downampled;
}
private function makeBlocks($data) { /* DIVISION + LAST BLOCK */ }
private function avg($block) { /* .. */ }
private function getRank($sample, $avg, $prev) { /* .. */ };
Il tutto è proporzionato al timespan in questione.
(Mockup)
Però il risultato è visivamente notevole...
Applicato all'IIOT
5000 samples
100 samples
250 samples
50 samples
Sveinn Steinarsson (https://skemman.is/handle/1946/15343)
Applicato all'IIOT
Progetti Derivati
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
Copyright SENECA SRL
Applicato all'IIOT
alessandro.cappellozza@gmail.com