SlideShare a Scribd company logo
1 of 44
Download to read offline
UNIVERSITÀ DEGLI STUDI DI
TRIESTE
Dipartimento di Ingegneria e Architettura
Laurea Triennale in Ingegneria dell’Informazione
Progettazione e sviluppo di un software
applicativo su single-board computer
2 dicembre 2016
Laureando Relatore
Alessandro Mascherin Chiar.mo Prof. Sergio Carrato
Correlatore
Ing. Piergiorgio Menia
Anno Accademico 2016/2017
Indice
Introduzione ii
Obiettivi della tesi . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Organizzazione della tesi . . . . . . . . . . . . . . . . . . . . . . . . iii
Strumenti utilizzati . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
1 Analisi Introduttiva 1
1.1 Descrizione problema . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Scelta dell’ambiente di lavoro . . . . . . . . . . . . . . . . . . 3
1.3 Descrizione dell’hardware . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Interfaccia e protocollo Wiegand . . . . . . . . . . . . 5
1.4 Scelta del sistema operativo . . . . . . . . . . . . . . . . . . . 6
2 Impostazione iniziale dispositivo 7
2.1 Installazione librerie . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Configurazioni di rete . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Partizioni del sistema operativo . . . . . . . . . . . . . . . . . 10
3 Sviluppo Software applicativo 13
3.1 Descrizione generale . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Modulo di stampa . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Modulo di analisi e gestione dell’input . . . . . . . . . . . . . 17
3.4 Modulo di configurazione . . . . . . . . . . . . . . . . . . . . 18
4 Automatizzazione del dispositivo 21
4.1 Script per la gestione del software applicativo . . . . . . . . . 21
4.2 Accesso via FTP e SSH e permessi utente . . . . . . . . . . . 22
Conclusioni 24
A Codice Sorgente 26
A.1 PrinterModule.cpp . . . . . . . . . . . . . . . . . . . . . . . . 26
A.2 InputParser.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 30
A.3 Configuration.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 33
A.4 Utilizzo Tiny Xml . . . . . . . . . . . . . . . . . . . . . . . . 38
i
Introduzione
La seguente tesi descriverà il lavoro svolto presso la Elimos S.r.l per il pro-
getto e lo sviluppo di un software applicativo implementato su single-board
computer. Il progetto prevede l’installazione di numerose unità distribuite
in rete in grado di leggere un Tag RFID, richiedere ad un server centrale
connesso in rete TCP/IP un set di informazioni legate al Tag in oggetto, e
stampare su stampante termica i dati ricevuti dal centro di controllo. Que-
sto sistema deve essere piccolo, inscatolato per ambiente industriale, ed in
grado di essere acceso/spento senza controllo di un operatore. A fronte del-
l’analisi dei requisiti e dei target di costo richiesti da un’azienda committente
esterna, si è deciso di utilizzare come unità centrale di elaborazione per ogni
modulo un single-board computer (SBC) embedded Raspberry Pi 2 model
B. Il lavoro si è sviluppato in più fasi. Nella prima, ci si è dedicati alla
scelta dell’ambiente di lavoro, andando a definire la totalità dell’hardware
su cui si sarebbe andati a lavorare e gli strumenti software a disposizione.
Nella seconda fase si è andati a sviluppare un software applicativo in gra-
do di soddisfare le richieste del committente. Si è cercato di sviluppare un
programma il più modulare possibile, per far fronte a diversi casistiche e
per garantire longevità al progetto. In fase di sviluppo si è lavorato paral-
lelamente sul codice applicativo e sulla parte piu’ sistemistica propria del
Raspberry, installando driver e pacchetti, e creando le librerie software ne-
cessarie al funzionamento del programma, il tutto per ottenere un sistema
operativo configurato in maniera opportuna. Nella fase finale del lavoro si è
andati a integrare l’applicativo sull’hardware testando completamente tutta
la connettività e ricercando delle soluzioni per rendere il dispositivo in grado
di operare in maniera autonoma senza operatore. In questa fase sono anche
stati sviluppati dei tool per permettere un accesso remoto alla SBC e special-
mente le procedure di aggiornamento e configurazione remota direttamente
sul campo.
Nei capitoli seguenti andremo ad esporre le soluzioni sviluppate, descri-
vendo le problematiche riscontrate in fase di sviluppo e le scelte effettuate
per soddisfare i requisiti di progetto.
ii
INTRODUZIONE
Obiettivi della tesi
L’obiettivo della tesi è quello di descrivere le operazioni da svolgere per
utilizzare un single-board computer ARM-based in ambiente non presidiato,
a partire dall’impostazione iniziale dell’ambiente di sviluppo per giungere
all’implementazione di un programma di una certa complessità sul sistema
oggetto di analisi. Uno dei requisiti cardine sui cui si è sviluppato il progetto
è stato quello di ottenere un sistema di semplice utilizzo per l’utente finale,
privo di interfacce per l’operatore. Il sistema deve essere interfacciato ad
un lettore di tessere RFID, ad una stampante termica e deve comunicare
attraverso la rete TCP/IP. Si è quindi lavorato per rendere il dispositivo in
grado di lavorare in maniera autonoma, gestendo eventuali configurazioni e
aggiornamenti in maniera remota.
Organizzazione della tesi
Nel capitolo 1 verrà effettuata un’analisi dei requisiti richiesti dal committen-
te e dei motivi che hanno portato alla scelta del Raspberry Pi come ambiente
di lavoro per la realizzazione del progetto. Verranno inoltre discusse alcune
possibili scelte relative ai sistemi operativi utilizzabili. Le varie configurazio-
ni del sistema operativo scelte e le principali librerie software installate sono
descritte nel capitolo 2. Nel capitolo 3 viene esposto il software applicativo
sviluppato, sia nel suo funzionamento generale che negli specifici moduli svi-
luppati dal laureando. Il capitolo 4 è dedicato alla descrizione degli strumenti
utilizzati per garantire un funzionamento autonomo del Raspberry, fornendo
nel contempo degli strumenti per l’aggiornamento e la configurazione remota
all’utente. Infine il capitolo 5 è dedicato alle conclusioni sul lavoro svolto,
mentre in appendice sono inseriti la bibliografia e parte del codice sorgente
opportunamente commentato.
Strumenti utilizzati
L’hardware su cui si è andati a sviluppare il progetto è stato il Raspberry
Pi 2 model B, un computer single-board rilasciato sul mercato nel febbraio
2015 dalla RaspberryPi Foundation™. Il Raspberry Pi è un computer co-
siddetto single-board, in quanto costruito su un’unica scheda circuitale, con
processore ARM in grado di supportare una vasta gamma di sistemi operativi
GNU/Linux. Il software sviluppato è stato scritto in C/C++ 11 sfruttando
l’ambiente di sviluppo integrato NetBeans. Sono stati inoltre scritti diversi
script bash e per alcune opzioni di configurazione vengono utilizzati degli
script AWK open-source.
iii
Capitolo 1
Analisi Introduttiva
In questa sezione verranno descritte le specifiche richieste al progetto e le
scelte iniziali fatte per l’implementazione del dispositivo.
1.1 Descrizione problema
Le specifiche di progetto prevedevano la realizzazione di un sistema senza in-
terfaccia utente in grado di implementare diverse funzionalità che verranno
di seguito esposte. A livello globale il sistema finale si presenta essenzial-
mente come un timbratore che deve fornire a ogni operatore un ruolino di
servizio con le attività prevista per la giornata lavorativa. Il dispositivo fina-
le necessita di una connessione con un server centrale, installato in sede del
committente, che contiene i dati relativi agli operatori e alle attività previste.
Per quanto riguarda la connettività, il dispositivo finale deve quindi potersi
collegare alla rete Internet tramite protocollo TCP/IP, attraverso la tecno-
logia Ehternet. La comunicazione con il server centrale avviene attraverso
un web-service. Altro aspetto fondamentale dei requisiti è la gestione del
sistema di input/output. Per poter stampare il ruolino di servizio, il dispo-
sitivo deve poter comunicare con una stampante termica collegabile tramite
l’interfaccia USB, il cui modello è stato stabilito dal committente. Il sistema
deve gestire un processo di analisi dei pin in input ricevendo dei dati da un
lettore di tessere magnetiche, collegato ai GPIO del Raspberry. Questo letto-
re utilizza l’interfaccia Wiegand per la trasmissione dei dati e deve pertanto
essere opportunamente collegato al dispositivo su cui si andrà a sviluppare il
progetto. Tramite lo stesso lettore è necessario fornire all’utente un feedback
sonoro e visivo delle operazioni che vengono svolte; il feedback viene invia-
to grazie al led e al buzzer presenti sul lettore di tessere. Questo sistema
deve analizzare la sequenza di bit che riceve dal lettore di tessere e, in se-
guito ad un’opportuna elaborazione, inviare una richiesta al server centrale
con i dati elaborati. Per comunicare con il server è stato necessario quindi
implementare un client web-service sul dispositivo, per poter effettuare una
1
CAPITOLO 1. ANALISI INTRODUTTIVA
convalida dei dati elaborati dal badge e ricevere delle opportune risposte dal
server. L’utilizzo dei web service è lo standard de facto per la comunica-
zione attraverso il Web di diversi dispositivi e diverse applicazioni software.
Secondo la definizione fornita dal W3C, il World Wide Web Consortium, le
web service sono la tecnologia standard per poter mettere in comunicazione
diversi applicazioni software installate su una grande varietà di piattaforme
e framework. Nel caso in analisi lo sviluppo di una web service permette di
poter ottenere una facile comunicazione con il server centrale, senza doversi
preoccupare della compatibilità fra diverse piattaforme e sistemi operativi.
Sono previste diverse tipologie di risposta da parte del web server, e
il sistema deve adottare un opportuno comportamento per ogni possibile
risposta:
• Codice di errore in caso di tessera sconosciuta
• Codice di errore differente nel caso di tessera conosciuta, ma rispo-
sta di errore da parte del web server (data/ora non valida, mancata
timbratura, o altro)
• In caso di risposta positiva da parte del web server è necessaria un’e-
laborazione dei dati ricevuti e la preparazione di un modulo di stampa
da inviare alla stampante termica collegata al sistema.
Durante l’elaborazione dei dati per la stampa o in presenza di un codice
di errore, deve essere previsto uno specifico feedback per l’utente tramite il
led e il sistema sonoro implementato nel lettore di tessere. In assenza di erro-
ri, i dati ricevuti devono venire stampati in dei ruolini di servizio con diversi
formati; è necessario quindi sviluppare un programma in grado di distinguere
le varie casistiche e di creare dei layout di stampa adeguati, in modo tale da
fornire ad ogni diverso operatore il suo specifico ruolino di servizio. Il tutto,
dovendo operare senza interfaccia utente, deve inoltre disporre di un sistema
per l’aggiornamento e la configurazione in modo remoto, senza la necessità
di accedere fisicamente ai dispositivi finali. Per queste procedure è stato
deciso di utilizzare i protocolli FTP e SSH per accedere al sistema. L’ag-
giornamento e la configurazione devono avvenire stabilendo una connessione
ad un indirizzo IP specifico, che deve essere attivo per un certo intervallo
temporale dopo il quale il sistema si deve connettere a un indirizzo IP di
servizio. Un ulteriore requisito è la robustezza sia del software applicativo,
sia del dispositivo in caso di malfunzionamenti. In particolare, non essen-
doci interfaccia utente, bisogna prevenire errori di memoria in seguito allo
spegnimento della scheda o in caso di problemi legati all’alimentazione.
2
CAPITOLO 1. ANALISI INTRODUTTIVA
1.2 Scelta dell’ambiente di lavoro
La prima decisione è stata quella di non utilizzare dei semplici microcontrol-
lori, in quanto non in grado di rispettare le specifiche fornite in modo sem-
plice ed economico. Per poter soddisfare in modo efficace i requisiti imposti
dal committente è necessario avere a disposizione un dispositivo che deve
offrire le funzionalità di un personal computer, mantenendo nel contempo
delle dimensioni contenute. Si è scelto quindi di sfruttare una single-board
computer embedded, dispositivi molto compatti e dal costo contenuto, in
grado di fornire prestazioni comunque elevate. Sono dei computer completi
che vengono installati su una singola scheda elettronica. Questi single-board
computer hanno avuto un’ampia diffusione negli ultimi anni per utilizzi edu-
cazionali o come controllori embedded. Pur non potendo competere con la
potenza di calcolo di un personal computer di fascia alta, hanno come van-
taggio le dimensioni estremamente contenute, un basso consumo e un costo
notevolmente inferiore. L’hardware preso in considerazione consisteva nel
Raspberry Pi 2 model B e nell’Odroid-C1plus. Sono schede molto simili in
caratteristiche e prestazioni, e sono quasi equivalenti quando utilizzati per la
realizzazione di questo tipo di progetti. Entrambi i prodotti sono basati su
processori ARM, hanno dimensioni comparabili e sono dotati delle interfacce
I/O richieste. L’Odroid è superiore al Raspberry in termini di caratteristi-
che hardware, mentre è leggermente inferiore in termini di supporto tecnico
e di reperibilità dei dispositivi. Essendo il costo dei due prodotti essenzial-
mente identico e non essendo necessarie particolari prestazioni hardware per
l’implementazione delle richieste del committente, si è deciso di iniziare lo
sviluppo sul Raspberry Pi, data la facile reperibilità del prodotto e il maggior
supporto tecnico presente in rete.
1.3 Descrizione dell’hardware
Figura 1.1: Foto del chip BCM2836
3
CAPITOLO 1. ANALISI INTRODUTTIVA
Il Raspberry Pi 2 model B è basato su un processore quad-core ARM
Cortex-A7 da 900MHz costruito dalla Broadcom™. In particolare il chip uti-
lizzato è il BCM2836, che va a sostituire il precedente BCM2835, installato
sulle versioni precedenti del RaspberryPi.
Questo modello di Raspberry dispone inoltre di 1 GB di memoria RAM, e
di una GPU, prodotta anch’essa dalla Broadcom™, da 250 MHz con OpenGL
ES 2.0 OpenVG 1080p30 H.264. Per quanto riguarda la connettività la
scheda dispone di 4 porte USB 2.0, utilizzando il Microchip™ LAN9514, una
porta Ethernet, una porta HDMI e un jack audio da 3,5 mm che comprende
anche il video composito utilizzabile con un adattatore a 4 poli. Per la
memorizzazione dei dati è necessario l’utilizzo di una scheda SD, collegabile al
Raspberry tramite un apposito slot. Le periferiche di basso livello installate
sulla scheda consistono in un modulo GPIO da 40 pin, di un Serial Peripheral
Interface Bus (SPI), di pins per le comunicazioni I2C e I2S, e un Universal
asynchronous receiver/transmitter (UART). La scheda per il funzionamento
richiede 5V in DC, che possono essere forniti tramite una porta Micro USB
di tipo B.
La suddivisione dei pin dell’interfaccia GPIO è illustrata nell’immagine
1.2:
Figura 1.2: Schema GPIO
I pins 2 e 4 sono pin utilizzati per alimentare dispositivi collegati all’in-
terfaccia a una tensione di 5V e con una corrente che può arrivare a 1,5A. I
pin da 5V possono essere anche utilizzati per alimentare il Raspberry stesso,
prestando attenzioni ad eventuali picchi di tensione che potrebbero danneg-
giare i pins del bus GPIO. In modo analogo i pin 1 e 17 forniscono una
tensione di 3.3V ma con una limitazione alla corrente fornita. Esistono poi
dei pin deputati a specifici metodi di input/output. Ad esempio i pins 3, 5,
27 e 28 vengono utilizzati per il sistema di comunicazione I2C mentre i pin 8
e 10 vengono utilizzati per le comunicazioni UART (Universal Asynchronous
Receiver/Transmitter).
4
CAPITOLO 1. ANALISI INTRODUTTIVA
L’immagine 1.3 raffigura il mechanical drawing del modello di Raspberry
utilizzato. Il drawing che viene presentato è quello del Raspberry Pi Model
B+, ma risulta valido anche per il Raspberry Pi 2 Model B. Le misure sono
espresse in mm.
Figura 1.3: Mechanical Drawing
1.3.1 Interfaccia e protocollo Wiegand
Un’altra componente hardware fondamentale del sistema oggetto di analisi
è costituita dal lettore di tessere magnetiche. Questo lettore utilizza un in-
terfaccia Wiegand per la trasmissione dei dati al Raspberry. L’interfaccia
Wiegand è costituita da 3 linee che devono essere collegate al bus GPIO
del Raspberry. Una linea costituisce la massa e le altre due linee vengono
utilizzate per la trasmissione dei dati e di solito vengono chiamate DATA0 e
DATA1. In assenza di dati da trasmettere le due linee vengono mantenute
ad un livello di tensione “alto” che generalmente consiste in 5 V in DC. Nel
caso in cui viene trasmesso un bit di valore 0, la tensione della linea DATA0
viene abbassata per una specifica durata temporale. Viceversa, nel caso in
cui viene trasmesso un bit con valore 1 è la tensione della linea DATA1 ad
essere abbassata, mentre DATA0 rimarrà su un valore di tensione alto. Le
tempistiche di questi segnali sono tipicamente 50-100 µs per gli impulsi in-
tervallati da 2ms, ma non sono assolutamente stringenti e possono variare
anche in percentuali significative. Questi bit vengono inviati secondo il for-
5
CAPITOLO 1. ANALISI INTRODUTTIVA
mato stabilito dal protocollo Wiegand standard da 26 bit. Questo formato
consiste in uno stream di 26 bit che sono così suddivisi:
Figura 1.4: Protocollo Wiegand
Il bit di testa e di coda dello stream vengono utilizzati come controllori
di parità. In particolare il primo bit è settato in modo tale che il numero di
“1” nei primi 13 bit dello stream sia pari, mentre l’ultimo viene impostato
per far sì che il numero di “1” negli ultimi 13 bit dello stream sia dispari.
Il facility code contiene le informazioni relative al modello e al produttore
della scheda. L’user code, chiamato anche ID code, come viene suggerito dal
nome, codifica un valore identificativo della singola tessera. Per l’analisi di
questi bit è stato sviluppato un modulo software che si occupa di eseguire
il controllo di parità della sequenza trasmessa e di separare il facility code e
l’ID code.
1.4 Scelta del sistema operativo
Sui single-board computer ARM-based è possibile installare una gran varie-
tà di sistemi operativi, generalmente linux-based. Nel caso del Raspberry i
sistemi operativi supportati ufficialmente sono Rasbian e Noobs, maggior-
mente adatti per un utilizzo di tipo didattico o dimostrativo della scheda.
Esistono tuttavia alcuni sistemi operativi che non sono sviluppati dalla Ra-
spberryPi Foundation™ ma che sono comunque ottimizzati per il Raspberry,
quali Ubuntu Mate, Windows 10 IOT, Snappy Ubuntu e altri. Si è deciso
di installare una versione di Ubuntu Mate, in quanto più adatta alle speci-
fiche di progetto rispetto ai sistemi sopra menzionati. Un’altra alternativa
presa inizialmente in considerazione è stata quella di installare un sistema
Android; l’ipotesi è stata scartata durante la prima fase di analisi in quanto
il sistema non è ufficialmente supportato e risulta spesso instabile sul mo-
dello di hardware scelto. L’implementazione di Android sarebbe stata una
valida scelta utilizzando l’Odroid, che supporta nativamente questo sistema
operativo.
6
Capitolo 2
Impostazione iniziale
dispositivo
In questa sezione verrà descritto il lavoro svolto per l’impostazione generale
della scheda a livello software e le soluzioni adottate per rendere il sistema
robusto in seguito a eventi non previsti dalle specifiche, quali l’interruzione
improvvisa dell’alimentazione o la rimozione della scheda di memoria. Ver-
ranno introdotte e analizzate le librerie software installate, le configurazioni
di rete adottate e un’importante modifica al sistema operativo, ovvero la
creazione di differenti partizioni di sistema configurate in modo differente
per risolvere diverse situazioni impreviste che potrebbero insorgere durante
l’utilizzo del dispositivo.
2.1 Installazione librerie
Nel corso del processo di sviluppo del software applicativo è stato necessario
installare diverse librerie software disponibili in Internet. In ambiente linux
sono disponibili un ampio numero di librerie, la maggior parte delle quali
gratis e open-source. L’installazione, l’aggiornamento e la rimozione di li-
brerie e pacchetti software è gestita di default da APT (Advanced Packaging
Tool), software che mette a disposizione vari comandi che facilitano queste
operazioni, come apt-get. Tutte le librerie analizzate in seguito sono state
installate utilizzando il comando apt-get. Le principali librerie che sono sta-
te installate e configurate per lo sviluppo del software applicativo sono le
seguenti:
• le librerie grafiche Pango e Cairographics utilizzate per la creazione del
modulo di stampa
• la libreria CUPS per la gestione della stampante termica
• la libreria libconfig++, utilizzata per il modulo di configurazione
7
CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO
• ntpdate per l’aggiornamento di data e ora
• TinyXML
Cairographics è una libreria grafica 2D che viene utilizzata per la rea-
lizzazione di grafica vettoriale in modo consistente su diversi dispositivi e
sistemi operativi. E’ una libreria open-source sviluppata in linguaggio C,
che offre numerosi bindings per altri linguaggi di programmazione. Pango è
una libreria che viene utilizzata per la gestione del layout e del rendering di
contenuto di tipo testuale. E’ integrata nativamente con Cairo ed è infatti
raccomandata dalla documentazione di Cairographics per la gestione di testi.
CUPS è un sistema di stampa open source sviluppato da Apple per siste-
mi UNIX-like e macOS. Generalmente CUPS è installato di default in tutti
i sistemi Ubuntu, ma per operazioni di controllo più avanzate è necessario
installare la versione per sviluppatori della libreria. L’utilizzo di CUPS ci ha
permesso di controllare la stampante termica con il programma sviluppato
in C++ e di poter avere un maggior controllo delle opzioni di stampa, quali
le dimensioni della superficie di stampa o la gestione del taglio dei fogli.
Libconfig è una libreria per la gestione di file di configurazione strutturati.
La libreria è scritta in C++, e prevede l’uso di file di configurazione scritti in
un formato più semplice e leggibile dell’XML. E’ stata scelta questa libreria,
tra le molte disponibili, per la semplicità nell’utilizzo e per la consolidata
compatibilità con i sistemi Unix.
Ntp è un protocollo basato su TCP/IP per l’aggiornamento dell’ora di un
computer, il cui funzionamento è basato sulla comunicazione fra un client,
in esecuzione sulla macchina di cui si vuole aggiornare l’ora, e dei server
che forniscono l’ora corrente. Ubuntu dispone di due diversi strumenti per
utilizzare ntp, chiamati ntpdate e ntpd. Nel sistema oggetto di analisi si è
deciso di installare ntpdate, strumento che contatta i server ntp ad ogni avvio
del sistema operativo per aggiornare l’ora del sistema. Questo strumento
inoltre richiede meno utilizzo di risorse rispetto a ntpd.
Tinyxml è un analizzatore di testo XML sviluppato in C++. Questa
libreria fa dei suoi punti di forza la semplicità di utilizzo e un basso utilizzo
di risorse di sistema per il suo funzionamento. E’ inoltre disponibile gra-
tuitamente ed è open-source, caratteristiche che hanno quindi portato alla
scelta di questa libreria rispetto ad altre disponibili sul mercato.
8
CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO
2.2 Configurazioni di rete
Di default il Raspberry adotta il protocollo DHCP per ottenere accesso alla
rete IP al quale è stato connesso. Nel caso in esame è sorta la necessità
di installare diversi dispositivi sulla stessa network, rimanendo in grado di
individuare in modo univoco tramite l’indirizzo IP ogni scheda in quella
data network. Questa identificazione univoca è necessaria solo per poter
effettuare aggiornamenti e controlli mirati al singolo computer. E’ stato
quindi deciso di impostare due diversi tipi di configurazione, i cui parametri
verranno gestiti dal programma in esecuzione sulla macchina. Si è andati a
creare due distinte interfacce di rete, una configurata in maniera statica e una
seconda configurata in maniera dinamica tramite DHCP. Per implementare
queste interfacce è stato necessario modificare il file /etc/network/interfaces
contenente i parametri di accesso e configurazione ethernet. In questo file,
di default è presente l’interfaccia di loopback, ovverosia il localhost. Inoltre,
in presenza di un accesso ethernet il sistema operativo aggiunge in maniera
autonoma un’interfaccia ethernet configurata tramite DHCP. Nel caso in
analisi il file si presentava inizialmente così:
auto lo
auto MAC-Address
iface lo inet loopback
iface MAC-Address inet dhcp
Nei dispositivi in uso il sistema operativo denomina di default l’interfaccia
ethernet con l’indirizzo MAC della stessa (denominato MAC-Address nel file
precedente). E’ stato necessario utilizzare il seguente script, scritto in bash,
per modificare in modo permanente questa impostazione:
#!/bin/bash
mymac="$(ifconfig | grep ’HWaddr’ | cut -d -f11)"
file="/etc/udev/rules.d/70-persistent-net.rules"
echo $mymac
if [ -f $file ]
then
echo "$file Found"
else
echo "$file Not Found"
echo ’SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*",
ATTR{address}=="’$mymac’", ATTR{dev_id}=="0x0",
ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" ’>> $file
fi
Lo script va ad inserire una nuova regola di sistema nella quale si imposta
il nome dell’interfaccia ethernet con eth0. Questa operazione è stata resa
necessaria per avere un software uniforme e semplice su tutti i dispositivi
che si andranno a configurare.
9
CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO
Effettuata questa modifica, si è proceduto a modificare il file /etc/networ-
k/interfaces. L’interfaccia di loopback non è stata modificata, mentre sono
state distinte due diverse interfacce ethernet, chiamate eth0-conf e eth0-work,
la prima destinata a essere configurata tramite indirizzo statico, la seconda
tramite DHCP. L’interfaccia eth0-conf è stata configurata con un indirizzo
IP statico, appartenente alla network privata nella quale è installato il Rasp-
berry. L’interfaccia eth0-work è stata impostata in modalità DHCP. Queste
due interfacce sono modificabili dal software applicativo secondo necessità,
basandosi su un opportuno file di configurazione. E’ stato inoltre imposta-
to che all’avvio la scheda non deve connettersi con nessuna interfaccia, in
quanto sarà uno specifico script bash che si occuperà di attivare la connes-
sione di configurazione (eth0-conf), mentre il software applicativo effettuerà
il cambio di interfaccia portandosi sull’interfaccia eth0-work.
Il file /etc/network/interfaces modificato si presenta quindi così:
auto lo
iface lo inet loopback
iface eth0-work inet dhcp
iface eth0-conf inet static
address some-IP
network some-networknumber
gateway some-gatewayIP
I parametri di rete quali l’indirizzo IP, il network number e l’indirizzo di
gateway vengono specificati nel file di configurazione.
2.3 Partizioni del sistema operativo
Uno dei principali requisiti di lavoro consiste nell’ottenere un sistema in gra-
do di operare senza interfaccia utente. Nel caso del sistema operativo preso
in considerazione è stato riscontrato un bug che al momento della realizza-
zione del progetto non era ancora diventato oggetto di analisi per il team di
sviluppo di Ubuntu Mate. In seguito il problema non è stato riconosciuto
come valido in quanto le build del sistema operativo in cui si verifica questo
problema non sono state considerate come ufficiali. Il problema consiste nel
fatto che, in caso di spegnimento del dispositivo in seguito all’interruzione
dell’alimentazione, al reboot il sistema operativo entrerà sempre in una mo-
dalità di emergenza, richiedendo un controllo ai file di sistema. Per uscire da
questa modalità di emergenza è necessario inserire manualmente le creden-
ziali di amministratore e riavviare nuovamente il sistema operativo. Tuttavia
questa modalità blocca il boot del sistema operativo e impedisce l’esecuzione
di qualsiasi altro processo che si vuole eseguire, andando pertanto a creare
un incongruenza con le specifiche di progetto. Questo sistema di emergenza
è stato implementato per verificare l’integrità del sistema in caso di spegni-
10
CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO
menti considerati anomali dal sistema operativo, andando però a inficiare il
funzionamento automatizzato del Raspberry.
Per risolvere questo problema si è deciso quindi di configurare il sistema
in modo tale da prevenire errori in caso di spegnimento anomalo, evitando
l’insorgere del problema descritto.
La soluzione adottata è stata quella di impostare una parte del sistema
operativo in modalità read-only, per prevenire errori in fase di scrittura nel
file-system. Non è stato possibile impostare l’intero sistema operativo in
modalità di sola lettura in quanto il software necessario per il funzionamento
della stampante ha necessità di creare dei file temporanei in diverse sezioni
del sistema operativo. Si è optato quindi per una configurazione in cui solo
la directory contenente i file necessari all’avvio del sistema operativo è im-
postata in modalità di sola lettura, in modo tale da prevenire il problema
della modalità di emergenza descritto in precedenza.
La tabella di configurazione dei file system è un file che contiene i dati
relativi alle partizioni di sistema e alle sue proprietà, ed è nota come fstab e
si trova all’interno della directory etc. La tabella è stata così impostata:
file system mount-point type options dump pass
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs nodev, nosuid, 0 0
size=100 M, mode=1777
/dev/mmcblk0p2 / ext4 defaults,noatime 0 2
/dev/mmcblk0p1 /boot vfat defaults,ro 0 2
E’ stata allocata una partizione sulla RAM del dispositivo, in cui vengono
destinati i processi di creazione di file temporanei generati dal software appli-
cativo. In questa partizione verranno creati tutti i file temporanei necessari
per il funzionamento del software applicativo che si è andati a sviluppare.
Grazie a questa partizione, nel caso in cui avvenga una rimozione imprevista
della scheda di memoria si previene la possibilità di andare a corrompere dei
file essenziali per il funzionamento del software applicativo. L’unico impre-
visto che può arrecare danni al sistema è l’interruzione dell’alimentazione o
la rimozione della memoria durante le operazioni di aggiornamento software,
eventualità che non può essere risolta sfruttando particolari partizioni della
memoria. Questa problematica può essere però risolta utilizzando altri ac-
corgimenti software, come ad esempio l’utilizzo di backup esterni e copie del
programma principale in altre sezioni della scheda di memoria. La scheda SD
è stata quindi divisa in due partizioni, la prima in formato ext4 che contiene
la maggior parte del sistema operativo e una partizione in formato vfat in
cui è stata allocata la sezione /boot. Per la partizione /dev/mmcblk0p2 è
stata impostata l’opzione noatime per limitare le operazioni di scrittura sulla
11
CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO
scheda di memoria. Linux generalmente associa ad ogni file degli attributi
in cui vengono memorizzati le date di creazione del file, di ultimo accesso
al file e di ultima modifica. Senza l’opzione noatime questi attributi ven-
gono aggiornati per ogni accesso ad uno specifico file. Impostando noatime
in una partizione quando il sistema operativo legge un determinato file di
sistema non aggiorna più gli attributi dello stesso, limitando notevolmente
le operazioni di scrittura, migliorando le performance del sistema operati-
vo e riducendo la possibilità di incorrere in errori in fase di scrittura. La
partizione /dev/mmcblk0p1 è stata impostata in modalità di sola lettura
per ovviare al bug descritto all’inizio di questa sezione e per evitare errori
in scrittura nella partizione. In questo modo tutto ciò che è necessario per
l’avvio del sistema operativo viene protetto dalla corruzione dei dati in caso
di interruzione dell’alimentazione.
12
Capitolo 3
Sviluppo Software applicativo
Tutti i moduli software descritti in questa sezione sono stati sviluppati dal
laureando. Il programma è stato scritto in C++.
3.1 Descrizione generale
Lo schema generale del sistema sviluppato è così composto:
Il Raspberry è collegato ad un lettore di schede magnetiche, a un led e a
un piezo monotonale tramite l’interfaccia GPIO. E’ inoltre collegato ad una
stampante termica tramite USB e a internet tramite ethernet. Il software
si occupa di gestire tutte le periferiche collegate al Raspberry, eseguendo le
opportune elaborazioni richieste dal committente. Il software è stato svilup-
pato in maniera modulare e i vari moduli software vengono attivati in caso di
necessità. Il sistema si avvia quando si rileva un input dal lettore di tessere
magnetiche. E’ presente quindi un modulo che esegue un loop in lettura dei
13
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
dati provenienti dal lettore di schede connesso all’interfaccia GPIO. Quando
avviene la ricezione dei dati verrà attivato un modulo per fornire un feedback
audiovisivo all’utente che ha utilizzato il badge magnetico, e un secondo mo-
dulo che si occupa di codificare i dati ricevuti per renderli compatibili con
quelli attesi dal WebServer. Dal WebServer il sistema si attende tre possibili
risposte: scheda valida e dei dati, scheda non valida e scheda non riconosciu-
ta. Per ogni risposta è previsto un differente feedback audiovisivo. L’analisi
dei dati ricevuti viene svolta dai moduli descritti nella sezione 3.3, moduli
dedicati alla codifica e all’interpretazione dei dati. In caso di scheda valida, i
dati ricevuti e codificati vengono ulteriormente elaborati per essere inviati in
stampa alla stampante termica. Il modulo software che si occupa di questa
operazione è descritto nella sezione 3.2. Il modulo di stampa fornisce dei
feedback sullo stato della stampa e sulla presenza di eventuali errori nei dati
al modulo che lo ha attivato; quest’ultimo modulo segnalerà all’utente in
modo opportuno tramite l’interfaccia GPIO i feedback ricevuti.
All’avvio inoltre il software attiva un modulo dedicato alla configurazione
del sistema e all’aggiornamento dello stesso nel caso in cui siano presenti
nuove opzioni di configurazione.
3.2 Modulo di stampa
La stampa viene gestita da un modulo sviluppato in C++ chiamato Prin-
terModule.cpp. Questo modulo fa uso delle librerie Cairographics, Pango
e Cups descritte nella sezione 2.1. Pango e Cairo vengono utilizzate per
generare un file in formato PostScript che viene poi inviato alla stampante
termica tramite la libreria Cups. Le stampe generate hanno un layout simile
a quello presentato nella seguente immagine:
Figura 3.1: Esempio di layout di stampa desiderato
Lo scopo di questo modulo software è quelli di posizionare le varie strin-
ghe desiderate sul foglio, secondo uno specifico layout. Sono state previste le
14
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
seguenti tipologie di stampa per soddisfare i requisiti forniti dal committente:
• Una stampa effettuata all’avvio del sistema, per segnalare all’operatore
che il programma è pronto per l’utilizzo; in questo messaggio si è deciso
di inserire dati relativi alla versione del software e all’indirizzo di rete
di lavoro al quale è stato configurato il sistema
• A seconda dei dati ricevuti dal webserver è prevista una stampa deno-
minata “responsabile” e una seconda stampa chiamata “operatore”
Le stampe operatore e responsabile hanno layout e dati differenti, pertanto
sono gestiti da due metodi differenti.
Per generare un file PostScript con Cairo è necessario creare un oggetto
denominato surface che funge da struttura di base del file finale. Questa
surface è inizializzata specificando la tipologia di file che si vuole andare a
creare, in questo caso PostScript. Le dimensioni del file PostScript sono
espresse in punti tipografici. Questa unità di misura può essere convertita
utilizzando la seguente relazione: 1 punto tipografico = 1/72 inch = 0,35278
mm = 0,75 pixel. Questa equivalenza è importante in quanto Cairo for-
nisce le dimensioni del file generato in pixel o in punti tipografici, mentre
CUPS ha bisogno di un valore espresso in millimetri per un corretto taglio
della stampa. Ottenuta la superficie di lavoro il programma genera un og-
getto chiamato context su cui vengono effettuate le operazioni di disegno e
scrittura. Cairo fornisce un API per la gestione testuale che però presenta
diverse limitazioni e di cui è sconsigliato l’uso se non per prove o demo. Per
il progetto in analisi, se si fosse deciso di utilizzare Cairo anche per l’inse-
rimento del testo il codice sarebbe risultato molto più complesso in quanto
la gestione di diverse tipologie di font e il posizionamento del testo in modo
dinamico non sono ben gestiti dalle funzioni fornite dall’API. L’inserimento
del testo viene eseguito utilizzando la libreria Pango. Con questa libreria
è possibile definire agevolmente diversi font di scrittura, che possono venire
utilizzati per una preparazione più personalizzata della stampa. Per ogni
font desiderato si inizializza una struttura chiamata PangoFontDescription
in cui vengono specificati tramite diverse funzioni il colore, la dimensione, la
famiglia del font e altre opzioni. Il testo viene posizionato sulla superficie di
lavoro tramite coordinate cartesiane, la cui unità di misura è il punto tipo-
grafico e la cui origine è nell’angolo superiore sinistro della pagina. Pango
permette inoltre di definire vari parametri per il layout, quali le coordinate
di inserimento del testo, fornite da Cairo, l’interlinea tra le righe, la larghez-
za massima del testo e il “text wrapping”, ovvero le opzioni di gestione del
testo se questo supera una cella di dimensioni prefissate. Quando si procede
all’inserimento del testo si specifica il layout scelto e il descrittore del font
che si vuole utilizzare.
Uno dei problemi sorti durante lo sviluppo di questo modulo è stato il
calcolo delle dimensioni della stampa finale, in particolare della lunghezza
15
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
del file PostScript, parametro essenziale per il funzionamento corretto della
stampante. Il problema sorge in quanto in alcune tipologie di stampa previste
è variabile sia il numero di parametri che la lunghezza delle stringhe fornite.
Non è quindi possibile calcolare a priori la lunghezza finale del foglio ed è
stato necessario ricorrere a due cicli di impaginazione, il primo per il calcolo
della dimensione finale, e il secondo per la creazione del file PostScript finale.
Un’altra funziona importante implementata in questo modulo è quella
dedicata alla gestione della stampante termica tramite il sistema di stampa
CUPS. Questa funzione non si occupa solamente di inviare alla stampante
il file PostScript creato, ma deve anche rilevare eventuali malfunzionamenti
nella stampante per poterli segnalare all’utente tramite una specifica codifica
audio-visiva. Il programma per prima cosa crea una opzione di stampa per-
sonalizzata inizializzando l’oggetto cups_option_t. In questo oggetto viene
inserita la lunghezza del foglio che verrà stampato, determinata dalla funzio-
ne che crea il file PostScript, e la larghezza, che ha un valore predefinito. In
seguito la funzione invia un comando per svuotare la coda di stampa, questo
per evitare che in caso di errori e mancata stampa di una qualche chiamata
precedente della funzione non si accumulino troppe stampe in coda. Questo
potrebbe risultare problematico ad esempio nel momento in cui la carta o
il toner risultassero esauriti, poiché al ripristino della stampante potrebbero
esserci troppe stampe in attesa ed eventualmente multiple se l’utente ha pas-
sato il badge più volte sul lettore. Gli errori di stampa vengono rilevati da
un loop che verifica che, per ogni stampa che è stata inserita nella coda, lo
stato inviato come feedback dalla stampante sia “printing”. Nel caso in cui le
stampe in coda rimangono in uno stato di “pending” o di altro errore è neces-
sario segnalare all’utente il malfunzionamento della stampante. La funzione
terminerà quindi inviando un valore false (EXIT_FAILURE) al metodo
che l’ha invocata, che si occuperà quindi di segnalare il malfunzionamento
tramite il led e un segnale sonoro.
16
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
3.3 Modulo di analisi e gestione dell’input
In questa sezione verranno descritti i procedimenti eseguiti per l’analisi e la
gestione dei dati ricevuti dal WebServer in seguito alla lettura di un badge
tramite il lettore di tessere magnetiche. Il software applicativo contatta il
WebServer ad un URL specificato nei parametri di configurazione, che ver-
ranno descritti in seguito, inviando il codice della tessera magnetica e la
data. Il WebServer invia una risposta in un formato testuale, la cui sintassi
è molto simile a quella del linguaggio marcatore XML. Infatti, a causa di
un’errata configurazione del WebServer, alcuni caratteri vengono codificati
con una sintassi non corretta; ad esempio i tag ’<’ e ’>’ vengono letti dal
web-service client come ’&lt’ e ’&gt’. La prima operazione che viene esegui-
ta dal client è pertanto quella di convertire questa risposta in XML “puro”,
in modo tale da poter esplorare agevolmente i dati ricevuti. Effettuata la
conversione viene chiamata una funzione che ha il compito di eseguire un
parsing del testo XML per salvarlo in un vettore di stringhe. Il parsing
viene effettuando utilizzando la libreria TinyXML introdotta in preceden-
za (sezione 2.1). TinyXML a partire dal documento XML costruisce un
Document Object Model (o DOM), ovverosia struttura il documento come
oggetti C++ che possono essere esplorati e manipolati. Le varie sezioni del
documento XML vengono quindi modificate a seconda del loro significato; ad
esempio un commento, espresso in XML nella forma <!–Comment–>, diven-
ta un oggetto denominato TiXmlComment. Nell’analisi che si vuole andare
ad effettuare gli elementi rilevanti sono i tag, elaborati come TiXmlElement,
e il testo contenuto nelle varie sezioni, che diventa un oggetto TiXmlText.
L’algoritmo utilizza questi oggetti per analizzare il documento, inserendo in
un vettore di stringhe i vari elementi testuali che vengono incontrati duran-
te l’esplorazione dell’albero del DOM. L’algoritmo termina quando tutti gli
TiXmlElement sono stati analizzati e restituisce il vettore di stringhe al me-
todo invocante. Ogni stringa che viene inserita nel vettore contiene i dati
relativi per una singola stampa che si vuole andare a ottenere. I singoli va-
lori testuali che devono essere inseriti nel foglio che si vuole ottenere sono
inseriti con un ordine prestabilito e separati da un carattere con la funzione
di delimitatore. Queste stringhe devono essere quindi ulteriormente analiz-
zate per venire scomposte in vari “tokens” che potranno essere utilizzati dal
modulo di stampa. Questa operazione viene gestita da un modulo software
denominato InputParser. Il modulo inizialmente esplora la stringa fornitagli
in input andando a cercare al suo interno il carattere fornito come delimita-
tore, separando la stringa principale in sottostringhe in corrispondenza dei
delimitatori. La seconda operazione consiste nel riconoscimento della tipo-
logia di stampa, indicata dalla prima sottostringa. Sono previsti due casi
di stampa e per ogni tipologia viene effettuata un’ulteriore elaborazione dei
tokens. In un caso, denominato “operatore”, è previsto un numero di para-
metri stabilito a priori, pertanto l’algoritmo verifica se il numero di stringhe
17
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
è corretto, producendo una segnalazione di errore per il metodo invocante
nel caso in cui il controllo non venga superato. Fra le stringhe oggetto di
analisi è presente una stringa che ha il valore di una data che, da specifi-
che, deve essere convertita dal formato “dd/mm/yyyy” (giorni, mesi e anno
espressi in formato numerico) nel formato “dayname dd/monthname/yyyy”
dove dayname è il nome esteso in italiano del giorno, e monthname il no-
me esteso del mese considerato. La conversione viene effettuata sfruttando
le funzioni strptime e strftime di linux. Strptime converte una stringa che
rappresenta un’indicazione temporale in una struttura di tipo tm. Questa
struttura è definita nell’header time.h presente nella libreria standard di C.
Al suo interno vengono inseriti tutti i dati relativi alle informazioni temporali
contenute nella stringa che viene fornita a strptime. La struttura tm viene
poi riconvertita in stringa sfruttando la funzione strftime secondo i parametri
di formattazione desiderati.
Nel secondo caso, denominato “responsabile” i dati contenuti nei tokens
devono essere ulteriormente suddivisi in quanto prevedono a loro volta dei
delimitatori. Per ogni token viene quindi applicato l’algoritmo utilizzato
inizialmente per suddividere la stringa di input nei vari tokens.
3.4 Modulo di configurazione
La libreria libconfig++ permette di creare file di configurazione molto ver-
satili e in grado di fornire diversi dati al sistema. Il formato del file di confi-
gurazione è di tipo testuale ed è costruito per essere più leggibile e compatto
dell’XML. E’ inoltre un sistema di gestione della configurazione che richiede
poche risorse e, visto il numero non elevato di parametri da configurare nel
progetto in analisi, si è rivelato la scelta più opportuna.
Una configurazione, utilizzando la sintassi di questa libreria, è costituita
da un insieme settings associate ad un nome e a uno o più valori. I valori
possono essere valori scalari, array di valori, gruppi di settings, o liste. La
sintassi per definire una setting è la seguente: name:value; o name=value; i
gruppi vengono identificati dalle parentesi grafe. Ogni settings viene iden-
tificata in maniera univoca da un percorso (path), che è costituito da una
sequenza di nomi, separati da punti, che partono dal nome del gruppo di
livello più alto fino a giungere al nome della setting stessa. Un altro vantag-
gio nell’utilizzare questa libreria consiste nel fatto che il tipo dei valori viene
determinato in automatico dal valore stesso del dato. Ad esempio un valore
racchiuso fra apici viene considerato di tipo stringa; una stringa ’true’ (o ’fal-
se’), senza tenere in considerazione maiuscole o minuscole, viene considerata
come un parametro di tipo booleano.
18
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
Il file di configurazione per il progetto è stato così strutturato:
# --------------------------
# Example Configuration File
# --------------------------
version = "1.0.0.0";
authWebServiceURL = "http://192.168.50.50:8725/ws.asmx";
NetworkParam = {
conf = {
dhcp = FALSE;
address = "192.168.50.150";
gateway = "192.168.50.254";
netmask = "255.255.255.0";
network = "192.168.20.0";
dnsVal = "8.8.8.8 8.8.8.4";
}
work = {
dhcp = FALSE;
address = "192.168.50.151";
gateway = "192.168.50.254";
netmask = "255.255.255.0";
network = "192.168.20.0";
dnsVal = "8.8.8.8 8.8.8.4";
}
}
I parametri principali contenuti nel file di configurazione sono quindi la
versione del software, l’indirizzo URL del WebService con il quale il program-
ma dovrà comunicare e i parametri per la configurazione di rete. Il modulo
C++ di configurazione del software applicativo si occupa di effettuare un’a-
nalisi del documento e di estrarre i dati contenuti nel file, sfruttando le
funzionalità della libreria. L’indirizzo del WebService viene salvato e utiliz-
zato quando si verifica la necessità di contattare il WebService per ottenere
dei dati relativi a una tessere magnetica. I parametri di rete vengono utiliz-
zati come input per uno script AWK che si occupa della configurazione di
rete. Questo script è stato sviluppato da terze parti e consente la lettura e la
modifica del file /etc/network/interfaces, descritto nel paragrafo 2.2, senza
necessità di modificare direttamente il file dei parametri di rete. L’utilizzo
di questo script semplifica quindi il programma C++, in quanto è sufficiente
eseguire lo script senza la necessità di accedere ai file di sistema.
19
CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO
Lo script, chiamato changeInterfaces.awk viene eseguito nel seguente
modo:
awk -f /path/to/changeInterfaces.awk <interfaces file>
dev=<eth device>
[address=<ip addr>] [gateway=<ip addr>] [netmask=<ip addr>]
[network=<ip addr>] [mode=dhcp|static] [dns=<ip addr>]
Tutti i parametri indicati con le parentesi quadre sono opzionali, se non
inseriti non vengono modificati dallo script. Il modulo prepara quindi una
stringa corrispondente al comando awk che si vuole eseguire e la manda in
esecuzione sfruttando i privilegi di amministratore di cui è dotato il software.
Un’altra importante funzionalità gestita da questo modulo software è il
controllo dell’autenticità del programma. Per evitare che il software instal-
lato sulla scheda SD, che funge da memoria per il Raspberry, possa venir
clonato in modo incontrollato, è stato deciso di inserire una licenza univoca
per ogni dispositivo. La licenza sviluppata è legata ai parametri hardware
del Raspberry che, essendo univoci, permettono di legare in maniera indisso-
lubile licenza e hardware. I parametri hardware non sono inseriti “in chiaro”
all’interno della licenza, ma vengono elaborati e crittografati utilizzando un
sistema a codifica XOR. Per evitare manomissioni del file contenente la li-
cenza, essa è stata inserita nella partizione /boot impostata in modalità
read-only (descritta nella sezione 3.3), che risulta accessibile in lettura a tut-
ti gli utenti, ma modificabile solo da un utente amministratore che decida di
reimpostare la partizione in modalità di scrittura. La licenza viene infatti ge-
nerata dall’amministratore di sistema al momento della prima installazione
del software applicativo o in caso di sostituzione della scheda SD. Il modulo
software qui trattato esegue un algoritmo che gli permette di calcolare a sua
volta il codice di licenza che verrà poi confrontato con quello contenuto nella
partizione protetta. Il risultato e il timestamp di questo controllo viene sal-
vato su un file di log accessibile via FTP. Il risultato sarà rispettivamente un
valore EXIT_SUCCESS nel caso in cui il controllo risulti superato, per-
mettendo la prosecuzione del programma, o un valore EXIT_FAILURE
nel caso in cui il controllo fallisca, caso che verrà trattato opportunamente
dalla classe che ha invocato questa funzione.
20
Capitolo 4
Automatizzazione del
dispositivo
In questa sezione verranno descritti gli interventi fatti per rendere auto-
matizzato il funzionamento del dispositivo. In particolare verrà descritto e
analizzato lo script bash utilizzato per eseguire il software applicativo, i tool
e le configurazioni adottate per l’utilizzo di FTP e SSH.
4.1 Script per la gestione del software applicativo
Per poter eseguire all’avvio il software applicativo, è stato sviluppato uno
script bash che viene eseguito all’avvio del sistema. Questo script svolge
diverse funzioni: si occupa di monitorare la cartella dove vengono caricati
tramite il protocollo FTP i nuovi file di configurazione e di spostare questi
file nella sezione apposita del dispositivo, crea un log delle accensioni e man-
da in esecuzione il software applicativo. La prima operazione svolta dallo
script è quella di attivare l’interfaccia di rete di default, ovvero l’interfaccia
di configurazione che, come visto nel paragrafo 2.2 è stata configurata tra-
mite indirizzo IP statico. Il log delle accensioni della scheda viene generato
salvando gli ultimi 50 accessi, con data di accesso, data dell’ultimo login e
data dell’ultima configurazione. Questi dati vengono inseriti in un file che è
visualizzabile tramite FTP o SSH. Quando viene eseguito, lo script controlla
se sono stati inseriti nuovi aggiornamenti in una cartella definita in fase di
configurazione. Se il controllo non viene superato, lo script continua a mo-
nitorare la cartella in questione per 30 secondi tramite il tool “inotifywait”.
Inotifywait è un tool utilizzato per monitorare dei cambiamenti su specifici
files, utilizzando l’API inotify di linux. Questa API mette a disposizione
diverse system call per il monitoraggio di files e cartelle. Nel progetto in
analisi è stata scelta inotifywait in quanto più adatta per essere usata in
shell script. Se nel controllo iniziale, o nei 30 secondi successivi vengono
trovati aggiornamenti, lo script crea un backup del programma precedente
21
CAPITOLO 4. AUTOMATIZZAZIONE DEL DISPOSITIVO
e posiziona i nuovi file nel percorso voluto. Per l’esecuzione del programma
viene eseguita una fork dello script in esecuzione che viene messa in stato
di “sleep” per 50 secondi. Al termine di questo periodo il processo generato
tramite la chiamata di sistema fork() manda in esecuzione il programma e
invia un comando kill() tramite il segnale SIGUSR1 a se stesso e al processo
che l’ha generato. Tramite questa procedura il processo figlio dello script,
ovvero il software applicativo, viene “adottato” da root, garantendogli quindi
i permessi di amministratore per tutta la durata del programma. L’utiliz-
zo della system call kill() viene adottato anche per terminare eventuali casi
non previsti o errori durante l’esecuzione dello script, come ad esempio er-
rori duranti il loop di monitoraggio della cartella FTP o nella creazione del
backup.
In ambiente linux esistono diverse vie valide per eseguire un programma
o uno script all’avvio del sistema operativo. Una di queste prevede la crea-
zione di uno script, basato su un preciso standard, nella directory /etc/init.d
per eseguire un dato programma. Lavorando con il Raspberry esiste un’al-
ternativa più semplice per eseguire un comando o un programma al boot
del sistema operativo, senza la necessità di ulteriori script o configurazioni.
Questa alternativa consiste nell’inserire i comandi desiderati all’interno del
file rc.local, il cui percorso è /etc/rc.local. Nel caso in analisi la modifica del
file è stata l’inserimento della seguente stringa all’interno di rc.local:
sudo /path/to/ConfigMonitorRoutine.sh &
Il comando esegue con i permessi di superuser lo script analizzato in prece-
denza, chiamato appunto ConfigMonitorRoutine.sh. Il carattere ‘&’ è stato
inserito per eseguire una chiamata di sistema fork() del processo, consenten-
do allo stesso tempo il proseguimento dell’esecuzione dello script rc.local e
la conclusione del boot del Raspberry.
4.2 Accesso via FTP e SSH e permessi utente
Per consentire l’aggiornamento e un monitoraggio in remoto del sistema si è
deciso di abilitare un server FTP e di configurare opportunamente un SSH
server su ogni sistema operativo installato.
La prima operazione è stata quella di creare due tipologie di utente, la
prima con i permessi di superuser, mentre la seconda come utente standard,
senza privilegi di root. Questo per consentire solo agli amministratori di
sistema di effettuare modifiche significative al progetto. L’idea è quella di
garantire l’accesso completo da remoto al sistema solo agli utenti in possesso
dei privilegi di amministratore, mentre gli utenti standard devono poter avere
accesso solo a un particolare server FTP usato per gli aggiornamenti e per
la lettura di alcuni log di sistema.
22
CAPITOLO 4. AUTOMATIZZAZIONE DEL DISPOSITIVO
E’ stato quindi attivato un server FTP su ogni scheda, garantendo l’ac-
cesso a tutti gli utenti autorizzati solo a una specifica cartella. Questa car-
tella verrà utilizzata per l’inserimento di nuovi aggiornamenti del software
applicativo e del file di configurazione. In questa cartella vengono inoltre
generati alcuni file di log, in modo tale da garantire un primo metodo per
diagnosticare eventuali problemi avvenuti durante l’avvio del programma.
La configurazione del server FTP è stata fatta utilizzando vsftpd uno dei
programmi più usati disponibili in ambiente linux per l’installazione di un
server FTP. L’installazione di vsftpd viene fatta sfruttando il comando apt-
get, mentre per la configurazione dello stesso si procede alla modifica del file
vsftpd.conf, che viene collocato al momento dell’installazione nella directory
/etc. Il file di configurazione è stato quindi modificato negando l’accesso tra-
mite FTP ad utenti anonimi e limitando l’accesso alle due tipologie di utenti
prima descritte solo a una particolare cartella che si è deciso di collocare
nella directory /var.
Per consentire agli amministratori un accesso completo in remoto al si-
stema operativo è stato inoltre abilitato un server SSH. Anche in questo caso
per l’installazione si è utilizzato il tool apt-get, e per la configurazione è sta-
to necessario modificare il file di configurazione relativo, collocato anch’esso
nella directory /etc.
23
Conclusioni
Il dispositivo che si è andati a sviluppare si è dimostrato in grado di soddisfa-
re le specifiche emerse in fase di analisi introduttiva. Sono state utilizzate in
modo efficace tutte le periferiche imposte dal committente ed è stato svilup-
pato un opportuno sistema di feedback per l’utente. Il funzionamento della
web service e la comunicazione con il web server sono stata testati in modo
estensivo, così come i moduli software per l’elaborazione dei dati. Il sistema
si è inoltre dimostrato molto robusto, garantendo così stabilità al progetto.
Il software sviluppato è stato installato su diversi Raspberry Pi 2, che dopo
esser stati opportunamente configurati, sono adesso operativi nelle sedi sta-
bilite dal committente. Grazie alle soluzioni adottate per l’aggiornamento e
la configurazione remota del sistema, c’è la possibilità di migliorare ulterior-
mente il sistema in futuro e eventualmente di sviluppare nuove funzionalità.
Alcune delle soluzioni che sono state sviluppate sono inoltre utilizzabili anche
su differenti progetti basati sulla stessa tipologia di hardware. In particolare
l’impostazione di parte del file-system in modalità di sola lettura, lo script
per l’esecuzione all’avvio di un software applicativo e l’abilitazione degli ac-
cessi FTP e SSH sono strumenti che possono essere agevolmente utilizzati in
altri progetti basati sul Raspberry Pi o schede simili.
24
CONCLUSIONI
Figura 4.1: Foto del progetto ultimato e preparato per l’utilizzo industriale
Figura 4.2: Foto del progetto installato in ambito industriale
25
Appendice A
Codice Sorgente
In questa sezione verranno presentate le parti più significative del codice
sviluppato. Alcune sezioni sono state omesse per motivi di riservatezza.
A.1 PrinterModule.cpp
Classe contenente le varie funzioni dedite alla creazione di file PostScript per
la stampa e la gestione della stampante termica. Verrà presentata solo una
delle funzioni di stampa, quella per la creazione della stampa all’avvio. La
creazione delle stampe in seguito all’analisi dei dati ricevuti dal WebServer
è analoga, ma con un layout molto più complesso. I dati per questo tipo di
stampa vengono ottenuti dalla classe InputParser.c .
#include "PrinterModule.h"
#include "Periph/InputParser.h"
using std::string;
using std::vector;
/*
Definizione delle dimensioni del foglio e di alcuni parametri
come il bordo laterale e l’interlinea
Questi valori sono espressi in punti tipografici
*/
#define WIDTH 520
#define HEIGHT 2080
#define INTERLINE 20
#define BORD 0
#define FONT "Calibri"
26
APPENDICE A. CODICE SORGENTE
int settext (cairo_t *context, PangoFontDescription *desc,
int x, int y, char string[]){
/* Questo metodo posiziona una stringa su un cairo context alle
coordinate desiderate. */
PangoLayout *layout;
//Utilizzo dei metodi della librerie pango e cairo per la
gestione del testo
cairo_translate(context, x, y);
layout = pango_cairo_create_layout(context);
pango_layout_set_width(layout, WIDTH*PANGO_SCALE);
pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
pango_layout_set_spacing
(layout,(int)INTERLINE*PANGO_SCALE*0.5);
pango_layout_set_text(layout, string, -1);
pango_layout_set_font_description(layout, desc);
pango_cairo_update_layout(context, layout);
pango_cairo_show_layout(context, layout);
int width;
int height;
pango_layout_get_size (layout, &width, &height);
height =(int)(height/PANGO_SCALE);
cairo_translate(context, -x, -y);
g_object_unref(layout);
return height;
}
int setcenteredtext (cairo_t *context, PangoFontDescription *desc,
int x, int y, char string[]){
/*Posiziona il testo al centro della riga utilizzando il metodo
precedente*/
int x_len;
int y_len;
//funzione che fornisce lunghezza del testo in px
pango_layout_get_pixel_size (layout, &x_len, &y_len);
//calcola la lunghezza del testo in punti tipografici
x_len=0.75*x_len;
//calcola la posizione del testo centrato
int x_coord=0.5*(WIDTH-x_len+x);
return settext (context, desc, x_coord, y, string);
}
27
APPENDICE A. CODICE SORGENTE
int PrinterModule::PrintRoutine(char tmpfilename[], int len){
/*Questa sezione del programma dedicata all’invio alla stampante
termica dei file PostScript creati*/
int num_jobs;
cups_job_t *joblist; //Oggetto contenente la coda di stampa
//Determinazione della stampante di default
cups_dest_t *dests;
int num_dests = cupsGetDests(&dests);
//Creazione delle opzioni di stampa
int num_options;
cups_option_t *options;
num_options = 0;
options = (cups_option_t *)0;
int mylen=(int)((len+10)/10)*10;
string optiontext = "job-sheets=none,none
media=Custom.72x"+std::to_string(mylen)+"mm sides=one-sided";
//Impostazione delle opzioni di stampa
num_options = cupsParseOptions(optiontext.c_str(), num_options,
&options);
//Svuotamento della coda di stampa
cupsFreeDests(num_dests, dests);
cupsCancelJob(cupsGetDefault(), CUPS_JOBID_ALL);
//Inizio della stampa
int job = cupsPrintFile(cupsGetDefault(), tmpfilename, "cairo
PS", num_options, options);
num_jobs = cupsGetJobs(&joblist, NULL, 0, -1);
/*Controllo di eventuale stampe bloccate ed eventuale
segnalazione dell’errore.
Il controllo viene fatto verificando che le stampe inserite in
coda di stampa siano in uno stato di "printed"*/
for (int i = 0; i < num_jobs; i ++){
printf("%-16.16s %-6d %-12.12s %s (%s)n", joblist[i].dest,
joblist[i].id,
joblist[i].user, joblist[i].title,
joblist[i].state != IPP_JOB_PENDING ? "printed" : "pending");
if(joblist[i].id == job) {
if(joblist[i].state == IPP_JOB_PENDING ){
28
APPENDICE A. CODICE SORGENTE
std::cout << "Errore nella stampa"<<’n’;
cupsFreeOptions (num_options, options);
cupsFreeJobs(num_jobs, joblist);
return EXIT_FAILURE;
}
}
}
cupsFreeJobs(num_jobs, joblist);
cupsFreeOptions (num_options, options);
return EXIT_SUCCESS;
}
int PrinterModule::StartingPrint (string Version, string
MACAddress, string IPAddress){
char filename[]="/tmp/StartingPrint.ps";
cairo_surface_t* surface;
cairo_t *context;
PangoFontDescription *font;
//Creazione di una surface con una specifica dimensione (in
punti tipografici)
int len = 500;
double mmlen=len*(0.35)/(2.5);
surface=cairo_ps_surface_create (filename, WIDTH, len);
//Creazione del context
context=cairo_create (surface);
//Creazione di un descrittore di font
font=pango_font_description_new ();
pango_font_description_set_size (font,17*PANGO_SCALE);
pango_font_description_set_family (font, FONT);
pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD);
settext(context, font, BORD, 0, (char*)"QAS SERVICE REPORTER
STARTED");
settext(context, font, BORD, 50,
(char*)"------------------------------------------------------");
std::string Versiontext="Version: "+Version;
settext(context, font, BORD, 100, (char*)Versiontext.c_str());
std::string LicenseText="License OK";
settext(context, font, BORD, 150, (char*)LicenseText.c_str());
std::string MACtext="MACAddress: "+MACAddress;
settext(context, font, BORD, 200, (char*)MACtext.c_str());
std::string IPtext="Work IP Address: "+IPAddress;
settext(context, font, BORD, 250, (char*)IPtext.c_str());
settext(context, font, BORD, 300,
(char*)"------------------------------------------------------");
29
APPENDICE A. CODICE SORGENTE
//Salvataggio del file e distruzione degli oggetti creati
cairo_surface_flush(surface);
cairo_surface_destroy(surface);
cairo_destroy(context);
pango_font_description_free(font);
//Inizio stampa
PrintRoutine(filename, (int)mmlen);
return EXIT_SUCCESS;
}
A.2 InputParser.cpp
Classe contente le funzioni che vengono utilizzate da PrinterModule.cpp per
ottenere le stringhe da inserire nel modulo di stampa.
#include "InputParser.h"
using std::string;
using std::vector;
vector<string> InputParser::str_decode (string Input, string delim){
//Questa funzione scompone la stringa in input in vari token
separati dalla sottostringa delim
vector<string> res;
int start = 0;
int end = Input.find(delim);
while (end != std::string::npos)
{
res.push_back (Input.substr(start, end - start));
start = end + delim.length();
end = Input.find(delim, start);
}
return res;
}
int InputParser::ConvertDate (char *input){
/*Converte la data dal formato "dd/mm/yyyy" nel formato
"local_dayname dd/mmmm/yyyy" */
setlocale (LC_ALL, "" );
struct tm tm;
char buf[255];
30
APPENDICE A. CODICE SORGENTE
memset(&tm, 0, sizeof(struct tm));
//Utilizzo di strptime
if(strptime(input,"%d/%m/%Y",&tm)==NULL){
printf ("Formato data erraton"); //input non valido
return(EXIT_FAILURE);
};
//Utilizzo di strftime
if(strftime(buf, sizeof(buf), "%^A %d %^B %Y", &tm)==0) {
printf ("Errore conversione datan"); //contents of buf are
undeterminate
return(EXIT_FAILURE);
};
std::string Date ((const char*) buf);
this->Date=Date;
return(EXIT_SUCCESS);
}
int InputParser::CheckTokens (vector<string> tokens){
//Controlla il numero di tokens per l’input di tipo "operatore"
if (tokens.size()<6) {
printf("Numero Parametri Insufficienten");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int InputParser::GetOperator (){
this->Operator = this->tokens[0];
tokens.erase(tokens.begin());
return EXIT_SUCCESS;
}
int InputParser::DecodeInput (string input, string delim){
//Scompone l’input e prepara i parametri necessari alla classe
PrinterModule.cpp
this->tokens = str_decode (input, delim);
this->GetOperator();
if (Operator == "O") {
if (this->CheckTokens(tokens) == EXIT_FAILURE) {
return EXIT_FAILURE;
}
this->Name = str_decode (tokens[0],";");
if (Name.size()==0){
31
APPENDICE A. CODICE SORGENTE
this->Name.push_back(tokens[0]);
}
if(this->ConvertDate((char*)tokens[1].c_str()) ==
EXIT_FAILURE){
return EXIT_FAILURE;
}
this->Partofday = tokens[2];
this->Activity = tokens[3];
this->Location = tokens[4];
this->Vehicle = str_decode (tokens[5], ";");
if(Vehicle.size()==0){
this->Vehicle.push_back(tokens[5]);
}
this->Nota = tokens[6];
return EXIT_SUCCESS;
}
else if (Operator == "R") {
for(int i=0;i<tokens.size();i++){
vector<string> decode = str_decode(tokens[i], ";");
this->UncoveredActivities.insert(std::end(UncoveredActivities),
std::begin(decode),
std::end(decode));
}
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
32
APPENDICE A. CODICE SORGENTE
A.3 Configuration.cpp
#include "Configuration.h"
#include "Periph/PrinterModule.h"
using namespace std;
using namespace libconfig;
void Configuration::SettingNetwork(string networktype) {
//Funzione che si occupa di impostare le modifiche ai parametri di
rete basandosi sul file di configurazione
//Il programma genera una stringa costituente un apposito comando
AWK
//Il comando viene poi eseguito sfruttando i privilegi di
amministratore di questo programma
bool dhcp;
std::string Address;
std::string Gateway;
std::string Netmask;
std::string Network;
std::string dnsVal;
try {
/*Funzionamento script awk
awk -f /path/to/changeInterfaces.awk <interfaces file>
dev=<eth device> n
* [address=<ip addr>] [gateway=<ip addr>] [netmask=<ip
addr>] n
* [network=<ip addr>] [mode=dhcp|static] [dns=<ip addr>]
*/
std::string cmd = "awk -f /path/to/changeInterface.awk";
//posizione dello script sul dispositivo
cmd = cmd + " /etc/network/interfaces ";
cmd = cmd + "dev=eth0-" + networktype;
const Setting& root = cfg.getRoot();
const Setting& param =
root["NetworkParam"][networktype.c_str()];
//Connessione tramite DHCP
try {
dhcp = param.lookup("dhcp");
if (dhcp == true) {
cmd = cmd + " " + "mode=dhcp";
cmd = cmd + " >> /etc/network/interfaces.tmp && mv
/etc/network/interfaces.tmp
/etc/network/interfaces";
33
APPENDICE A. CODICE SORGENTE
system(cmd.c_str());
return;
}
} catch (const SettingNotFoundException &nfex) {
}
//Connessione statica
try {
param.lookupValue("address", Address);
cmd = cmd + " address=" + Address;
if (networktype == "work") {
this->WorkIPAddress = Address;
}
} catch (const SettingNotFoundException &nfex) {
}
try {
param.lookupValue("gateway", Gateway);
cmd = cmd + " gateway=" + Gateway;
} catch (const SettingNotFoundException &nfex) {
}
try {
param.lookupValue("netmask", Netmask);
cmd = cmd + " netmask=" + Netmask;
} catch (const SettingNotFoundException &nfex) {
}
try {
param.lookupValue("network", Network);
cmd = cmd + " network=" + Network;
} catch (const SettingNotFoundException &nfex) {
}
cmd = cmd + " " + "mode=static";
try {
param.lookupValue("dnsVal", dnsVal);
cmd = cmd + " dns=" + dnsVal;
} catch (const SettingNotFoundException &nfex) {
}
cmd = cmd + " >> /etc/network/interfaces.tmp && mv
/etc/network/interfaces.tmp /etc/network/interfaces";
system(cmd.c_str());
} catch (SettingNotFoundException &nfex) {
}
34
APPENDICE A. CODICE SORGENTE
}
string EncryptDecrypt(string source) {
//Cifratura XOR. La chiave contenuta nell’array key[], qui omesso
string output = source;
for (int i = 0; i < source.size(); i++) {
output[i] = source[i] ^ key[i % (sizeof (key) / sizeof
(char))];
}
return output;
}
string GetLocalSerial() {
char line[256];
FILE *mac;
mac = fopen("/sys/class/net/eth0/address", "r");
if (mac == NULL) {
throw "Rete non configurata correttamente";
}
fgets(line, 256, mac);
fclose(mac);
string result = line;
return EncryptDecrypt(result);
}
string GetLicenceSerial() {
//Verifica la presenza di una licenza e ne ottiene il codice.
FILE *licenceFile;
char line[256];
char code[256];
licenceFile = fopen("/boot/EliLicence.txt", "r");
if (licenceFile == NULL) {
throw "File di Licenza mancante";
}
else {
while (fgets(line, 256, licenceFile)) {
if (strncmp(line, "License Code", 12) == 0) {
strcpy(code, strchr(line, ’:’) + 2);
}
}
}
fclose(licenceFile);
string LicenseID = code;
return LicenseID;
}
int Configuration::CheckLicence() {
//Controllo della licenza
time_t rawtime;
35
APPENDICE A. CODICE SORGENTE
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
ofstream resfile;
//Crea un log nella partizione FTP contenente lo stato della
licenza
resfile.open("/var/ftp/configuser/LicenceResult.txt",
std::ofstream::app);
try {
this->LicenseID = GetLicenceSerial();
} catch (const char* msg) {
//File di licenza mancante
resfile << asctime(timeinfo) << ": " << msg << std::endl;
resfile.close();
throw msg;
return EXIT_FAILURE;
}
(...) //Calcolo della licenza RpiID tramite i parametri di
sistema
if (RpiID.compare(this->LicenseID) != 0) {
resfile << asctime(timeinfo) << ": Codice di licenza non
corrispondenten";
resfile.close();
std::cerr << "Codice di licenza non corrispondente" <<
std::endl;
throw "Codice di licenza non corrispondente";
return EXIT_FAILURE;
}
resfile << asctime(timeinfo) << ": Controllo licenza superato
correttamenten";
resfile.close();
ofstream licfile;
licfile.open("/var/ftp/configuser/EliLicence.txt");
licfile << this->LicenseID;
licfile.close();
return EXIT_SUCCESS;
}
int Configuration::LoadConfiguration() {
//Tentativo di lettura del file di configurazione.
try {
cfg.readFile("/path/to/configuration.cfg"); //posizione
prevista del file
} catch (const FileIOException &fioex) {
std::cerr << "MISSING CONFIGURATION FILE" << std::endl;
36
APPENDICE A. CODICE SORGENTE
return (EXIT_FAILURE);
} catch (const ParseException &pex) {
std::cerr << "Parse error at " << pex.getFile() << ":" <<
pex.getLine()
<< " - " << pex.getError() << std::endl;
return (EXIT_FAILURE);
}
//accesso ai parametri
const Setting& root = cfg.getRoot();
const Setting& param = root["NetworkParam"];
this->version = VERSION;
try {
cfg.lookupValue("authWebServiceURL",
this->authWebServiceURL);
} catch (const SettingNotFoundException &nfex) {
cerr << "No authWebServiceURL setting in configuration
file." << endl;
}
//impostazione configurazioni di rete
try {
SettingNetwork("conf");
SettingNetwork("work");
} catch (SettingNotFoundException &nfex) {
}
return 0;
}
void Configuration::SwitchConnection() {
//Passa dalla connessione di Configurazione a quella Operativa
//In caso di fallimento del controllo di licenza, questo cambio
non avviene e
//il dispositivo rimane connesso alla connessione di
configurazione
std::cout << "Turning OFF Configuration Connection" <<
std::endl;
system("ifdown eth0=eth0-conf");
std::cout << "Turning ON Service Connection" << std::endl;
system("ifup eth0=eth0-work");
}
void Configuration::Dump() {
//Stampa che viene effettuata all’avvio del programma, per
segnalare all’utente che il sistema operativo
std::string localIPAddress = GetLocalIPAddress();
PrinterModule module;
37
APPENDICE A. CODICE SORGENTE
module.StartingPrint(this->version, this->MACAddress,
localIPAddress);
}
A.4 Utilizzo Tiny Xml
Il parsing del testo XML ricevuto in input è gestito da un modulo inserito nel
WebClient sviluppato per il dispositivo. Esistono due possibili stringhe che
risultano di interesse. La prima è delimitata dai tag <string>< /string>,
mentre la seconda è una stringa inserita fra i tag <lavoro>< /lavoro>, che
viene inserita come figlio dell’elemento <string>.
(...)
vector<string> eliWebClient::ParseResultXML(std::string resultxml)
{
vector<string> result;
TiXmlDocument doc;
doc.Parse(resultxml.c_str(), 0, TIXML_ENCODING_UTF8);
//Determinazione dell’elemento "padre"
//father -> <string>..</string>
TiXmlElement* father = doc.FirstChildElement("string");
//Determinazioni dei "figli"
//element -> <string><lavoro>...</lavoro></string>
TiXmlNode* firstChildString = doc.FirstChild("string");
TiXmlElement* element;
if (firstChildString) {
element = firstChildString->FirstChildElement();
//<string><lavoro>...</lavoro></string>
if (element)
{
for(element; element;
element=element->NextSiblingElement()){
result.push_back(element->GetText());
}
return result;
}
}
if (father){
result.push_back(father->GetText());
38
APPENDICE A. CODICE SORGENTE
return result;
}
return result;
}
39
Bibliografia
[1] J. Kuan. (2015) Script awk per modificare il file /etc/net-
work/interfaces. [Online]. Available: https://github.com/JoeKuan/
Network-Interfaces-Script
[2] (2016) Dati tecnici raspberry pi 2 model b. [Online]. Available:
http://elinux.org/RPi_Hardware
[3] RaspberryPiFoundation. (2016) Documentazione ufficiale raspberry pi.
[Online]. Available: https://www.raspberrypi.org/documentation/
[4] W3C. (2016) Web server architecture. [Online]. Available: https:
//www.w3.org/TR/ws-arch/
[5] Cairographics. [Online]. Available: https://www.cairographics.org/
[6] Pango. [Online]. Available: https://developer.gnome.org/pango/stable/
[7] Libconfig++. [Online]. Available: http://www.hyperrealm.com/
libconfig/
40

More Related Content

Similar to Progettazione e sviluppo di un software applicativo su un single board computer

Digitalizzazione di un processo industriale
Digitalizzazione di un processo industrialeDigitalizzazione di un processo industriale
Digitalizzazione di un processo industrialeGiulioDeBiasio2
 
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...Estrazione automatica di informazioni da documenti cartacei: progetto e reali...
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...Luca Bressan
 
v2 Presentazione Lelli
v2 Presentazione Lelliv2 Presentazione Lelli
v2 Presentazione LelliMatteo Lelli
 
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Idriss Riouak
 
Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Andrea Marchetti
 
Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Andrea Marchetti
 
Caratterizzazione dei sistemi cloud per la Pubblica Amministrazione
Caratterizzazione dei sistemi cloud per la Pubblica AmministrazioneCaratterizzazione dei sistemi cloud per la Pubblica Amministrazione
Caratterizzazione dei sistemi cloud per la Pubblica AmministrazioneAmmLibera AL
 
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open source
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open sourceLinux Day 2014 - Napoli - Programma Il Futuro: una scelta open source
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open sourceMario Rossano
 
Programma il futuro: una scelta open source
Programma il futuro: una scelta open sourceProgramma il futuro: una scelta open source
Programma il futuro: una scelta open sourceMarco Ferrigno
 
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...Alessandro Umek
 
Accesso remoto al proprio computer in una rete eterogenea
Accesso remoto al proprio computer in una rete eterogeneaAccesso remoto al proprio computer in una rete eterogenea
Accesso remoto al proprio computer in una rete eterogeneaGiacomo Antonino Fazio
 
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...MassimoPalmisano
 
Paper presentazione social media
Paper presentazione social mediaPaper presentazione social media
Paper presentazione social mediaalessioemireni
 
Il web service e i sistemi embedded - Tesi - cap2
Il web service e i sistemi embedded - Tesi - cap2Il web service e i sistemi embedded - Tesi - cap2
Il web service e i sistemi embedded - Tesi - cap2pma77
 
Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012L Dr
 
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...diegohusu
 
SugarCRM Enterprise Development Virtual Appliance
SugarCRM Enterprise Development Virtual ApplianceSugarCRM Enterprise Development Virtual Appliance
SugarCRM Enterprise Development Virtual ApplianceAntonio Musarra
 
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...LeonardoIurada
 
Monitoraggio di mac address in lan
Monitoraggio di mac address in lanMonitoraggio di mac address in lan
Monitoraggio di mac address in lanCe.Se.N.A. Security
 

Similar to Progettazione e sviluppo di un software applicativo su un single board computer (20)

Digitalizzazione di un processo industriale
Digitalizzazione di un processo industrialeDigitalizzazione di un processo industriale
Digitalizzazione di un processo industriale
 
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...Estrazione automatica di informazioni da documenti cartacei: progetto e reali...
Estrazione automatica di informazioni da documenti cartacei: progetto e reali...
 
v2 Presentazione Lelli
v2 Presentazione Lelliv2 Presentazione Lelli
v2 Presentazione Lelli
 
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
Uno studio sull'efficacia di checker automatici per la modernizzazione di cod...
 
Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.
 
Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.Set up and management of an integrated information system on Linux.
Set up and management of an integrated information system on Linux.
 
Caratterizzazione dei sistemi cloud per la Pubblica Amministrazione
Caratterizzazione dei sistemi cloud per la Pubblica AmministrazioneCaratterizzazione dei sistemi cloud per la Pubblica Amministrazione
Caratterizzazione dei sistemi cloud per la Pubblica Amministrazione
 
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open source
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open sourceLinux Day 2014 - Napoli - Programma Il Futuro: una scelta open source
Linux Day 2014 - Napoli - Programma Il Futuro: una scelta open source
 
Programma il futuro: una scelta open source
Programma il futuro: una scelta open sourceProgramma il futuro: una scelta open source
Programma il futuro: una scelta open source
 
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...
Integrazione e sviluppo di una piattaforma per la gestione delle conformità a...
 
MODULO 1-Informatica di Base
MODULO 1-Informatica di BaseMODULO 1-Informatica di Base
MODULO 1-Informatica di Base
 
Accesso remoto al proprio computer in una rete eterogenea
Accesso remoto al proprio computer in una rete eterogeneaAccesso remoto al proprio computer in una rete eterogenea
Accesso remoto al proprio computer in una rete eterogenea
 
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...
Prototipazione di una piattaforma di controllo degli accessi fisici cross ven...
 
Paper presentazione social media
Paper presentazione social mediaPaper presentazione social media
Paper presentazione social media
 
Il web service e i sistemi embedded - Tesi - cap2
Il web service e i sistemi embedded - Tesi - cap2Il web service e i sistemi embedded - Tesi - cap2
Il web service e i sistemi embedded - Tesi - cap2
 
Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012Supsi dti abstract_informatica_2012
Supsi dti abstract_informatica_2012
 
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...
Progetto e realizzazione di un'applicazione WebGIS per la visualizzazione di ...
 
SugarCRM Enterprise Development Virtual Appliance
SugarCRM Enterprise Development Virtual ApplianceSugarCRM Enterprise Development Virtual Appliance
SugarCRM Enterprise Development Virtual Appliance
 
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
 
Monitoraggio di mac address in lan
Monitoraggio di mac address in lanMonitoraggio di mac address in lan
Monitoraggio di mac address in lan
 

Recently uploaded

Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO Simone
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO SimoneGiornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO Simone
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO SimoneServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI Daniele
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI DanieleGiornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI Daniele
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI DanieleServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI Giovanni
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI GiovanniGiornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI Giovanni
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI GiovanniServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO Antonio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO AntonioGiornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO Antonio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO AntonioServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA Giorgio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA GiorgioGiornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA Giorgio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA GiorgioServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' Davide
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' DavideGiornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' Davide
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' DavideServizi a rete
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO Andrea
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO AndreaGiornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO Andrea
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO AndreaServizi a rete
 

Recently uploaded (7)

Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO Simone
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO SimoneGiornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO Simone
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DI DOMENICO Simone
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI Daniele
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI DanieleGiornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI Daniele
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | RENZI Daniele
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI Giovanni
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI GiovanniGiornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI Giovanni
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | CADEI Giovanni
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO Antonio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO AntonioGiornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO Antonio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | DISCIPIO Antonio
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA Giorgio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA GiorgioGiornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA Giorgio
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | SERRA Giorgio
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' Davide
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' DavideGiornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' Davide
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ROMANO' Davide
 
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO Andrea
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO AndreaGiornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO Andrea
Giornata Tecnica da Piave Servizi, 11 aprile 2024 | ALBIERO Andrea
 

Progettazione e sviluppo di un software applicativo su un single board computer

  • 1. UNIVERSITÀ DEGLI STUDI DI TRIESTE Dipartimento di Ingegneria e Architettura Laurea Triennale in Ingegneria dell’Informazione Progettazione e sviluppo di un software applicativo su single-board computer 2 dicembre 2016 Laureando Relatore Alessandro Mascherin Chiar.mo Prof. Sergio Carrato Correlatore Ing. Piergiorgio Menia Anno Accademico 2016/2017
  • 2. Indice Introduzione ii Obiettivi della tesi . . . . . . . . . . . . . . . . . . . . . . . . . . . iii Organizzazione della tesi . . . . . . . . . . . . . . . . . . . . . . . . iii Strumenti utilizzati . . . . . . . . . . . . . . . . . . . . . . . . . . . iii 1 Analisi Introduttiva 1 1.1 Descrizione problema . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Scelta dell’ambiente di lavoro . . . . . . . . . . . . . . . . . . 3 1.3 Descrizione dell’hardware . . . . . . . . . . . . . . . . . . . . 3 1.3.1 Interfaccia e protocollo Wiegand . . . . . . . . . . . . 5 1.4 Scelta del sistema operativo . . . . . . . . . . . . . . . . . . . 6 2 Impostazione iniziale dispositivo 7 2.1 Installazione librerie . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Configurazioni di rete . . . . . . . . . . . . . . . . . . . . . . 9 2.3 Partizioni del sistema operativo . . . . . . . . . . . . . . . . . 10 3 Sviluppo Software applicativo 13 3.1 Descrizione generale . . . . . . . . . . . . . . . . . . . . . . . 13 3.2 Modulo di stampa . . . . . . . . . . . . . . . . . . . . . . . . 14 3.3 Modulo di analisi e gestione dell’input . . . . . . . . . . . . . 17 3.4 Modulo di configurazione . . . . . . . . . . . . . . . . . . . . 18 4 Automatizzazione del dispositivo 21 4.1 Script per la gestione del software applicativo . . . . . . . . . 21 4.2 Accesso via FTP e SSH e permessi utente . . . . . . . . . . . 22 Conclusioni 24 A Codice Sorgente 26 A.1 PrinterModule.cpp . . . . . . . . . . . . . . . . . . . . . . . . 26 A.2 InputParser.cpp . . . . . . . . . . . . . . . . . . . . . . . . . . 30 A.3 Configuration.cpp . . . . . . . . . . . . . . . . . . . . . . . . . 33 A.4 Utilizzo Tiny Xml . . . . . . . . . . . . . . . . . . . . . . . . 38 i
  • 3. Introduzione La seguente tesi descriverà il lavoro svolto presso la Elimos S.r.l per il pro- getto e lo sviluppo di un software applicativo implementato su single-board computer. Il progetto prevede l’installazione di numerose unità distribuite in rete in grado di leggere un Tag RFID, richiedere ad un server centrale connesso in rete TCP/IP un set di informazioni legate al Tag in oggetto, e stampare su stampante termica i dati ricevuti dal centro di controllo. Que- sto sistema deve essere piccolo, inscatolato per ambiente industriale, ed in grado di essere acceso/spento senza controllo di un operatore. A fronte del- l’analisi dei requisiti e dei target di costo richiesti da un’azienda committente esterna, si è deciso di utilizzare come unità centrale di elaborazione per ogni modulo un single-board computer (SBC) embedded Raspberry Pi 2 model B. Il lavoro si è sviluppato in più fasi. Nella prima, ci si è dedicati alla scelta dell’ambiente di lavoro, andando a definire la totalità dell’hardware su cui si sarebbe andati a lavorare e gli strumenti software a disposizione. Nella seconda fase si è andati a sviluppare un software applicativo in gra- do di soddisfare le richieste del committente. Si è cercato di sviluppare un programma il più modulare possibile, per far fronte a diversi casistiche e per garantire longevità al progetto. In fase di sviluppo si è lavorato paral- lelamente sul codice applicativo e sulla parte piu’ sistemistica propria del Raspberry, installando driver e pacchetti, e creando le librerie software ne- cessarie al funzionamento del programma, il tutto per ottenere un sistema operativo configurato in maniera opportuna. Nella fase finale del lavoro si è andati a integrare l’applicativo sull’hardware testando completamente tutta la connettività e ricercando delle soluzioni per rendere il dispositivo in grado di operare in maniera autonoma senza operatore. In questa fase sono anche stati sviluppati dei tool per permettere un accesso remoto alla SBC e special- mente le procedure di aggiornamento e configurazione remota direttamente sul campo. Nei capitoli seguenti andremo ad esporre le soluzioni sviluppate, descri- vendo le problematiche riscontrate in fase di sviluppo e le scelte effettuate per soddisfare i requisiti di progetto. ii
  • 4. INTRODUZIONE Obiettivi della tesi L’obiettivo della tesi è quello di descrivere le operazioni da svolgere per utilizzare un single-board computer ARM-based in ambiente non presidiato, a partire dall’impostazione iniziale dell’ambiente di sviluppo per giungere all’implementazione di un programma di una certa complessità sul sistema oggetto di analisi. Uno dei requisiti cardine sui cui si è sviluppato il progetto è stato quello di ottenere un sistema di semplice utilizzo per l’utente finale, privo di interfacce per l’operatore. Il sistema deve essere interfacciato ad un lettore di tessere RFID, ad una stampante termica e deve comunicare attraverso la rete TCP/IP. Si è quindi lavorato per rendere il dispositivo in grado di lavorare in maniera autonoma, gestendo eventuali configurazioni e aggiornamenti in maniera remota. Organizzazione della tesi Nel capitolo 1 verrà effettuata un’analisi dei requisiti richiesti dal committen- te e dei motivi che hanno portato alla scelta del Raspberry Pi come ambiente di lavoro per la realizzazione del progetto. Verranno inoltre discusse alcune possibili scelte relative ai sistemi operativi utilizzabili. Le varie configurazio- ni del sistema operativo scelte e le principali librerie software installate sono descritte nel capitolo 2. Nel capitolo 3 viene esposto il software applicativo sviluppato, sia nel suo funzionamento generale che negli specifici moduli svi- luppati dal laureando. Il capitolo 4 è dedicato alla descrizione degli strumenti utilizzati per garantire un funzionamento autonomo del Raspberry, fornendo nel contempo degli strumenti per l’aggiornamento e la configurazione remota all’utente. Infine il capitolo 5 è dedicato alle conclusioni sul lavoro svolto, mentre in appendice sono inseriti la bibliografia e parte del codice sorgente opportunamente commentato. Strumenti utilizzati L’hardware su cui si è andati a sviluppare il progetto è stato il Raspberry Pi 2 model B, un computer single-board rilasciato sul mercato nel febbraio 2015 dalla RaspberryPi Foundation™. Il Raspberry Pi è un computer co- siddetto single-board, in quanto costruito su un’unica scheda circuitale, con processore ARM in grado di supportare una vasta gamma di sistemi operativi GNU/Linux. Il software sviluppato è stato scritto in C/C++ 11 sfruttando l’ambiente di sviluppo integrato NetBeans. Sono stati inoltre scritti diversi script bash e per alcune opzioni di configurazione vengono utilizzati degli script AWK open-source. iii
  • 5. Capitolo 1 Analisi Introduttiva In questa sezione verranno descritte le specifiche richieste al progetto e le scelte iniziali fatte per l’implementazione del dispositivo. 1.1 Descrizione problema Le specifiche di progetto prevedevano la realizzazione di un sistema senza in- terfaccia utente in grado di implementare diverse funzionalità che verranno di seguito esposte. A livello globale il sistema finale si presenta essenzial- mente come un timbratore che deve fornire a ogni operatore un ruolino di servizio con le attività prevista per la giornata lavorativa. Il dispositivo fina- le necessita di una connessione con un server centrale, installato in sede del committente, che contiene i dati relativi agli operatori e alle attività previste. Per quanto riguarda la connettività, il dispositivo finale deve quindi potersi collegare alla rete Internet tramite protocollo TCP/IP, attraverso la tecno- logia Ehternet. La comunicazione con il server centrale avviene attraverso un web-service. Altro aspetto fondamentale dei requisiti è la gestione del sistema di input/output. Per poter stampare il ruolino di servizio, il dispo- sitivo deve poter comunicare con una stampante termica collegabile tramite l’interfaccia USB, il cui modello è stato stabilito dal committente. Il sistema deve gestire un processo di analisi dei pin in input ricevendo dei dati da un lettore di tessere magnetiche, collegato ai GPIO del Raspberry. Questo letto- re utilizza l’interfaccia Wiegand per la trasmissione dei dati e deve pertanto essere opportunamente collegato al dispositivo su cui si andrà a sviluppare il progetto. Tramite lo stesso lettore è necessario fornire all’utente un feedback sonoro e visivo delle operazioni che vengono svolte; il feedback viene invia- to grazie al led e al buzzer presenti sul lettore di tessere. Questo sistema deve analizzare la sequenza di bit che riceve dal lettore di tessere e, in se- guito ad un’opportuna elaborazione, inviare una richiesta al server centrale con i dati elaborati. Per comunicare con il server è stato necessario quindi implementare un client web-service sul dispositivo, per poter effettuare una 1
  • 6. CAPITOLO 1. ANALISI INTRODUTTIVA convalida dei dati elaborati dal badge e ricevere delle opportune risposte dal server. L’utilizzo dei web service è lo standard de facto per la comunica- zione attraverso il Web di diversi dispositivi e diverse applicazioni software. Secondo la definizione fornita dal W3C, il World Wide Web Consortium, le web service sono la tecnologia standard per poter mettere in comunicazione diversi applicazioni software installate su una grande varietà di piattaforme e framework. Nel caso in analisi lo sviluppo di una web service permette di poter ottenere una facile comunicazione con il server centrale, senza doversi preoccupare della compatibilità fra diverse piattaforme e sistemi operativi. Sono previste diverse tipologie di risposta da parte del web server, e il sistema deve adottare un opportuno comportamento per ogni possibile risposta: • Codice di errore in caso di tessera sconosciuta • Codice di errore differente nel caso di tessera conosciuta, ma rispo- sta di errore da parte del web server (data/ora non valida, mancata timbratura, o altro) • In caso di risposta positiva da parte del web server è necessaria un’e- laborazione dei dati ricevuti e la preparazione di un modulo di stampa da inviare alla stampante termica collegata al sistema. Durante l’elaborazione dei dati per la stampa o in presenza di un codice di errore, deve essere previsto uno specifico feedback per l’utente tramite il led e il sistema sonoro implementato nel lettore di tessere. In assenza di erro- ri, i dati ricevuti devono venire stampati in dei ruolini di servizio con diversi formati; è necessario quindi sviluppare un programma in grado di distinguere le varie casistiche e di creare dei layout di stampa adeguati, in modo tale da fornire ad ogni diverso operatore il suo specifico ruolino di servizio. Il tutto, dovendo operare senza interfaccia utente, deve inoltre disporre di un sistema per l’aggiornamento e la configurazione in modo remoto, senza la necessità di accedere fisicamente ai dispositivi finali. Per queste procedure è stato deciso di utilizzare i protocolli FTP e SSH per accedere al sistema. L’ag- giornamento e la configurazione devono avvenire stabilendo una connessione ad un indirizzo IP specifico, che deve essere attivo per un certo intervallo temporale dopo il quale il sistema si deve connettere a un indirizzo IP di servizio. Un ulteriore requisito è la robustezza sia del software applicativo, sia del dispositivo in caso di malfunzionamenti. In particolare, non essen- doci interfaccia utente, bisogna prevenire errori di memoria in seguito allo spegnimento della scheda o in caso di problemi legati all’alimentazione. 2
  • 7. CAPITOLO 1. ANALISI INTRODUTTIVA 1.2 Scelta dell’ambiente di lavoro La prima decisione è stata quella di non utilizzare dei semplici microcontrol- lori, in quanto non in grado di rispettare le specifiche fornite in modo sem- plice ed economico. Per poter soddisfare in modo efficace i requisiti imposti dal committente è necessario avere a disposizione un dispositivo che deve offrire le funzionalità di un personal computer, mantenendo nel contempo delle dimensioni contenute. Si è scelto quindi di sfruttare una single-board computer embedded, dispositivi molto compatti e dal costo contenuto, in grado di fornire prestazioni comunque elevate. Sono dei computer completi che vengono installati su una singola scheda elettronica. Questi single-board computer hanno avuto un’ampia diffusione negli ultimi anni per utilizzi edu- cazionali o come controllori embedded. Pur non potendo competere con la potenza di calcolo di un personal computer di fascia alta, hanno come van- taggio le dimensioni estremamente contenute, un basso consumo e un costo notevolmente inferiore. L’hardware preso in considerazione consisteva nel Raspberry Pi 2 model B e nell’Odroid-C1plus. Sono schede molto simili in caratteristiche e prestazioni, e sono quasi equivalenti quando utilizzati per la realizzazione di questo tipo di progetti. Entrambi i prodotti sono basati su processori ARM, hanno dimensioni comparabili e sono dotati delle interfacce I/O richieste. L’Odroid è superiore al Raspberry in termini di caratteristi- che hardware, mentre è leggermente inferiore in termini di supporto tecnico e di reperibilità dei dispositivi. Essendo il costo dei due prodotti essenzial- mente identico e non essendo necessarie particolari prestazioni hardware per l’implementazione delle richieste del committente, si è deciso di iniziare lo sviluppo sul Raspberry Pi, data la facile reperibilità del prodotto e il maggior supporto tecnico presente in rete. 1.3 Descrizione dell’hardware Figura 1.1: Foto del chip BCM2836 3
  • 8. CAPITOLO 1. ANALISI INTRODUTTIVA Il Raspberry Pi 2 model B è basato su un processore quad-core ARM Cortex-A7 da 900MHz costruito dalla Broadcom™. In particolare il chip uti- lizzato è il BCM2836, che va a sostituire il precedente BCM2835, installato sulle versioni precedenti del RaspberryPi. Questo modello di Raspberry dispone inoltre di 1 GB di memoria RAM, e di una GPU, prodotta anch’essa dalla Broadcom™, da 250 MHz con OpenGL ES 2.0 OpenVG 1080p30 H.264. Per quanto riguarda la connettività la scheda dispone di 4 porte USB 2.0, utilizzando il Microchip™ LAN9514, una porta Ethernet, una porta HDMI e un jack audio da 3,5 mm che comprende anche il video composito utilizzabile con un adattatore a 4 poli. Per la memorizzazione dei dati è necessario l’utilizzo di una scheda SD, collegabile al Raspberry tramite un apposito slot. Le periferiche di basso livello installate sulla scheda consistono in un modulo GPIO da 40 pin, di un Serial Peripheral Interface Bus (SPI), di pins per le comunicazioni I2C e I2S, e un Universal asynchronous receiver/transmitter (UART). La scheda per il funzionamento richiede 5V in DC, che possono essere forniti tramite una porta Micro USB di tipo B. La suddivisione dei pin dell’interfaccia GPIO è illustrata nell’immagine 1.2: Figura 1.2: Schema GPIO I pins 2 e 4 sono pin utilizzati per alimentare dispositivi collegati all’in- terfaccia a una tensione di 5V e con una corrente che può arrivare a 1,5A. I pin da 5V possono essere anche utilizzati per alimentare il Raspberry stesso, prestando attenzioni ad eventuali picchi di tensione che potrebbero danneg- giare i pins del bus GPIO. In modo analogo i pin 1 e 17 forniscono una tensione di 3.3V ma con una limitazione alla corrente fornita. Esistono poi dei pin deputati a specifici metodi di input/output. Ad esempio i pins 3, 5, 27 e 28 vengono utilizzati per il sistema di comunicazione I2C mentre i pin 8 e 10 vengono utilizzati per le comunicazioni UART (Universal Asynchronous Receiver/Transmitter). 4
  • 9. CAPITOLO 1. ANALISI INTRODUTTIVA L’immagine 1.3 raffigura il mechanical drawing del modello di Raspberry utilizzato. Il drawing che viene presentato è quello del Raspberry Pi Model B+, ma risulta valido anche per il Raspberry Pi 2 Model B. Le misure sono espresse in mm. Figura 1.3: Mechanical Drawing 1.3.1 Interfaccia e protocollo Wiegand Un’altra componente hardware fondamentale del sistema oggetto di analisi è costituita dal lettore di tessere magnetiche. Questo lettore utilizza un in- terfaccia Wiegand per la trasmissione dei dati al Raspberry. L’interfaccia Wiegand è costituita da 3 linee che devono essere collegate al bus GPIO del Raspberry. Una linea costituisce la massa e le altre due linee vengono utilizzate per la trasmissione dei dati e di solito vengono chiamate DATA0 e DATA1. In assenza di dati da trasmettere le due linee vengono mantenute ad un livello di tensione “alto” che generalmente consiste in 5 V in DC. Nel caso in cui viene trasmesso un bit di valore 0, la tensione della linea DATA0 viene abbassata per una specifica durata temporale. Viceversa, nel caso in cui viene trasmesso un bit con valore 1 è la tensione della linea DATA1 ad essere abbassata, mentre DATA0 rimarrà su un valore di tensione alto. Le tempistiche di questi segnali sono tipicamente 50-100 µs per gli impulsi in- tervallati da 2ms, ma non sono assolutamente stringenti e possono variare anche in percentuali significative. Questi bit vengono inviati secondo il for- 5
  • 10. CAPITOLO 1. ANALISI INTRODUTTIVA mato stabilito dal protocollo Wiegand standard da 26 bit. Questo formato consiste in uno stream di 26 bit che sono così suddivisi: Figura 1.4: Protocollo Wiegand Il bit di testa e di coda dello stream vengono utilizzati come controllori di parità. In particolare il primo bit è settato in modo tale che il numero di “1” nei primi 13 bit dello stream sia pari, mentre l’ultimo viene impostato per far sì che il numero di “1” negli ultimi 13 bit dello stream sia dispari. Il facility code contiene le informazioni relative al modello e al produttore della scheda. L’user code, chiamato anche ID code, come viene suggerito dal nome, codifica un valore identificativo della singola tessera. Per l’analisi di questi bit è stato sviluppato un modulo software che si occupa di eseguire il controllo di parità della sequenza trasmessa e di separare il facility code e l’ID code. 1.4 Scelta del sistema operativo Sui single-board computer ARM-based è possibile installare una gran varie- tà di sistemi operativi, generalmente linux-based. Nel caso del Raspberry i sistemi operativi supportati ufficialmente sono Rasbian e Noobs, maggior- mente adatti per un utilizzo di tipo didattico o dimostrativo della scheda. Esistono tuttavia alcuni sistemi operativi che non sono sviluppati dalla Ra- spberryPi Foundation™ ma che sono comunque ottimizzati per il Raspberry, quali Ubuntu Mate, Windows 10 IOT, Snappy Ubuntu e altri. Si è deciso di installare una versione di Ubuntu Mate, in quanto più adatta alle speci- fiche di progetto rispetto ai sistemi sopra menzionati. Un’altra alternativa presa inizialmente in considerazione è stata quella di installare un sistema Android; l’ipotesi è stata scartata durante la prima fase di analisi in quanto il sistema non è ufficialmente supportato e risulta spesso instabile sul mo- dello di hardware scelto. L’implementazione di Android sarebbe stata una valida scelta utilizzando l’Odroid, che supporta nativamente questo sistema operativo. 6
  • 11. Capitolo 2 Impostazione iniziale dispositivo In questa sezione verrà descritto il lavoro svolto per l’impostazione generale della scheda a livello software e le soluzioni adottate per rendere il sistema robusto in seguito a eventi non previsti dalle specifiche, quali l’interruzione improvvisa dell’alimentazione o la rimozione della scheda di memoria. Ver- ranno introdotte e analizzate le librerie software installate, le configurazioni di rete adottate e un’importante modifica al sistema operativo, ovvero la creazione di differenti partizioni di sistema configurate in modo differente per risolvere diverse situazioni impreviste che potrebbero insorgere durante l’utilizzo del dispositivo. 2.1 Installazione librerie Nel corso del processo di sviluppo del software applicativo è stato necessario installare diverse librerie software disponibili in Internet. In ambiente linux sono disponibili un ampio numero di librerie, la maggior parte delle quali gratis e open-source. L’installazione, l’aggiornamento e la rimozione di li- brerie e pacchetti software è gestita di default da APT (Advanced Packaging Tool), software che mette a disposizione vari comandi che facilitano queste operazioni, come apt-get. Tutte le librerie analizzate in seguito sono state installate utilizzando il comando apt-get. Le principali librerie che sono sta- te installate e configurate per lo sviluppo del software applicativo sono le seguenti: • le librerie grafiche Pango e Cairographics utilizzate per la creazione del modulo di stampa • la libreria CUPS per la gestione della stampante termica • la libreria libconfig++, utilizzata per il modulo di configurazione 7
  • 12. CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO • ntpdate per l’aggiornamento di data e ora • TinyXML Cairographics è una libreria grafica 2D che viene utilizzata per la rea- lizzazione di grafica vettoriale in modo consistente su diversi dispositivi e sistemi operativi. E’ una libreria open-source sviluppata in linguaggio C, che offre numerosi bindings per altri linguaggi di programmazione. Pango è una libreria che viene utilizzata per la gestione del layout e del rendering di contenuto di tipo testuale. E’ integrata nativamente con Cairo ed è infatti raccomandata dalla documentazione di Cairographics per la gestione di testi. CUPS è un sistema di stampa open source sviluppato da Apple per siste- mi UNIX-like e macOS. Generalmente CUPS è installato di default in tutti i sistemi Ubuntu, ma per operazioni di controllo più avanzate è necessario installare la versione per sviluppatori della libreria. L’utilizzo di CUPS ci ha permesso di controllare la stampante termica con il programma sviluppato in C++ e di poter avere un maggior controllo delle opzioni di stampa, quali le dimensioni della superficie di stampa o la gestione del taglio dei fogli. Libconfig è una libreria per la gestione di file di configurazione strutturati. La libreria è scritta in C++, e prevede l’uso di file di configurazione scritti in un formato più semplice e leggibile dell’XML. E’ stata scelta questa libreria, tra le molte disponibili, per la semplicità nell’utilizzo e per la consolidata compatibilità con i sistemi Unix. Ntp è un protocollo basato su TCP/IP per l’aggiornamento dell’ora di un computer, il cui funzionamento è basato sulla comunicazione fra un client, in esecuzione sulla macchina di cui si vuole aggiornare l’ora, e dei server che forniscono l’ora corrente. Ubuntu dispone di due diversi strumenti per utilizzare ntp, chiamati ntpdate e ntpd. Nel sistema oggetto di analisi si è deciso di installare ntpdate, strumento che contatta i server ntp ad ogni avvio del sistema operativo per aggiornare l’ora del sistema. Questo strumento inoltre richiede meno utilizzo di risorse rispetto a ntpd. Tinyxml è un analizzatore di testo XML sviluppato in C++. Questa libreria fa dei suoi punti di forza la semplicità di utilizzo e un basso utilizzo di risorse di sistema per il suo funzionamento. E’ inoltre disponibile gra- tuitamente ed è open-source, caratteristiche che hanno quindi portato alla scelta di questa libreria rispetto ad altre disponibili sul mercato. 8
  • 13. CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO 2.2 Configurazioni di rete Di default il Raspberry adotta il protocollo DHCP per ottenere accesso alla rete IP al quale è stato connesso. Nel caso in esame è sorta la necessità di installare diversi dispositivi sulla stessa network, rimanendo in grado di individuare in modo univoco tramite l’indirizzo IP ogni scheda in quella data network. Questa identificazione univoca è necessaria solo per poter effettuare aggiornamenti e controlli mirati al singolo computer. E’ stato quindi deciso di impostare due diversi tipi di configurazione, i cui parametri verranno gestiti dal programma in esecuzione sulla macchina. Si è andati a creare due distinte interfacce di rete, una configurata in maniera statica e una seconda configurata in maniera dinamica tramite DHCP. Per implementare queste interfacce è stato necessario modificare il file /etc/network/interfaces contenente i parametri di accesso e configurazione ethernet. In questo file, di default è presente l’interfaccia di loopback, ovverosia il localhost. Inoltre, in presenza di un accesso ethernet il sistema operativo aggiunge in maniera autonoma un’interfaccia ethernet configurata tramite DHCP. Nel caso in analisi il file si presentava inizialmente così: auto lo auto MAC-Address iface lo inet loopback iface MAC-Address inet dhcp Nei dispositivi in uso il sistema operativo denomina di default l’interfaccia ethernet con l’indirizzo MAC della stessa (denominato MAC-Address nel file precedente). E’ stato necessario utilizzare il seguente script, scritto in bash, per modificare in modo permanente questa impostazione: #!/bin/bash mymac="$(ifconfig | grep ’HWaddr’ | cut -d -f11)" file="/etc/udev/rules.d/70-persistent-net.rules" echo $mymac if [ -f $file ] then echo "$file Found" else echo "$file Not Found" echo ’SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="’$mymac’", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0" ’>> $file fi Lo script va ad inserire una nuova regola di sistema nella quale si imposta il nome dell’interfaccia ethernet con eth0. Questa operazione è stata resa necessaria per avere un software uniforme e semplice su tutti i dispositivi che si andranno a configurare. 9
  • 14. CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO Effettuata questa modifica, si è proceduto a modificare il file /etc/networ- k/interfaces. L’interfaccia di loopback non è stata modificata, mentre sono state distinte due diverse interfacce ethernet, chiamate eth0-conf e eth0-work, la prima destinata a essere configurata tramite indirizzo statico, la seconda tramite DHCP. L’interfaccia eth0-conf è stata configurata con un indirizzo IP statico, appartenente alla network privata nella quale è installato il Rasp- berry. L’interfaccia eth0-work è stata impostata in modalità DHCP. Queste due interfacce sono modificabili dal software applicativo secondo necessità, basandosi su un opportuno file di configurazione. E’ stato inoltre imposta- to che all’avvio la scheda non deve connettersi con nessuna interfaccia, in quanto sarà uno specifico script bash che si occuperà di attivare la connes- sione di configurazione (eth0-conf), mentre il software applicativo effettuerà il cambio di interfaccia portandosi sull’interfaccia eth0-work. Il file /etc/network/interfaces modificato si presenta quindi così: auto lo iface lo inet loopback iface eth0-work inet dhcp iface eth0-conf inet static address some-IP network some-networknumber gateway some-gatewayIP I parametri di rete quali l’indirizzo IP, il network number e l’indirizzo di gateway vengono specificati nel file di configurazione. 2.3 Partizioni del sistema operativo Uno dei principali requisiti di lavoro consiste nell’ottenere un sistema in gra- do di operare senza interfaccia utente. Nel caso del sistema operativo preso in considerazione è stato riscontrato un bug che al momento della realizza- zione del progetto non era ancora diventato oggetto di analisi per il team di sviluppo di Ubuntu Mate. In seguito il problema non è stato riconosciuto come valido in quanto le build del sistema operativo in cui si verifica questo problema non sono state considerate come ufficiali. Il problema consiste nel fatto che, in caso di spegnimento del dispositivo in seguito all’interruzione dell’alimentazione, al reboot il sistema operativo entrerà sempre in una mo- dalità di emergenza, richiedendo un controllo ai file di sistema. Per uscire da questa modalità di emergenza è necessario inserire manualmente le creden- ziali di amministratore e riavviare nuovamente il sistema operativo. Tuttavia questa modalità blocca il boot del sistema operativo e impedisce l’esecuzione di qualsiasi altro processo che si vuole eseguire, andando pertanto a creare un incongruenza con le specifiche di progetto. Questo sistema di emergenza è stato implementato per verificare l’integrità del sistema in caso di spegni- 10
  • 15. CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO menti considerati anomali dal sistema operativo, andando però a inficiare il funzionamento automatizzato del Raspberry. Per risolvere questo problema si è deciso quindi di configurare il sistema in modo tale da prevenire errori in caso di spegnimento anomalo, evitando l’insorgere del problema descritto. La soluzione adottata è stata quella di impostare una parte del sistema operativo in modalità read-only, per prevenire errori in fase di scrittura nel file-system. Non è stato possibile impostare l’intero sistema operativo in modalità di sola lettura in quanto il software necessario per il funzionamento della stampante ha necessità di creare dei file temporanei in diverse sezioni del sistema operativo. Si è optato quindi per una configurazione in cui solo la directory contenente i file necessari all’avvio del sistema operativo è im- postata in modalità di sola lettura, in modo tale da prevenire il problema della modalità di emergenza descritto in precedenza. La tabella di configurazione dei file system è un file che contiene i dati relativi alle partizioni di sistema e alle sue proprietà, ed è nota come fstab e si trova all’interno della directory etc. La tabella è stata così impostata: file system mount-point type options dump pass proc /proc proc defaults 0 0 tmpfs /tmp tmpfs nodev, nosuid, 0 0 size=100 M, mode=1777 /dev/mmcblk0p2 / ext4 defaults,noatime 0 2 /dev/mmcblk0p1 /boot vfat defaults,ro 0 2 E’ stata allocata una partizione sulla RAM del dispositivo, in cui vengono destinati i processi di creazione di file temporanei generati dal software appli- cativo. In questa partizione verranno creati tutti i file temporanei necessari per il funzionamento del software applicativo che si è andati a sviluppare. Grazie a questa partizione, nel caso in cui avvenga una rimozione imprevista della scheda di memoria si previene la possibilità di andare a corrompere dei file essenziali per il funzionamento del software applicativo. L’unico impre- visto che può arrecare danni al sistema è l’interruzione dell’alimentazione o la rimozione della memoria durante le operazioni di aggiornamento software, eventualità che non può essere risolta sfruttando particolari partizioni della memoria. Questa problematica può essere però risolta utilizzando altri ac- corgimenti software, come ad esempio l’utilizzo di backup esterni e copie del programma principale in altre sezioni della scheda di memoria. La scheda SD è stata quindi divisa in due partizioni, la prima in formato ext4 che contiene la maggior parte del sistema operativo e una partizione in formato vfat in cui è stata allocata la sezione /boot. Per la partizione /dev/mmcblk0p2 è stata impostata l’opzione noatime per limitare le operazioni di scrittura sulla 11
  • 16. CAPITOLO 2. IMPOSTAZIONE INIZIALE DISPOSITIVO scheda di memoria. Linux generalmente associa ad ogni file degli attributi in cui vengono memorizzati le date di creazione del file, di ultimo accesso al file e di ultima modifica. Senza l’opzione noatime questi attributi ven- gono aggiornati per ogni accesso ad uno specifico file. Impostando noatime in una partizione quando il sistema operativo legge un determinato file di sistema non aggiorna più gli attributi dello stesso, limitando notevolmente le operazioni di scrittura, migliorando le performance del sistema operati- vo e riducendo la possibilità di incorrere in errori in fase di scrittura. La partizione /dev/mmcblk0p1 è stata impostata in modalità di sola lettura per ovviare al bug descritto all’inizio di questa sezione e per evitare errori in scrittura nella partizione. In questo modo tutto ciò che è necessario per l’avvio del sistema operativo viene protetto dalla corruzione dei dati in caso di interruzione dell’alimentazione. 12
  • 17. Capitolo 3 Sviluppo Software applicativo Tutti i moduli software descritti in questa sezione sono stati sviluppati dal laureando. Il programma è stato scritto in C++. 3.1 Descrizione generale Lo schema generale del sistema sviluppato è così composto: Il Raspberry è collegato ad un lettore di schede magnetiche, a un led e a un piezo monotonale tramite l’interfaccia GPIO. E’ inoltre collegato ad una stampante termica tramite USB e a internet tramite ethernet. Il software si occupa di gestire tutte le periferiche collegate al Raspberry, eseguendo le opportune elaborazioni richieste dal committente. Il software è stato svilup- pato in maniera modulare e i vari moduli software vengono attivati in caso di necessità. Il sistema si avvia quando si rileva un input dal lettore di tessere magnetiche. E’ presente quindi un modulo che esegue un loop in lettura dei 13
  • 18. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO dati provenienti dal lettore di schede connesso all’interfaccia GPIO. Quando avviene la ricezione dei dati verrà attivato un modulo per fornire un feedback audiovisivo all’utente che ha utilizzato il badge magnetico, e un secondo mo- dulo che si occupa di codificare i dati ricevuti per renderli compatibili con quelli attesi dal WebServer. Dal WebServer il sistema si attende tre possibili risposte: scheda valida e dei dati, scheda non valida e scheda non riconosciu- ta. Per ogni risposta è previsto un differente feedback audiovisivo. L’analisi dei dati ricevuti viene svolta dai moduli descritti nella sezione 3.3, moduli dedicati alla codifica e all’interpretazione dei dati. In caso di scheda valida, i dati ricevuti e codificati vengono ulteriormente elaborati per essere inviati in stampa alla stampante termica. Il modulo software che si occupa di questa operazione è descritto nella sezione 3.2. Il modulo di stampa fornisce dei feedback sullo stato della stampa e sulla presenza di eventuali errori nei dati al modulo che lo ha attivato; quest’ultimo modulo segnalerà all’utente in modo opportuno tramite l’interfaccia GPIO i feedback ricevuti. All’avvio inoltre il software attiva un modulo dedicato alla configurazione del sistema e all’aggiornamento dello stesso nel caso in cui siano presenti nuove opzioni di configurazione. 3.2 Modulo di stampa La stampa viene gestita da un modulo sviluppato in C++ chiamato Prin- terModule.cpp. Questo modulo fa uso delle librerie Cairographics, Pango e Cups descritte nella sezione 2.1. Pango e Cairo vengono utilizzate per generare un file in formato PostScript che viene poi inviato alla stampante termica tramite la libreria Cups. Le stampe generate hanno un layout simile a quello presentato nella seguente immagine: Figura 3.1: Esempio di layout di stampa desiderato Lo scopo di questo modulo software è quelli di posizionare le varie strin- ghe desiderate sul foglio, secondo uno specifico layout. Sono state previste le 14
  • 19. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO seguenti tipologie di stampa per soddisfare i requisiti forniti dal committente: • Una stampa effettuata all’avvio del sistema, per segnalare all’operatore che il programma è pronto per l’utilizzo; in questo messaggio si è deciso di inserire dati relativi alla versione del software e all’indirizzo di rete di lavoro al quale è stato configurato il sistema • A seconda dei dati ricevuti dal webserver è prevista una stampa deno- minata “responsabile” e una seconda stampa chiamata “operatore” Le stampe operatore e responsabile hanno layout e dati differenti, pertanto sono gestiti da due metodi differenti. Per generare un file PostScript con Cairo è necessario creare un oggetto denominato surface che funge da struttura di base del file finale. Questa surface è inizializzata specificando la tipologia di file che si vuole andare a creare, in questo caso PostScript. Le dimensioni del file PostScript sono espresse in punti tipografici. Questa unità di misura può essere convertita utilizzando la seguente relazione: 1 punto tipografico = 1/72 inch = 0,35278 mm = 0,75 pixel. Questa equivalenza è importante in quanto Cairo for- nisce le dimensioni del file generato in pixel o in punti tipografici, mentre CUPS ha bisogno di un valore espresso in millimetri per un corretto taglio della stampa. Ottenuta la superficie di lavoro il programma genera un og- getto chiamato context su cui vengono effettuate le operazioni di disegno e scrittura. Cairo fornisce un API per la gestione testuale che però presenta diverse limitazioni e di cui è sconsigliato l’uso se non per prove o demo. Per il progetto in analisi, se si fosse deciso di utilizzare Cairo anche per l’inse- rimento del testo il codice sarebbe risultato molto più complesso in quanto la gestione di diverse tipologie di font e il posizionamento del testo in modo dinamico non sono ben gestiti dalle funzioni fornite dall’API. L’inserimento del testo viene eseguito utilizzando la libreria Pango. Con questa libreria è possibile definire agevolmente diversi font di scrittura, che possono venire utilizzati per una preparazione più personalizzata della stampa. Per ogni font desiderato si inizializza una struttura chiamata PangoFontDescription in cui vengono specificati tramite diverse funzioni il colore, la dimensione, la famiglia del font e altre opzioni. Il testo viene posizionato sulla superficie di lavoro tramite coordinate cartesiane, la cui unità di misura è il punto tipo- grafico e la cui origine è nell’angolo superiore sinistro della pagina. Pango permette inoltre di definire vari parametri per il layout, quali le coordinate di inserimento del testo, fornite da Cairo, l’interlinea tra le righe, la larghez- za massima del testo e il “text wrapping”, ovvero le opzioni di gestione del testo se questo supera una cella di dimensioni prefissate. Quando si procede all’inserimento del testo si specifica il layout scelto e il descrittore del font che si vuole utilizzare. Uno dei problemi sorti durante lo sviluppo di questo modulo è stato il calcolo delle dimensioni della stampa finale, in particolare della lunghezza 15
  • 20. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO del file PostScript, parametro essenziale per il funzionamento corretto della stampante. Il problema sorge in quanto in alcune tipologie di stampa previste è variabile sia il numero di parametri che la lunghezza delle stringhe fornite. Non è quindi possibile calcolare a priori la lunghezza finale del foglio ed è stato necessario ricorrere a due cicli di impaginazione, il primo per il calcolo della dimensione finale, e il secondo per la creazione del file PostScript finale. Un’altra funziona importante implementata in questo modulo è quella dedicata alla gestione della stampante termica tramite il sistema di stampa CUPS. Questa funzione non si occupa solamente di inviare alla stampante il file PostScript creato, ma deve anche rilevare eventuali malfunzionamenti nella stampante per poterli segnalare all’utente tramite una specifica codifica audio-visiva. Il programma per prima cosa crea una opzione di stampa per- sonalizzata inizializzando l’oggetto cups_option_t. In questo oggetto viene inserita la lunghezza del foglio che verrà stampato, determinata dalla funzio- ne che crea il file PostScript, e la larghezza, che ha un valore predefinito. In seguito la funzione invia un comando per svuotare la coda di stampa, questo per evitare che in caso di errori e mancata stampa di una qualche chiamata precedente della funzione non si accumulino troppe stampe in coda. Questo potrebbe risultare problematico ad esempio nel momento in cui la carta o il toner risultassero esauriti, poiché al ripristino della stampante potrebbero esserci troppe stampe in attesa ed eventualmente multiple se l’utente ha pas- sato il badge più volte sul lettore. Gli errori di stampa vengono rilevati da un loop che verifica che, per ogni stampa che è stata inserita nella coda, lo stato inviato come feedback dalla stampante sia “printing”. Nel caso in cui le stampe in coda rimangono in uno stato di “pending” o di altro errore è neces- sario segnalare all’utente il malfunzionamento della stampante. La funzione terminerà quindi inviando un valore false (EXIT_FAILURE) al metodo che l’ha invocata, che si occuperà quindi di segnalare il malfunzionamento tramite il led e un segnale sonoro. 16
  • 21. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO 3.3 Modulo di analisi e gestione dell’input In questa sezione verranno descritti i procedimenti eseguiti per l’analisi e la gestione dei dati ricevuti dal WebServer in seguito alla lettura di un badge tramite il lettore di tessere magnetiche. Il software applicativo contatta il WebServer ad un URL specificato nei parametri di configurazione, che ver- ranno descritti in seguito, inviando il codice della tessera magnetica e la data. Il WebServer invia una risposta in un formato testuale, la cui sintassi è molto simile a quella del linguaggio marcatore XML. Infatti, a causa di un’errata configurazione del WebServer, alcuni caratteri vengono codificati con una sintassi non corretta; ad esempio i tag ’<’ e ’>’ vengono letti dal web-service client come ’&lt’ e ’&gt’. La prima operazione che viene esegui- ta dal client è pertanto quella di convertire questa risposta in XML “puro”, in modo tale da poter esplorare agevolmente i dati ricevuti. Effettuata la conversione viene chiamata una funzione che ha il compito di eseguire un parsing del testo XML per salvarlo in un vettore di stringhe. Il parsing viene effettuando utilizzando la libreria TinyXML introdotta in preceden- za (sezione 2.1). TinyXML a partire dal documento XML costruisce un Document Object Model (o DOM), ovverosia struttura il documento come oggetti C++ che possono essere esplorati e manipolati. Le varie sezioni del documento XML vengono quindi modificate a seconda del loro significato; ad esempio un commento, espresso in XML nella forma <!–Comment–>, diven- ta un oggetto denominato TiXmlComment. Nell’analisi che si vuole andare ad effettuare gli elementi rilevanti sono i tag, elaborati come TiXmlElement, e il testo contenuto nelle varie sezioni, che diventa un oggetto TiXmlText. L’algoritmo utilizza questi oggetti per analizzare il documento, inserendo in un vettore di stringhe i vari elementi testuali che vengono incontrati duran- te l’esplorazione dell’albero del DOM. L’algoritmo termina quando tutti gli TiXmlElement sono stati analizzati e restituisce il vettore di stringhe al me- todo invocante. Ogni stringa che viene inserita nel vettore contiene i dati relativi per una singola stampa che si vuole andare a ottenere. I singoli va- lori testuali che devono essere inseriti nel foglio che si vuole ottenere sono inseriti con un ordine prestabilito e separati da un carattere con la funzione di delimitatore. Queste stringhe devono essere quindi ulteriormente analiz- zate per venire scomposte in vari “tokens” che potranno essere utilizzati dal modulo di stampa. Questa operazione viene gestita da un modulo software denominato InputParser. Il modulo inizialmente esplora la stringa fornitagli in input andando a cercare al suo interno il carattere fornito come delimita- tore, separando la stringa principale in sottostringhe in corrispondenza dei delimitatori. La seconda operazione consiste nel riconoscimento della tipo- logia di stampa, indicata dalla prima sottostringa. Sono previsti due casi di stampa e per ogni tipologia viene effettuata un’ulteriore elaborazione dei tokens. In un caso, denominato “operatore”, è previsto un numero di para- metri stabilito a priori, pertanto l’algoritmo verifica se il numero di stringhe 17
  • 22. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO è corretto, producendo una segnalazione di errore per il metodo invocante nel caso in cui il controllo non venga superato. Fra le stringhe oggetto di analisi è presente una stringa che ha il valore di una data che, da specifi- che, deve essere convertita dal formato “dd/mm/yyyy” (giorni, mesi e anno espressi in formato numerico) nel formato “dayname dd/monthname/yyyy” dove dayname è il nome esteso in italiano del giorno, e monthname il no- me esteso del mese considerato. La conversione viene effettuata sfruttando le funzioni strptime e strftime di linux. Strptime converte una stringa che rappresenta un’indicazione temporale in una struttura di tipo tm. Questa struttura è definita nell’header time.h presente nella libreria standard di C. Al suo interno vengono inseriti tutti i dati relativi alle informazioni temporali contenute nella stringa che viene fornita a strptime. La struttura tm viene poi riconvertita in stringa sfruttando la funzione strftime secondo i parametri di formattazione desiderati. Nel secondo caso, denominato “responsabile” i dati contenuti nei tokens devono essere ulteriormente suddivisi in quanto prevedono a loro volta dei delimitatori. Per ogni token viene quindi applicato l’algoritmo utilizzato inizialmente per suddividere la stringa di input nei vari tokens. 3.4 Modulo di configurazione La libreria libconfig++ permette di creare file di configurazione molto ver- satili e in grado di fornire diversi dati al sistema. Il formato del file di confi- gurazione è di tipo testuale ed è costruito per essere più leggibile e compatto dell’XML. E’ inoltre un sistema di gestione della configurazione che richiede poche risorse e, visto il numero non elevato di parametri da configurare nel progetto in analisi, si è rivelato la scelta più opportuna. Una configurazione, utilizzando la sintassi di questa libreria, è costituita da un insieme settings associate ad un nome e a uno o più valori. I valori possono essere valori scalari, array di valori, gruppi di settings, o liste. La sintassi per definire una setting è la seguente: name:value; o name=value; i gruppi vengono identificati dalle parentesi grafe. Ogni settings viene iden- tificata in maniera univoca da un percorso (path), che è costituito da una sequenza di nomi, separati da punti, che partono dal nome del gruppo di livello più alto fino a giungere al nome della setting stessa. Un altro vantag- gio nell’utilizzare questa libreria consiste nel fatto che il tipo dei valori viene determinato in automatico dal valore stesso del dato. Ad esempio un valore racchiuso fra apici viene considerato di tipo stringa; una stringa ’true’ (o ’fal- se’), senza tenere in considerazione maiuscole o minuscole, viene considerata come un parametro di tipo booleano. 18
  • 23. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO Il file di configurazione per il progetto è stato così strutturato: # -------------------------- # Example Configuration File # -------------------------- version = "1.0.0.0"; authWebServiceURL = "http://192.168.50.50:8725/ws.asmx"; NetworkParam = { conf = { dhcp = FALSE; address = "192.168.50.150"; gateway = "192.168.50.254"; netmask = "255.255.255.0"; network = "192.168.20.0"; dnsVal = "8.8.8.8 8.8.8.4"; } work = { dhcp = FALSE; address = "192.168.50.151"; gateway = "192.168.50.254"; netmask = "255.255.255.0"; network = "192.168.20.0"; dnsVal = "8.8.8.8 8.8.8.4"; } } I parametri principali contenuti nel file di configurazione sono quindi la versione del software, l’indirizzo URL del WebService con il quale il program- ma dovrà comunicare e i parametri per la configurazione di rete. Il modulo C++ di configurazione del software applicativo si occupa di effettuare un’a- nalisi del documento e di estrarre i dati contenuti nel file, sfruttando le funzionalità della libreria. L’indirizzo del WebService viene salvato e utiliz- zato quando si verifica la necessità di contattare il WebService per ottenere dei dati relativi a una tessere magnetica. I parametri di rete vengono utiliz- zati come input per uno script AWK che si occupa della configurazione di rete. Questo script è stato sviluppato da terze parti e consente la lettura e la modifica del file /etc/network/interfaces, descritto nel paragrafo 2.2, senza necessità di modificare direttamente il file dei parametri di rete. L’utilizzo di questo script semplifica quindi il programma C++, in quanto è sufficiente eseguire lo script senza la necessità di accedere ai file di sistema. 19
  • 24. CAPITOLO 3. SVILUPPO SOFTWARE APPLICATIVO Lo script, chiamato changeInterfaces.awk viene eseguito nel seguente modo: awk -f /path/to/changeInterfaces.awk <interfaces file> dev=<eth device> [address=<ip addr>] [gateway=<ip addr>] [netmask=<ip addr>] [network=<ip addr>] [mode=dhcp|static] [dns=<ip addr>] Tutti i parametri indicati con le parentesi quadre sono opzionali, se non inseriti non vengono modificati dallo script. Il modulo prepara quindi una stringa corrispondente al comando awk che si vuole eseguire e la manda in esecuzione sfruttando i privilegi di amministratore di cui è dotato il software. Un’altra importante funzionalità gestita da questo modulo software è il controllo dell’autenticità del programma. Per evitare che il software instal- lato sulla scheda SD, che funge da memoria per il Raspberry, possa venir clonato in modo incontrollato, è stato deciso di inserire una licenza univoca per ogni dispositivo. La licenza sviluppata è legata ai parametri hardware del Raspberry che, essendo univoci, permettono di legare in maniera indisso- lubile licenza e hardware. I parametri hardware non sono inseriti “in chiaro” all’interno della licenza, ma vengono elaborati e crittografati utilizzando un sistema a codifica XOR. Per evitare manomissioni del file contenente la li- cenza, essa è stata inserita nella partizione /boot impostata in modalità read-only (descritta nella sezione 3.3), che risulta accessibile in lettura a tut- ti gli utenti, ma modificabile solo da un utente amministratore che decida di reimpostare la partizione in modalità di scrittura. La licenza viene infatti ge- nerata dall’amministratore di sistema al momento della prima installazione del software applicativo o in caso di sostituzione della scheda SD. Il modulo software qui trattato esegue un algoritmo che gli permette di calcolare a sua volta il codice di licenza che verrà poi confrontato con quello contenuto nella partizione protetta. Il risultato e il timestamp di questo controllo viene sal- vato su un file di log accessibile via FTP. Il risultato sarà rispettivamente un valore EXIT_SUCCESS nel caso in cui il controllo risulti superato, per- mettendo la prosecuzione del programma, o un valore EXIT_FAILURE nel caso in cui il controllo fallisca, caso che verrà trattato opportunamente dalla classe che ha invocato questa funzione. 20
  • 25. Capitolo 4 Automatizzazione del dispositivo In questa sezione verranno descritti gli interventi fatti per rendere auto- matizzato il funzionamento del dispositivo. In particolare verrà descritto e analizzato lo script bash utilizzato per eseguire il software applicativo, i tool e le configurazioni adottate per l’utilizzo di FTP e SSH. 4.1 Script per la gestione del software applicativo Per poter eseguire all’avvio il software applicativo, è stato sviluppato uno script bash che viene eseguito all’avvio del sistema. Questo script svolge diverse funzioni: si occupa di monitorare la cartella dove vengono caricati tramite il protocollo FTP i nuovi file di configurazione e di spostare questi file nella sezione apposita del dispositivo, crea un log delle accensioni e man- da in esecuzione il software applicativo. La prima operazione svolta dallo script è quella di attivare l’interfaccia di rete di default, ovvero l’interfaccia di configurazione che, come visto nel paragrafo 2.2 è stata configurata tra- mite indirizzo IP statico. Il log delle accensioni della scheda viene generato salvando gli ultimi 50 accessi, con data di accesso, data dell’ultimo login e data dell’ultima configurazione. Questi dati vengono inseriti in un file che è visualizzabile tramite FTP o SSH. Quando viene eseguito, lo script controlla se sono stati inseriti nuovi aggiornamenti in una cartella definita in fase di configurazione. Se il controllo non viene superato, lo script continua a mo- nitorare la cartella in questione per 30 secondi tramite il tool “inotifywait”. Inotifywait è un tool utilizzato per monitorare dei cambiamenti su specifici files, utilizzando l’API inotify di linux. Questa API mette a disposizione diverse system call per il monitoraggio di files e cartelle. Nel progetto in analisi è stata scelta inotifywait in quanto più adatta per essere usata in shell script. Se nel controllo iniziale, o nei 30 secondi successivi vengono trovati aggiornamenti, lo script crea un backup del programma precedente 21
  • 26. CAPITOLO 4. AUTOMATIZZAZIONE DEL DISPOSITIVO e posiziona i nuovi file nel percorso voluto. Per l’esecuzione del programma viene eseguita una fork dello script in esecuzione che viene messa in stato di “sleep” per 50 secondi. Al termine di questo periodo il processo generato tramite la chiamata di sistema fork() manda in esecuzione il programma e invia un comando kill() tramite il segnale SIGUSR1 a se stesso e al processo che l’ha generato. Tramite questa procedura il processo figlio dello script, ovvero il software applicativo, viene “adottato” da root, garantendogli quindi i permessi di amministratore per tutta la durata del programma. L’utiliz- zo della system call kill() viene adottato anche per terminare eventuali casi non previsti o errori durante l’esecuzione dello script, come ad esempio er- rori duranti il loop di monitoraggio della cartella FTP o nella creazione del backup. In ambiente linux esistono diverse vie valide per eseguire un programma o uno script all’avvio del sistema operativo. Una di queste prevede la crea- zione di uno script, basato su un preciso standard, nella directory /etc/init.d per eseguire un dato programma. Lavorando con il Raspberry esiste un’al- ternativa più semplice per eseguire un comando o un programma al boot del sistema operativo, senza la necessità di ulteriori script o configurazioni. Questa alternativa consiste nell’inserire i comandi desiderati all’interno del file rc.local, il cui percorso è /etc/rc.local. Nel caso in analisi la modifica del file è stata l’inserimento della seguente stringa all’interno di rc.local: sudo /path/to/ConfigMonitorRoutine.sh & Il comando esegue con i permessi di superuser lo script analizzato in prece- denza, chiamato appunto ConfigMonitorRoutine.sh. Il carattere ‘&’ è stato inserito per eseguire una chiamata di sistema fork() del processo, consenten- do allo stesso tempo il proseguimento dell’esecuzione dello script rc.local e la conclusione del boot del Raspberry. 4.2 Accesso via FTP e SSH e permessi utente Per consentire l’aggiornamento e un monitoraggio in remoto del sistema si è deciso di abilitare un server FTP e di configurare opportunamente un SSH server su ogni sistema operativo installato. La prima operazione è stata quella di creare due tipologie di utente, la prima con i permessi di superuser, mentre la seconda come utente standard, senza privilegi di root. Questo per consentire solo agli amministratori di sistema di effettuare modifiche significative al progetto. L’idea è quella di garantire l’accesso completo da remoto al sistema solo agli utenti in possesso dei privilegi di amministratore, mentre gli utenti standard devono poter avere accesso solo a un particolare server FTP usato per gli aggiornamenti e per la lettura di alcuni log di sistema. 22
  • 27. CAPITOLO 4. AUTOMATIZZAZIONE DEL DISPOSITIVO E’ stato quindi attivato un server FTP su ogni scheda, garantendo l’ac- cesso a tutti gli utenti autorizzati solo a una specifica cartella. Questa car- tella verrà utilizzata per l’inserimento di nuovi aggiornamenti del software applicativo e del file di configurazione. In questa cartella vengono inoltre generati alcuni file di log, in modo tale da garantire un primo metodo per diagnosticare eventuali problemi avvenuti durante l’avvio del programma. La configurazione del server FTP è stata fatta utilizzando vsftpd uno dei programmi più usati disponibili in ambiente linux per l’installazione di un server FTP. L’installazione di vsftpd viene fatta sfruttando il comando apt- get, mentre per la configurazione dello stesso si procede alla modifica del file vsftpd.conf, che viene collocato al momento dell’installazione nella directory /etc. Il file di configurazione è stato quindi modificato negando l’accesso tra- mite FTP ad utenti anonimi e limitando l’accesso alle due tipologie di utenti prima descritte solo a una particolare cartella che si è deciso di collocare nella directory /var. Per consentire agli amministratori un accesso completo in remoto al si- stema operativo è stato inoltre abilitato un server SSH. Anche in questo caso per l’installazione si è utilizzato il tool apt-get, e per la configurazione è sta- to necessario modificare il file di configurazione relativo, collocato anch’esso nella directory /etc. 23
  • 28. Conclusioni Il dispositivo che si è andati a sviluppare si è dimostrato in grado di soddisfa- re le specifiche emerse in fase di analisi introduttiva. Sono state utilizzate in modo efficace tutte le periferiche imposte dal committente ed è stato svilup- pato un opportuno sistema di feedback per l’utente. Il funzionamento della web service e la comunicazione con il web server sono stata testati in modo estensivo, così come i moduli software per l’elaborazione dei dati. Il sistema si è inoltre dimostrato molto robusto, garantendo così stabilità al progetto. Il software sviluppato è stato installato su diversi Raspberry Pi 2, che dopo esser stati opportunamente configurati, sono adesso operativi nelle sedi sta- bilite dal committente. Grazie alle soluzioni adottate per l’aggiornamento e la configurazione remota del sistema, c’è la possibilità di migliorare ulterior- mente il sistema in futuro e eventualmente di sviluppare nuove funzionalità. Alcune delle soluzioni che sono state sviluppate sono inoltre utilizzabili anche su differenti progetti basati sulla stessa tipologia di hardware. In particolare l’impostazione di parte del file-system in modalità di sola lettura, lo script per l’esecuzione all’avvio di un software applicativo e l’abilitazione degli ac- cessi FTP e SSH sono strumenti che possono essere agevolmente utilizzati in altri progetti basati sul Raspberry Pi o schede simili. 24
  • 29. CONCLUSIONI Figura 4.1: Foto del progetto ultimato e preparato per l’utilizzo industriale Figura 4.2: Foto del progetto installato in ambito industriale 25
  • 30. Appendice A Codice Sorgente In questa sezione verranno presentate le parti più significative del codice sviluppato. Alcune sezioni sono state omesse per motivi di riservatezza. A.1 PrinterModule.cpp Classe contenente le varie funzioni dedite alla creazione di file PostScript per la stampa e la gestione della stampante termica. Verrà presentata solo una delle funzioni di stampa, quella per la creazione della stampa all’avvio. La creazione delle stampe in seguito all’analisi dei dati ricevuti dal WebServer è analoga, ma con un layout molto più complesso. I dati per questo tipo di stampa vengono ottenuti dalla classe InputParser.c . #include "PrinterModule.h" #include "Periph/InputParser.h" using std::string; using std::vector; /* Definizione delle dimensioni del foglio e di alcuni parametri come il bordo laterale e l’interlinea Questi valori sono espressi in punti tipografici */ #define WIDTH 520 #define HEIGHT 2080 #define INTERLINE 20 #define BORD 0 #define FONT "Calibri" 26
  • 31. APPENDICE A. CODICE SORGENTE int settext (cairo_t *context, PangoFontDescription *desc, int x, int y, char string[]){ /* Questo metodo posiziona una stringa su un cairo context alle coordinate desiderate. */ PangoLayout *layout; //Utilizzo dei metodi della librerie pango e cairo per la gestione del testo cairo_translate(context, x, y); layout = pango_cairo_create_layout(context); pango_layout_set_width(layout, WIDTH*PANGO_SCALE); pango_layout_set_wrap (layout, PANGO_WRAP_WORD); pango_layout_set_spacing (layout,(int)INTERLINE*PANGO_SCALE*0.5); pango_layout_set_text(layout, string, -1); pango_layout_set_font_description(layout, desc); pango_cairo_update_layout(context, layout); pango_cairo_show_layout(context, layout); int width; int height; pango_layout_get_size (layout, &width, &height); height =(int)(height/PANGO_SCALE); cairo_translate(context, -x, -y); g_object_unref(layout); return height; } int setcenteredtext (cairo_t *context, PangoFontDescription *desc, int x, int y, char string[]){ /*Posiziona il testo al centro della riga utilizzando il metodo precedente*/ int x_len; int y_len; //funzione che fornisce lunghezza del testo in px pango_layout_get_pixel_size (layout, &x_len, &y_len); //calcola la lunghezza del testo in punti tipografici x_len=0.75*x_len; //calcola la posizione del testo centrato int x_coord=0.5*(WIDTH-x_len+x); return settext (context, desc, x_coord, y, string); } 27
  • 32. APPENDICE A. CODICE SORGENTE int PrinterModule::PrintRoutine(char tmpfilename[], int len){ /*Questa sezione del programma dedicata all’invio alla stampante termica dei file PostScript creati*/ int num_jobs; cups_job_t *joblist; //Oggetto contenente la coda di stampa //Determinazione della stampante di default cups_dest_t *dests; int num_dests = cupsGetDests(&dests); //Creazione delle opzioni di stampa int num_options; cups_option_t *options; num_options = 0; options = (cups_option_t *)0; int mylen=(int)((len+10)/10)*10; string optiontext = "job-sheets=none,none media=Custom.72x"+std::to_string(mylen)+"mm sides=one-sided"; //Impostazione delle opzioni di stampa num_options = cupsParseOptions(optiontext.c_str(), num_options, &options); //Svuotamento della coda di stampa cupsFreeDests(num_dests, dests); cupsCancelJob(cupsGetDefault(), CUPS_JOBID_ALL); //Inizio della stampa int job = cupsPrintFile(cupsGetDefault(), tmpfilename, "cairo PS", num_options, options); num_jobs = cupsGetJobs(&joblist, NULL, 0, -1); /*Controllo di eventuale stampe bloccate ed eventuale segnalazione dell’errore. Il controllo viene fatto verificando che le stampe inserite in coda di stampa siano in uno stato di "printed"*/ for (int i = 0; i < num_jobs; i ++){ printf("%-16.16s %-6d %-12.12s %s (%s)n", joblist[i].dest, joblist[i].id, joblist[i].user, joblist[i].title, joblist[i].state != IPP_JOB_PENDING ? "printed" : "pending"); if(joblist[i].id == job) { if(joblist[i].state == IPP_JOB_PENDING ){ 28
  • 33. APPENDICE A. CODICE SORGENTE std::cout << "Errore nella stampa"<<’n’; cupsFreeOptions (num_options, options); cupsFreeJobs(num_jobs, joblist); return EXIT_FAILURE; } } } cupsFreeJobs(num_jobs, joblist); cupsFreeOptions (num_options, options); return EXIT_SUCCESS; } int PrinterModule::StartingPrint (string Version, string MACAddress, string IPAddress){ char filename[]="/tmp/StartingPrint.ps"; cairo_surface_t* surface; cairo_t *context; PangoFontDescription *font; //Creazione di una surface con una specifica dimensione (in punti tipografici) int len = 500; double mmlen=len*(0.35)/(2.5); surface=cairo_ps_surface_create (filename, WIDTH, len); //Creazione del context context=cairo_create (surface); //Creazione di un descrittore di font font=pango_font_description_new (); pango_font_description_set_size (font,17*PANGO_SCALE); pango_font_description_set_family (font, FONT); pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD); settext(context, font, BORD, 0, (char*)"QAS SERVICE REPORTER STARTED"); settext(context, font, BORD, 50, (char*)"------------------------------------------------------"); std::string Versiontext="Version: "+Version; settext(context, font, BORD, 100, (char*)Versiontext.c_str()); std::string LicenseText="License OK"; settext(context, font, BORD, 150, (char*)LicenseText.c_str()); std::string MACtext="MACAddress: "+MACAddress; settext(context, font, BORD, 200, (char*)MACtext.c_str()); std::string IPtext="Work IP Address: "+IPAddress; settext(context, font, BORD, 250, (char*)IPtext.c_str()); settext(context, font, BORD, 300, (char*)"------------------------------------------------------"); 29
  • 34. APPENDICE A. CODICE SORGENTE //Salvataggio del file e distruzione degli oggetti creati cairo_surface_flush(surface); cairo_surface_destroy(surface); cairo_destroy(context); pango_font_description_free(font); //Inizio stampa PrintRoutine(filename, (int)mmlen); return EXIT_SUCCESS; } A.2 InputParser.cpp Classe contente le funzioni che vengono utilizzate da PrinterModule.cpp per ottenere le stringhe da inserire nel modulo di stampa. #include "InputParser.h" using std::string; using std::vector; vector<string> InputParser::str_decode (string Input, string delim){ //Questa funzione scompone la stringa in input in vari token separati dalla sottostringa delim vector<string> res; int start = 0; int end = Input.find(delim); while (end != std::string::npos) { res.push_back (Input.substr(start, end - start)); start = end + delim.length(); end = Input.find(delim, start); } return res; } int InputParser::ConvertDate (char *input){ /*Converte la data dal formato "dd/mm/yyyy" nel formato "local_dayname dd/mmmm/yyyy" */ setlocale (LC_ALL, "" ); struct tm tm; char buf[255]; 30
  • 35. APPENDICE A. CODICE SORGENTE memset(&tm, 0, sizeof(struct tm)); //Utilizzo di strptime if(strptime(input,"%d/%m/%Y",&tm)==NULL){ printf ("Formato data erraton"); //input non valido return(EXIT_FAILURE); }; //Utilizzo di strftime if(strftime(buf, sizeof(buf), "%^A %d %^B %Y", &tm)==0) { printf ("Errore conversione datan"); //contents of buf are undeterminate return(EXIT_FAILURE); }; std::string Date ((const char*) buf); this->Date=Date; return(EXIT_SUCCESS); } int InputParser::CheckTokens (vector<string> tokens){ //Controlla il numero di tokens per l’input di tipo "operatore" if (tokens.size()<6) { printf("Numero Parametri Insufficienten"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int InputParser::GetOperator (){ this->Operator = this->tokens[0]; tokens.erase(tokens.begin()); return EXIT_SUCCESS; } int InputParser::DecodeInput (string input, string delim){ //Scompone l’input e prepara i parametri necessari alla classe PrinterModule.cpp this->tokens = str_decode (input, delim); this->GetOperator(); if (Operator == "O") { if (this->CheckTokens(tokens) == EXIT_FAILURE) { return EXIT_FAILURE; } this->Name = str_decode (tokens[0],";"); if (Name.size()==0){ 31
  • 36. APPENDICE A. CODICE SORGENTE this->Name.push_back(tokens[0]); } if(this->ConvertDate((char*)tokens[1].c_str()) == EXIT_FAILURE){ return EXIT_FAILURE; } this->Partofday = tokens[2]; this->Activity = tokens[3]; this->Location = tokens[4]; this->Vehicle = str_decode (tokens[5], ";"); if(Vehicle.size()==0){ this->Vehicle.push_back(tokens[5]); } this->Nota = tokens[6]; return EXIT_SUCCESS; } else if (Operator == "R") { for(int i=0;i<tokens.size();i++){ vector<string> decode = str_decode(tokens[i], ";"); this->UncoveredActivities.insert(std::end(UncoveredActivities), std::begin(decode), std::end(decode)); } return EXIT_SUCCESS; } return EXIT_FAILURE; } 32
  • 37. APPENDICE A. CODICE SORGENTE A.3 Configuration.cpp #include "Configuration.h" #include "Periph/PrinterModule.h" using namespace std; using namespace libconfig; void Configuration::SettingNetwork(string networktype) { //Funzione che si occupa di impostare le modifiche ai parametri di rete basandosi sul file di configurazione //Il programma genera una stringa costituente un apposito comando AWK //Il comando viene poi eseguito sfruttando i privilegi di amministratore di questo programma bool dhcp; std::string Address; std::string Gateway; std::string Netmask; std::string Network; std::string dnsVal; try { /*Funzionamento script awk awk -f /path/to/changeInterfaces.awk <interfaces file> dev=<eth device> n * [address=<ip addr>] [gateway=<ip addr>] [netmask=<ip addr>] n * [network=<ip addr>] [mode=dhcp|static] [dns=<ip addr>] */ std::string cmd = "awk -f /path/to/changeInterface.awk"; //posizione dello script sul dispositivo cmd = cmd + " /etc/network/interfaces "; cmd = cmd + "dev=eth0-" + networktype; const Setting& root = cfg.getRoot(); const Setting& param = root["NetworkParam"][networktype.c_str()]; //Connessione tramite DHCP try { dhcp = param.lookup("dhcp"); if (dhcp == true) { cmd = cmd + " " + "mode=dhcp"; cmd = cmd + " >> /etc/network/interfaces.tmp && mv /etc/network/interfaces.tmp /etc/network/interfaces"; 33
  • 38. APPENDICE A. CODICE SORGENTE system(cmd.c_str()); return; } } catch (const SettingNotFoundException &nfex) { } //Connessione statica try { param.lookupValue("address", Address); cmd = cmd + " address=" + Address; if (networktype == "work") { this->WorkIPAddress = Address; } } catch (const SettingNotFoundException &nfex) { } try { param.lookupValue("gateway", Gateway); cmd = cmd + " gateway=" + Gateway; } catch (const SettingNotFoundException &nfex) { } try { param.lookupValue("netmask", Netmask); cmd = cmd + " netmask=" + Netmask; } catch (const SettingNotFoundException &nfex) { } try { param.lookupValue("network", Network); cmd = cmd + " network=" + Network; } catch (const SettingNotFoundException &nfex) { } cmd = cmd + " " + "mode=static"; try { param.lookupValue("dnsVal", dnsVal); cmd = cmd + " dns=" + dnsVal; } catch (const SettingNotFoundException &nfex) { } cmd = cmd + " >> /etc/network/interfaces.tmp && mv /etc/network/interfaces.tmp /etc/network/interfaces"; system(cmd.c_str()); } catch (SettingNotFoundException &nfex) { } 34
  • 39. APPENDICE A. CODICE SORGENTE } string EncryptDecrypt(string source) { //Cifratura XOR. La chiave contenuta nell’array key[], qui omesso string output = source; for (int i = 0; i < source.size(); i++) { output[i] = source[i] ^ key[i % (sizeof (key) / sizeof (char))]; } return output; } string GetLocalSerial() { char line[256]; FILE *mac; mac = fopen("/sys/class/net/eth0/address", "r"); if (mac == NULL) { throw "Rete non configurata correttamente"; } fgets(line, 256, mac); fclose(mac); string result = line; return EncryptDecrypt(result); } string GetLicenceSerial() { //Verifica la presenza di una licenza e ne ottiene il codice. FILE *licenceFile; char line[256]; char code[256]; licenceFile = fopen("/boot/EliLicence.txt", "r"); if (licenceFile == NULL) { throw "File di Licenza mancante"; } else { while (fgets(line, 256, licenceFile)) { if (strncmp(line, "License Code", 12) == 0) { strcpy(code, strchr(line, ’:’) + 2); } } } fclose(licenceFile); string LicenseID = code; return LicenseID; } int Configuration::CheckLicence() { //Controllo della licenza time_t rawtime; 35
  • 40. APPENDICE A. CODICE SORGENTE struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); ofstream resfile; //Crea un log nella partizione FTP contenente lo stato della licenza resfile.open("/var/ftp/configuser/LicenceResult.txt", std::ofstream::app); try { this->LicenseID = GetLicenceSerial(); } catch (const char* msg) { //File di licenza mancante resfile << asctime(timeinfo) << ": " << msg << std::endl; resfile.close(); throw msg; return EXIT_FAILURE; } (...) //Calcolo della licenza RpiID tramite i parametri di sistema if (RpiID.compare(this->LicenseID) != 0) { resfile << asctime(timeinfo) << ": Codice di licenza non corrispondenten"; resfile.close(); std::cerr << "Codice di licenza non corrispondente" << std::endl; throw "Codice di licenza non corrispondente"; return EXIT_FAILURE; } resfile << asctime(timeinfo) << ": Controllo licenza superato correttamenten"; resfile.close(); ofstream licfile; licfile.open("/var/ftp/configuser/EliLicence.txt"); licfile << this->LicenseID; licfile.close(); return EXIT_SUCCESS; } int Configuration::LoadConfiguration() { //Tentativo di lettura del file di configurazione. try { cfg.readFile("/path/to/configuration.cfg"); //posizione prevista del file } catch (const FileIOException &fioex) { std::cerr << "MISSING CONFIGURATION FILE" << std::endl; 36
  • 41. APPENDICE A. CODICE SORGENTE return (EXIT_FAILURE); } catch (const ParseException &pex) { std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() << " - " << pex.getError() << std::endl; return (EXIT_FAILURE); } //accesso ai parametri const Setting& root = cfg.getRoot(); const Setting& param = root["NetworkParam"]; this->version = VERSION; try { cfg.lookupValue("authWebServiceURL", this->authWebServiceURL); } catch (const SettingNotFoundException &nfex) { cerr << "No authWebServiceURL setting in configuration file." << endl; } //impostazione configurazioni di rete try { SettingNetwork("conf"); SettingNetwork("work"); } catch (SettingNotFoundException &nfex) { } return 0; } void Configuration::SwitchConnection() { //Passa dalla connessione di Configurazione a quella Operativa //In caso di fallimento del controllo di licenza, questo cambio non avviene e //il dispositivo rimane connesso alla connessione di configurazione std::cout << "Turning OFF Configuration Connection" << std::endl; system("ifdown eth0=eth0-conf"); std::cout << "Turning ON Service Connection" << std::endl; system("ifup eth0=eth0-work"); } void Configuration::Dump() { //Stampa che viene effettuata all’avvio del programma, per segnalare all’utente che il sistema operativo std::string localIPAddress = GetLocalIPAddress(); PrinterModule module; 37
  • 42. APPENDICE A. CODICE SORGENTE module.StartingPrint(this->version, this->MACAddress, localIPAddress); } A.4 Utilizzo Tiny Xml Il parsing del testo XML ricevuto in input è gestito da un modulo inserito nel WebClient sviluppato per il dispositivo. Esistono due possibili stringhe che risultano di interesse. La prima è delimitata dai tag <string>< /string>, mentre la seconda è una stringa inserita fra i tag <lavoro>< /lavoro>, che viene inserita come figlio dell’elemento <string>. (...) vector<string> eliWebClient::ParseResultXML(std::string resultxml) { vector<string> result; TiXmlDocument doc; doc.Parse(resultxml.c_str(), 0, TIXML_ENCODING_UTF8); //Determinazione dell’elemento "padre" //father -> <string>..</string> TiXmlElement* father = doc.FirstChildElement("string"); //Determinazioni dei "figli" //element -> <string><lavoro>...</lavoro></string> TiXmlNode* firstChildString = doc.FirstChild("string"); TiXmlElement* element; if (firstChildString) { element = firstChildString->FirstChildElement(); //<string><lavoro>...</lavoro></string> if (element) { for(element; element; element=element->NextSiblingElement()){ result.push_back(element->GetText()); } return result; } } if (father){ result.push_back(father->GetText()); 38
  • 43. APPENDICE A. CODICE SORGENTE return result; } return result; } 39
  • 44. Bibliografia [1] J. Kuan. (2015) Script awk per modificare il file /etc/net- work/interfaces. [Online]. Available: https://github.com/JoeKuan/ Network-Interfaces-Script [2] (2016) Dati tecnici raspberry pi 2 model b. [Online]. Available: http://elinux.org/RPi_Hardware [3] RaspberryPiFoundation. (2016) Documentazione ufficiale raspberry pi. [Online]. Available: https://www.raspberrypi.org/documentation/ [4] W3C. (2016) Web server architecture. [Online]. Available: https: //www.w3.org/TR/ws-arch/ [5] Cairographics. [Online]. Available: https://www.cairographics.org/ [6] Pango. [Online]. Available: https://developer.gnome.org/pango/stable/ [7] Libconfig++. [Online]. Available: http://www.hyperrealm.com/ libconfig/ 40