IIoT REST APIs
L'unione tra anni di esperienza e la mia passione per la programmazione.
Introduzione
Ciao a tutti!
Vista la mia grande curiosità per lo sviluppo back-end con Python, ho voluto unire la mia ultra-settennale esperienza lavorativa nel mondo dell’IIoT con la passione per la programmazione, realizzando un software che potesse essere al contempo utile e mi desse la possibilità di approfondire in maniera pratica quanto ho studiato negli ultimi mesi.
Come obiettivo di questo progetto, ho tenuto a mente il fenomeno della “digital transformation”, utilizzando delle tecnologie modulari e più orientate ai microservizi piuttosto che altri framework maggiormente conosciuti e più articolati, attraverso i quali sono comunque riuscito a realizzare quanto preventivato.
Tenendo a mente questo “docet” con il quale sviluppare il progetto, con poco più di 600 righe di codice distribuite nei vari scripts (tra i quali ci sono anche files contenenti solamente strutture dati e non algoritmi), ho realizzato un software per il campionamento, la storicizzazione, l’interrogazione e l’analisi dati di un numero finito di misure analogiche in un ambiente di produzione.
Struttura del progetto
Il progetto è composto principalmente da due “moduli”, ognuno dei quali ha un ruolo ben preciso:
il collettore, il quale ha il compito di storicizzare i dati di processo ottenuti da un dispositivo in campo (PLC) su un database locale SQLite;
il server API, il quale ha il compito di gestire le richieste e fornire le dovute risposte al/ai client(s).
Le principali tecnologie utilizzate per i vari moduli del progetto sono:
Modulo n. 1 (collettore):
python-snap7 per la comunicazione tra il dispositivo PLC e Python;
schedule per la schedulazione dei tasks di storicizzazione.
Modulo n. 2 (server API):
FastAPI per la creazione delle REST APIs;
matplotlib per la creazione dei grafici delle misure;
uvicorn come server ASGI.
In entrambi i moduli, viene utilizzato SQLAlchemy come ORM.
La struttura logica del software è così rappresentata:
Logica di funzionamento dei moduli
Il funzionamento dei moduli del progetto è illustrato di seguito:
il collettore, una volta avviato, ottiene le tags configurate per la storicizzazione dal database SQLite e, in base ai vari task definiti grazie alla libreria “schedule”, ogni intervallo di tempo impostato viene effettuato un salvataggio dei valori delle stesse nel database locale.
Ogni salvataggio produce una riga di log per informare l’utente della riuscita storicizzazione dei dati:
il server API, una volta avviato, espone differenti endpoints, con i quali gli utenti possono interagire attraverso dei “query parameters” per ottenere le informazioni che necessitano.
Ogni endpoint configurato ritorna una response in formato JSON (tranne per un endpoint il quale ritorna un’immagine), in modo tale da essere facilmente human-readable, ed essendo un formato ampiamente utilizzato nel mondo del web development, può essere facilmente interpretato dalla maggior parte dei framework front-end e da svariati altri tools:
Configurazione degli endpoint
Gli endpoint configurati sono cinque (compreso l’endpoint “root” e "docs"), e sono:
root: l’endpoint principale che fa un redirect sulla documentazione delle API;
tags: dedicato all’ottenimento e alla configurazione di nuove tags per il campionamento dei valori delle stesse;
data: dedicato all’ottenimento dei valori delle tags campionate;
chart: dedicato alla generazione e alla visualizzazione di un grafico storico della tag selezionata e dei relativi setpoint (di allarme);
docs: dedicato alla documentazione delle API, generato automaticamente da OpenAPI.
Di seguito è possibile osservare nel dettaglio le funzionalità dei vari endpoints attraverso la documentazione interattiva.
Tags endpoint
Figura 1 - Panoramica dell'endpoint "tags" relativa al metodo "GET"
Figura 2 - Panoramica dell'endpoint "tags" relativa al metodo "GET" con l'applicazione del filtro "name_like"
Figura 3 - Panoramica dell'endpoint "tags" relativa al metodo "GET" con l'applicazione dei filtri "name_like" e "description_like"
Figura 4 - Panoramica dell'endpoint "tags" relativa al metodo "POST"
Data endpoint
Figura 1 - Panoramica dell'endpoint "data" relativa al metodo "GET"
Figura 2 - Panoramica dell'endpoint "data" relativa al metodo "GET" con l'applicazione dei filtri "period" e "name_like" (continua in Figura 3)
Figura 3 - Panoramica dell'endpoint "data" relativa al metodo "GET" con l'applicazione dei filtri "period" e "name_like"
Chart endpoint
Figura 1 - Panoramica dell'endpoint "chart" relativa al metodo "GET"
Figura 2 - Panoramica dell'endpoint "chart" relativa al metodo "GET" con l'applicazione dei filtri "tag_name" e "period" (continua in Figura 3)
Figura 3 - Panoramica dell'endpoint "chart" relativa al metodo "GET" con l'applicazione dei filtri "tag_name" e "period"
Codice
Come accennato nell'introduzione, ho sviluppato il progetto utilizzando il linguaggio di programmazione Python, un linguaggio semplice ma potente con un ampio bacino di campi di applicazione (machine learning, data analysis, web development, data science, scripting...).
L'uso della tipizzazione nel codice, unito alla scrittura dei commenti e delle docstrings, ha reso lo stesso più chiaro e developer-friendly, oltre che a rendere possibile la creazione automatica della documentazione delle API grazie a OpenAPI.
Grazie alla struttura modulare degli scripts, ho avuto la possibilità di scrivere svariate parti di codice una volta sola, potendolo così riutilizzarlo in più parti del progetto, evitando così di avere codice "boilerplate".
Nel corso dello sviluppo del progetto ho utilizzato gran parte delle mie conoscenze applicate già nel mondo del lavoro, specialmente riguardo la gestione dei dati attraverso un database, e le espressioni regolari, con le quali ho avuto modo di realizzare la maggior parte dei controlli di validazione dell'input degli utenti.
Con l'approccio al Continuous Integration, ho avuto modo di monitorare il comportamento delle varie parti dell'applicazione, intervenendo con criterio dove notavo un'anomalia, anche grazie all'ausilio di librerie per il logging delle informazioni.
Una volta risolte le anomalie e testate in maniera diretta, le modifiche venivano sincronizzate con un repository su GitHub attraverso l'utilizzo di git.
Grazie a tools di linting come ruff, ho avuto la possibilità di individuare degli errori critici e degli warning durante lo sviluppo del codice, rendendolo più funzionale e quanto più conforme alle PEP 8.
Figura 1 - Funzione per la validazione del periodo temporale inserito dall'utente
Figura 2 - Metodo "GET" dell'endpoint "data", con l'utilizzo dell'approccio "ORM" di SQLAlchemy
Figura 3 - Definizione dei DTOs (Data Transfer Objects), utilizzati da FastAPI e Pydantic
Figura 4 - Utilizzo di matplotlib per la creazione dei grafici storici
Figura 5 - Utilizzo di ruff per il linting del codice
Considerazioni
Sono estremamente soddisfatto di aver portato a compimento la prima versione di questo piccolo progetto, poiché ho avuto modo di imparare nuovi aspetti della programmazione, coltivandoli sull'esperienza acquisita finora.
Il progetto potrebbe essere espanso ulteriormente, poiché attualmente la lettura dei dati viene effettuata da un solo device, ma con una piccola integrazione nel codice del collettore, i dati potrebbero facilmente essere campionati da più devices, purché questi siano raggiungibili via Ethernet.
Per completezza, data la mia curiosità e il mio recente studio riguardo i "Docker containers", avrei voluto containerizzare l'intera applicazione, rendendola a tutti gli effetti un pacchetto di micro-servizi, ma per via del fatto che il PC sul quale ho sviluppato l'applicazione risulta avere Windows Server 2016 come sistema operativo, avrei dovuto installare la versione Enterprise di Docker Desktop.
Un'ulteriore integrazione che mi piacerebbe davvero tanto fare è quella di realizzare il front-end dell'applicazione, utilizzando un framework semplice e robusto, dandole così un aspetto più user-friendly.
Il motivo principale che mi ha spinto a realizzare quest'applicazione è stato quello di creare da zero un progetto che potesse conciliare con i miei anni di esperienza nel mondo IIoT con la grande passione della programmazione e dello sviluppo back-end.
Se sei arrivato fin qui, ti ringrazio immensamente per la lettura, e nel caso in cui avessi dei consigli da darmi, ti invito a contattarmi attraverso i miei social network o attraverso il modulo contatti.