Continuiamo la serie di articoli a scopo didattico sulla realizzazione di un progetto all’interno del nostro FabLab, Fab-O-Matic. In questo articolo, tratteremo dello sviluppo della parte server (o “back-end”). Questa parte del progetto funziona su Raspberry Pi, con un server Web https, un’interfaccia MQTT con Mosquitto e una gestione database con Python : mostreremo esempi pratici delle varie tecnologie usate con l’aiuto di ChatGPT.
Sommario degli articoli:
- 1) Intro & prototipo HW (parte I)
- 2) Scheda mille-fori (parte II)
- 3) Progettazione PCB (parte III)
- 4) Schema elettrico (parte IV)
- 5) Sponsorship OpenHWLAB e PCB assemblato (parte V)
- 6) Sviluppo embedded con C++ moderno (parte VI)
- 7) Backend Python e Raspberry Pi (parte VII, questo articolo)
- 8) Collaudi del software in ambito embedded (parte VIII)
- 9) La messa in funzione del sistema nel Lab (parte IX)
Puoi consultare inoltre le istruzioni per l’uso di Fab-O-Matic qua.
L’applicazione finita
Prima di vedere com’è fatta, presentiamo alcune videate di esempio dell’applicativo back-end nelle sue principali videate. E’ un sito web che usa la classica libreria Bootstrap per l’aspetto grafico.
Il manuale del back-end è disponibile nella repository Github.
Scelte tecnologiche back-end
Sarebbe impraticabile memorizzare le tessere abilitate su ogni board e doverle riprogrammare quando si associa una nuova persona. Un database è più adeguato quando si vuole memorizzare elenchi di dati come utenti, interventi, usi delle macchine, abilitazioni come in Fab-O-Matic. Serve dunque un altro software, che chiameremo “back-end”, per processare le richieste e registrare le informazioni mandate dalle varie board installate nel FabLab.
Un requisito importante è di poter far funzionare questo back-end su un Raspberry Pi Zero, che costa circa 15 EUR : come la board, il back-end è alla portata di tutte le associazioni che desiderano gestire l’accesso macchine con tessere RFID.
Per il database, abbiamo scelto SQLite, che evita attività di manutenzione delle database engine più elaborate. E’ un engine semplice, estremamente veloce, disponibile .
Nell’articolo sul software embedded, abbiamo visto che la board usa il protocollo MQTT per comunicare con il backend. MQTT è un protocollo semplice, leggibile, e molto diffuso per le applicazioni Internet Of Things. Ci servirà dunque un “server MQTT”, un cosidetto “broker”. Abbiamo scelto Mosquitto che è il broker più popolare e diffuso.
Infine, per realizzare il front-end e la logica di interazione fra board e database, abbiamo usato Python con framework Flask. Python e Flask sono molto diffusi nel campo delle applicazioni IoT: ad esempio, stanno alla base di HomeAssistant e sono attivamente mantenuti.
ChatGPT durante lo sviluppo
Durante lo sviluppo di questo progetto è uscito ChatGPT 4.0 e l’abbiamo messo alla prova con questo progetto di back-end. Il bilancio dell’esperimento è che ChatGPT è un ottimo professore e riferimento se vuoi imparare una nuova tecnologia rapidamente ; un po’ come un collega più esperto e disponibile, ma non gli puoi chiedere di fare tutto il tuo lavoro !
Possiamo riassumere l’esperienza con Fab-O-Matic in linguaggio Python come segue:
Caso di utilizzo e link alla chat | Qualità della risposta | Valutazione / Risparmio di tempo |
Code generation : riscrivi questo template flask usando bootstrap https://chatgpt.com/share/9303ee7e-c591-4e00-bd20-1a8e67f63fb8 | Boilerplate code ok, qualche inconsistenza fra pagine con risposte lunghe. Ottima base di partenza, da rilavorare. | ⭐⭐⭐ |
Code generation : Esamina le entità SQLAlchemy seguenti e genera codice Python per un sito web con Flask per le operazioni CRUD su quelle entità. https://chatgpt.com/share/4b9a395c-7524-4a70-a40c-1a1266fc68c1 | Mancanza di consistenza fra risposte e entità. Ottima base di partenza, da rilavorare. | ⭐⭐⭐ |
Debugging degli errori incontrati | Solo per problemi semplici o comuni. Funziona meglio in questo caso d’uso CoPilot che beneficia del contesto completo della soluzione. | ⭐⭐ |
Learning : Capire come usare un framework nuovo con degli esempi su misura | Elimina tante ricerche e (quasi) la necessità di leggere la documentazione fornendo esempi ben commentati. | ⭐⭐⭐⭐⭐ |
Porting : Migrare da un framework (Mongo) ad un altro (SQLAlchemy) https://chatgpt.com/share/104ffc2f-3379-4a27-9159-23db1df491d7 | Molto difficile trovare materiali online senza studiarsi due framework | ⭐⭐⭐⭐⭐ |
Python / Flask
Se hai una conoscenza di Python o familiarità con altri framework applicativi come PHP/Laravel, ASPNET.Core, è abbastanza facile imparare ad usare Flask. E quando sei bloccato con problemi di principianti, ChatGPT ti sarà un ottimo aiuto.
Sono due i concetti principali per fare applicativi Flask : i templates e le routes.
Template Flask : generazione di pagine HTML
Un Template Flask è un file contenente codice HTML con un markup specifico {% istruzione %}
oppure {{ variabile }}
Questo file viene interpretato dal Runtime per inserire i contenuti dinamici e, per esempio, generare pagine Web che cambiano in base ai contenuti del database.
Ad esempio, il codice sotto enumera un elenco di macchine e genera, per ognuna di loro una riga di tabella con ID, nome, tipo di macchina, ore lavorate, icona di blocco e 3 bottoni di VIEW/EDIT/DELETE.
{% for machine in machines %}
<tr>
<td class="d-none d-lg-table-cell">{{ machine.machine_id }}</td>
<td>{{ machine.machine_name }}</td>
<td class="d-none d-lg-table-cell"><span class="badge bg-info">{{ machine.machine_type.type_name }}</span></td>
<td>{{ machine.machine_hours | format_hours }}</td>
<td>
{% if machine.blocked %}
<i class="fa-solid fa-lock"></i> <!-- Locked Icon -->
{% else %}
<i class="fa-solid fa-lock-open"></i> <!-- Unlocked Icon -->
{% endif %}
</td>
<td>
<a href="{{ url_for('view_machine_use_history', machine_id=machine.machine_id) }}"
class="btn btn-info">{{ _("View history") }}</a>
<a href="{{ url_for('edit_machine', machine_id=machine.machine_id) }}"
class="btn btn-warning">{{ _("Edit") }}</a>
<a href="{{ url_for('delete_machine', machine_id=machine.machine_id) }}"
class="btn btn-danger">{{ _("Delete") }}</a>
</td>
</tr>
{% endfor %}
Routes : collegamento browser/logica
Le variabili usate nel template Flask di sopra devono essere passate da qualche logica Python. Sono i file di routes che uniscono le azioni su database con i template.
La tabella è sopra è gestita da questa route:
@app.route("/machines", methods=["GET"])
@login_required
def view_machines():
session = DBSession()
machine_repo = MachineRepository(session)
machines = session.query(Machine).order_by(Machine.machine_id).all()
return render_template("view_machines.html", machines=machines)
In questo frammento di codice python:
- L’attributo
@app.route("/machines", methods=["GET"])
indica al framework Flask che la funzioneview_machines()
va chiamata quando il browser fa una richiesta GET sull’URL /machines.
- L’attributo
@login_required
richiede che l’utente sia autenticato per mostrare la pagina.
- La logica Python tramite Alchemy interroga la tabella delle macchine, ordina per ID e passa la variabile ottenuta alla funzione
render_templates
per il frammento html di sopra.
Python / SQLAlchemy
SQLAlchemy è una potente libreria per Python che permette di gestire in modo efficiente e intuitivo l’interazione con i database.
Fornendo un’astrazione di più alto livello, SQLAlchemy facilita lo sviluppo di applicazioni che necessitano di interagire con database relazionali. In questo articolo esploreremo alcune delle funzionalità più rilevanti offerte da SQLAlchemy, concentrandoci in particolare sugli approcci “code-first” e il versioning.
Approccio “Code-First”
L’approccio “code-first” è uno dei principali punti di forza di SQLAlchemy. Invece di creare prima il database e poi scrivere il codice per interagirvi, SQLAlchemy consente di definire le entità direttamente nel codice. Questo approccio offre una maggiore flessibilità e controllo sullo schema del database, permettendo di gestire le modifiche in modo più semplice ed efficace.
Un esempio di definizione di una entità utilizzando SQLAlchemy è il seguente:
class Role(Base):
"""Dataclass handling a role."""
__tablename__ = "roles"
role_id = Column(Integer, primary_key=True, autoincrement=True)
role_name = Column(String, unique=True, nullable=False)
authorize_all = Column(Boolean, default=False, nullable=False)
reserved = Column(Boolean, default=False, nullable=False)
maintenance = Column(Boolean, default=False, nullable=False)
backend_admin = Column(Boolean, default=False, nullable=False)
users = relationship("User", back_populates="role")
__table_args__ = (Index("idx_roles_role_name_unique", "role_name", unique=True),)
In questo esempio, abbiamo definito una classe Role
che rappresenta una tabella del database. Utilizzando gli attributi di classe, possiamo specificare i campi della tabella, i loro tipi di dati, le restrizioni e le relazioni con altre tabelle.
Versioning del database
Una delle sfide nello sviluppo di applicazioni che utilizzano un database è gestire le modifiche allo schema durante la vita del progetto. La libreria Alembic costruita sopra SQLAlchemy affronta questa sfida attraverso il versioning, che consente di generare script di aggiornamento del database automaticamente quando vengono apportate modifiche alle entità, chiamate “migrazioni”. Le migrazioni del database sono dei file Python che applicano le modifiche al database rilevate.
Ad esempio, se si aggiunge un nuovo attributo alla classe Role
, Alembic genererà automaticamente uno script di migrazione in Python per eseguire un’istruzione SQL "ALTER TABLE ... ADD COLUMN ..."
, lasciandoti comunque la possibilità di applicare manuale altre trasformazioni ai dati nello script di migrazione.
Durante lo sviluppo del progetto è stato modificato più volte lo schema del database: grazie alla tecnologia sopra, l’aggiornamento del server è sempre stato trasparente, veloce e senza perdita di dati. Ci sono molti esempi di migrazioni nella repository di Fab-O-Matic.
Navigazione Semplice fra le Entità in Relazione
SQLAlchemy facilita la gestione delle relazioni tra entità utilizzando relazioni e join. Nell’esempio precedente, la connessione tra Role e User è rappresentata dall’attributo users, che impiega la funzione relationship per creare un legame bidirezionale tra le due entità. Questo consente di accedere agevolmente agli utenti associati a un ruolo specifico e viceversa. Non è più necessario scrivere manualmente comandi SQL come “SELECT … FROM A JOIN B ON …”!
Concludendo su SQLAlchemy
SQLAlchemy è una libreria che raccomandiamo per chiunque lavori con database relazionali in Python, perché ti concentri sulla logica dell’applicazione, piuttosto che sui dettagli implementativi della gestione del database.
Librerie, packaging e distribuzione
Per semplificare l’installazione di Python con i suoi prerequisiti, Fab-O-Matic usa il formato TOML per la configurazione e la pubblicazione di un package Python su PyPi. Il file pyproject.toml
offre in fatti un metodo più intuitivo e leggibile per specificare le dipendenze e le informazioni di build rispetto ai tradizionali file setup.py
e setup.cfg
.
Troviamo elencate nel file pyproject.toml di Fab-O-Matic le dipendenze:
Titolo Libreria | Breve Descrizione | Link al Sito di Riferimento |
---|---|---|
Hatchling | Strumento di build per Python che semplifica la creazione di pacchetti. | Hatchling |
SQLAlchemy | Toolkit SQL per Python e Object-Relational Mapping (ORM). | SQLAlchemy |
toml | Libreria per lavorare con i file TOML in Python. Usato per il parsing del file di configurazione. | toml |
paho-mqtt | Client MQTT per Python per collegarsi al broker Mosquitto della Eclipse Foundation. | paho-mqtt |
colorlog | Libreria per la colorazione dei log in Python. | colorlog |
flask | Micro framework web per Python usato per il sito web. | Flask |
requests | Libreria per fare richieste HTTP in modo semplice e intuitivo. | – |
pyopenssl | Wrapper Python per la libreria OpenSSL, usata per crittografia e sicurezza. Usato per fornire https. | pyopenssl |
flask-login | Estensione Flask per la gestione dell’autenticazione degli utenti. | flask-login |
flask-mail | Estensione Flask per l’invio di email. | flask-mail |
alembic | Strumento di migrazione e versioning del database per SQLAlchemy. | alembic |
psutil | Libreria per l’accesso a informazioni di sistema e processi. Usato per lanciare aggiornamenti automatici nella pagina di sistema. | psutil |
flask-excel | Estensione Flask per il supporto dell’importazione e dell’esportazione di file Excel (analisi manuali o richieste di accesso ai dati GDPR). | flask-excel |
pyexcel-xlsx | Libreria per la lettura e la scrittura di file Excel in formato xlsx. | pyexcel-xlsx |
Flask-Babel | Estensione Flask per l’internazionalizzazione e la localizzazione delle applicazioni web. | Flask-Babel |
Pubblicare il software per altri utenti
PyPi, abbreviazione di Python Package Index, è il repository ufficiale di software per il linguaggio di programmazione Python. Funziona come una grande libreria online dove gli sviluppatori possono caricare e condividere i propri pacchetti Python, rendendoli facilmente accessibili alla comunità globale. Grazie a PyPi, gli utenti possono installare rapidamente le librerie necessarie utilizzando strumenti di gestione dei pacchetti come pip
.
Questo sistema centralizzato semplifica notevolmente la distribuzione e l’installazione di software, promuovendo la riusabilità del codice e accelerando lo sviluppo di applicazioni Python. Fab-O-Matic usa un’azione Github per pubblicare automaticamente i commit sul ramo main, che passano i test con successo, verso PyPi.
Conclusione
Lo sviluppo del back-end è stato molto più semplice e veloce rispetto al software embedded, perché i framework scelti sono molto rapidi da usare e ChatGPT ha velocizzato la scrittura del codice boilerplate.
Dopo il periodo di prova iniziale di funzionamento su Raspberry Pi Zero, a Giugno 2024, abbiamo spostato questo back-end su una macchina virtuale Debian 12 (1GB RAM) ospitata in un server PROXMOX configurato in FabLab dal BGLUG : se queste tecnologie ti incuriosiscono, vienici a trovare un martedì sera e troverai esperti con cui parlarne.
Nel prossimo articolo, vedremo come è stato possibile automatizzare i test di questi software embedded e back-end per garantire una buona qualità e evitare bug che potrebbero inficiare sul normale funzionamento delle macchine del FabLab !