UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE...

71
UNIVERSITÀ DEGLI STUDI DI PARMA F ACOLTÀ DI SCIENZE MATEMATICHE FISICHE E NATURALI CORSO DI LAUREA IN INFORMATICA Progettazione e realizzazione di un framework per il controllo integrato di dispositivi via porta seriale, USB ed Ethernet Relatore: Candidato: Ing. Federico Bergenti Francesco Sacchi

Transcript of UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE...

Page 1: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

UNIVERSITÀ DEGLI STUDI DI PARMA

FACOLTÀ DI SCIENZE

MATEMATICHE FISICHE E NATURALI

CORSO DI LAUREA IN INFORMATICA

Progettazione e realizzazione di un frameworkper il controllo integrato di dispositivi via porta

seriale, USB ed Ethernet

Relatore: Candidato:

Ing. Federico Bergenti Francesco Sacchi

Page 2: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione
Page 3: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

Indice

1 Ringraziamenti............................................................................................7

2 Introduzione................................................................................................9

2.1 L'azienda Custom Engineering S.p.A...............................................................................9

2.2 I prodotti...........................................................................................................................9

2.2.1 Soluzioni P.O.S. Retail..............................................................................................9

2.2.2 Soluzioni OEM/industriali......................................................................................10

2.2.3 Emettitori di biglietti (TKT) e scanner per scommesse..........................................11

2.2.4 Soluzioni multimediali............................................................................................11

2.3 Il lavoro svolto nel tirocinio aziendale...........................................................................12

2.3.1 Descrizione del lavoro di tesi..................................................................................13

2.4 Il protocollo ad alto livello dei prodotti Custom............................................................13

2.5 Comunicazione Seriale, Ethernet, USB in Linux...........................................................14

2.5.1 Comunicazione seriale............................................................................................14

2.5.2 Comunicazione Ethernet.........................................................................................16

2.5.3 Comunicazione USB...............................................................................................18

2.5.4 La libreria “libusb-1.0”...........................................................................................20

3 Il problema della migrazione da C a C++................................................21

3.1 Motivazioni e vantaggi...................................................................................................21

3.2 Framework software e librerie software.........................................................................21

3.3 Le differenze nell'approccio...........................................................................................22

3.3.1 Le classi..................................................................................................................22

3.3.2 L'overloading dei metodi........................................................................................23

3.3.3 I namespace.............................................................................................................24

3.3.4 I reference...............................................................................................................24

3.3.5 I casting...................................................................................................................24

3.3.6 Le eccezioni............................................................................................................25

3.3.7 La Standard Template Library................................................................................25

3.4 UML...............................................................................................................................26

3.5 Design pattern.................................................................................................................27

Page 4: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4 Analisi funzionale.....................................................................................29

4.1 Requisiti di funzionamento.............................................................................................29

4.2 La scelta di non realizzare un driver...............................................................................30

4.3 Modalità di utilizzo del framework................................................................................31

4.3.1 UML: Use-case diagram.........................................................................................33

4.3.2 UML: Sequence diagram........................................................................................35

4.3.3 UML: Activity diagram..........................................................................................35

5 Progettazione del framework C++............................................................37

5.1 Scelte architetturali con l'uso di Design Pattern.............................................................37

5.1.1 Façade.....................................................................................................................38

5.1.2 Lazy Initialization...................................................................................................38

5.2 Struttura del framework con uso di UML......................................................................39

5.2.1 UML: Class diagram...............................................................................................40

5.2.2 UML: Communication diagram..............................................................................42

5.3 Elenco dei metodi forniti all'utente.................................................................................43

5.3.1 Istanziazione del framework...................................................................................43

5.3.2 Gestione di connessioni multiple contemporanee...................................................43

5.3.3 Inizializzazione di comunicazioni seriali................................................................44

5.3.4 Inizializzazione di comunicazioni Ethernet............................................................45

5.3.5 Inizializzazione di comunicazioni USB..................................................................46

5.3.6 La comunicazione vera e propria coi dispositivi....................................................46

6 Implementazione del framework C++......................................................49

6.1 I sorgenti realizzati.........................................................................................................49

6.1.1 Elenco dei sorgenti che compongono il framework...............................................49

6.1.2 Alcuni dettagli sull'implementazione del codice....................................................49

6.2 La gestione del progetto.................................................................................................56

6.2.1 Le librerie a collegamento dinamico.......................................................................56

6.2.2 Il comando “make” per la compilazione.................................................................56

6.2.3 Il debugging............................................................................................................59

6.3 La documentazione.........................................................................................................60

6.3.1 Istruzioni per la compilazione.................................................................................61

6.3.2 Lo strumento “Doxygen” per la documentazione...................................................61

Page 5: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.3.3 La documentazione risultante in formato HTML...................................................64

7 Testing e applicazioni di esempio.............................................................65

7.1 Il testing durante l'implementazione...............................................................................65

7.2 Applicazioni di esempio.................................................................................................65

8 Conclusioni...............................................................................................69

8.1 Bibliografia.....................................................................................................................71

Page 6: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione
Page 7: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

1 Ringraziamenti

1 Ringraziamenti

Desidero ringraziare:

L'azienda Custom Engineering che mi ha permesso di effettuare lo stage

aziendale su cui si basa questo lavoro di tesi;

Luca Bettati, che è stato il mio tutor aziendale e che mi ha aiutato nella

comprensione dei prodotti della Custom, fornendomi la documentazione

necessaria e preziosi consigli;

L'ing. Paolo Virgili di Custom Engineering per aver organizzato lo stage;

L'ing. Luciano Varani per avermi segnalato l'offerta di stage dell'azienda;

L'ing. Federico Bergenti per aver accettato di svolgere il compito di tutor

universitario per il tirocinio aziendale e di relatore per questo lavoro di tesi;

La mia famiglia, che mi ha sempre sostenuto in questi anni di studi.

7

Page 8: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

1 Ringraziamenti

8

Page 9: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

2 Introduzione

Il framework progettato e realizzato, di cui tratta questo lavoro di tesi, è stato

sviluppato per la Custom Engineering S.p.A. durante un periodo di tirocinio

universitario svolto presso l'azienda.

2.1 L'azienda Custom Engineering S.p.A.La Custom Engineering S.p.A. (www.custom.biz) è un'azienda nata a Fontevivo

(PR) nel 1992, conta oggi alcune centinaia di dipendenti e ha sedi in Italia e

all'estero. Si è affermata nella progettazione, produzione e distribuzione di

soluzioni per stampa su carta termica, gestione del punto vendita, acquisizione di

immagini e si sta espandendo in altri settori di supporto tecnologico per le aziende.

I mercati principali sono quello italiano, spagnolo, russo e l'emergente mercato

cinese.

La sede legale dell'azienda, nonché ubicazione del mio tirocinio, è in Strada

Berettine 2, a Fontevivo (PR).

2.2 I prodotti

2.2.1 Soluzioni P.O.S. Retail

I prodotti per Point Of Sale (in italiano: punto vendita, o semplicemente “cassa”)

retail della Custom Engineering escono dall'azienda già pronti per l'installazione

presso i clienti. Sono numerosi e suddivisibili in queste categorie:

• Stampanti per scontrini (versioni fiscali e non fiscali)

• Stampanti per etichette

9

Page 10: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.2 I prodotti

• Scanner

• PC-POS con touch-screen e stampante integrata (fiscali e non fiscali)

• Stampanti portatili (collegabili a palmari via Bluetooth o a reti Wi-Fi)

• Mini Player per immagini e video pubblicitari

• accessori come display aggiuntivi, cassetti rendi-resto, lettori di carte, tastiere

2.2.2 Soluzioni OEM/industriali

Sono soluzioni adatte ad essere installate da parte dei clienti all'interno di sistemi

personalizzati, come chioschi, ATM bancari, registratori di cassa e scanner anche

in formato A4. Alcuni di questi prodotti sono la versione “nuda” (o uno dei

componenti) di altri prodotti retail, mentre altri sono stati progettati in modo

specifico.

10

Da sinistra: stampante portatile MY3, stampante Kube II, punto-cassa Kube T, XPC-POS

Da sinistra: mini-stampante CM 112, stampante industriale PRT 80, scanner A4

Page 11: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

2.2.3 Emettitori di biglietti (TKT) e scanner per scommesse

Questi dispositivi fanno parte di una famiglia molto numerosa ed in espansione

per la Custom Engineering, e si tratta di prodotti specifici o prodotti delle altre

famiglie integrati con nuovi componenti, come scanner per le schedine delle

giocate o lettori di codici a barre.

Esistono sia versioni retail (pronte per l'uso) sia OEM (da integrare in altri

sistemi personalizzati prima dell'uso).

2.2.4 Soluzioni multimediali

Questo è un settore nuovo ed in

espansione, e include monitor (anche

touch-screen) di varie dimensioni e

prodotti denominati “Mini Player”,

che sono dei piccoli ed economici

computer dal basso consumo, in

grado di gestire schermi S-VGA e riprodurre sequenze pubblicitarie: anch'essi

supportano una parte del protocollo di stampanti e scanner, ma solo via Ethernet, e

sono compatibili con il software realizzato durante il tirocinio.

11

Stampante Kube II Scanner, stampanti di biglietti con RFID e barcode: TK300 II e KPM300 H

Mini Player e monitor touch LCD 17" M17

Page 12: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.3 Il lavoro svolto nel tirocinio aziendale

2.3 Il lavoro svolto nel tirocinio aziendaleDurante il periodo di tirocinio, che si è svolto da fine dicembre 2008 a fine aprile

2009, su un totale di circa 450 ore, sono stato inserito nel reparto di ricerca e

sviluppo, dove mi è stato assegnato un tutor aziendale (Luca Bettati), che cinque

anni prima aveva svolto un'esperienza simile alla mia proprio alla Custom, e che

mi ha dato il supporto necessario per conoscere i prodotti con cui interfacciarmi, i

relativi comandi integrati, nonché le linee-guida per le specifiche del software. In

ogni caso mi è stata concessa parecchia libertà riguardo la strutturazione del

progetto, la stesura del codice, nonché nel modo in cui creare la documentazione.

L'azienda mi ha richiesto la realizzazione di due versioni, per sistema operativo

Linux, della stessa libreria software: una più semplice in linguaggio C, per

questioni di leggerezza e compatibilità con sistemi embedded ad uso industriale e

vecchi personal computer che sono utilizzati da alcuni clienti, e l'altra, che

chiameremo “framework”, in linguaggio C++, per sfruttare le caratteristiche più

avanzate offerte da questo linguaggio.

Lo scopo del software doveva essere quello di comunicare con i dispositivi

prodotti dalla Custom Engineering attraverso tre tipi di connessione (seriale,

Ethernet, USB) in modo trasparente all'utente, ovvero in modo che non si notino

differenze nel comunicare con connessioni differenti.

Come linea-guida mi è stata consegnata la documentazione di un'analoga libreria

per ambiente Windows, già sviluppata precedentemente dalla Custom Engineering

con Microsoft Visual Studio. Il mio compito è stato quello di progettare e

realizzare le versioni per Linux con un comportamento il più simile possibile a

quella per Windows, in particolar modo riguardo i nomi di funzioni, metodi e

classi e i meccanismi di uso che si devono adottare per interfacciarsi con queste

librerie software.

12

Page 13: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

A livello di strumentazione mi sono stati forniti un PC con sistema operativo

Linux Fedora 10, per sviluppare e testare il mio lavoro, e alcuni modelli di

stampanti, scanner e Mini Player, da collegare al computer con i tre diversi tipi di

collegamento supportati.

2.3.1 Descrizione del lavoro di tesi

Questo lavoro di testi tratta principalmente della migrazione ed estensione da

“libreria C” (realizzata per prima) a “framework C++”, mostrando le operazioni

svolte per sfruttare il più possibile le caratteristiche del linguaggio orientato agli

oggetti e descrivendo le tecniche di analisi e progettazione utilizzate per lo scopo.

Inoltre si descriveranno le principali chiamate di sistema utilizzate per la

comunicazione a basso livello con i dispositivi, i dettagli sulla documentazione

creata a corredo del framework C++ e alcuni programmi di esempio che lo

utilizzano per mostrarne le possibili applicazioni pratiche.

2.4 Il protocollo ad alto livello dei prodotti Cus tomI collegamenti di cui sono dotati i dispositivi Custom Engineering sono la porta

seriale RS-232, la porta Ethernet (con protocollo TCP o UDP) o la porta USB

(quest'ultima con tre diversi tipi di interfacciamento logico). Tutti i prodotti, ad

eccezione di alcuni accessori, condividono un protocollo di comunicazione

proprietario, basato su sequenze di codici esadecimali dette ESC/POS™ (per

richieste speciali), dati testuali (per la stampa di testo) e dati binari (per la stampa

o l'acquisizione di immagini).

Va sottolineato che la gestione del significato di comandi e relative risposte non

è compito del framework realizzato, che lavora ad un livello più basso,

occupandosi del corretto invio dei dati e della corretta ricezione di eventuali

risposte. A titolo di esempio ecco alcuni dei principali comandi ESC/POS™

13

Page 14: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.4 Il protocollo ad alto livello dei prodotti Custom

esadecimali (ne esistono decine):

NOME SEQUENZA SIGNIFICATO

DLE_EOT 0x10, 0x04, 0x14 Richiesta informazioni sullo stato attuale deldispositivo. Risposta di 6 byte.

GSI 0x10, 0x04, 0x15 Richiesta informazioni compatte. Risposta di 1 byte.

HD_TEMP 0x1D, 0xDE Richiesta temperatura testina. Risposta di 6 byte.

FULL_CUT 0x1B, 0x6D Taglio della carta con la taglierina integrata.

EJECT 0xB0 Espulsione del ticket già tagliato (pochi modelli).

BAR_CODE 0x0A, 0x0A, …,0x00

Indica l'inizio di un codice a barre da stampare. La parte […] determina i valori del codice a barre.

Alcuni comandi sono disponibili per tutti i dispositivi della Custom Engineering,

mentre altri non lo sono: ad esempio i Mini Player non supportano i comandi di

taglio della carta, mentre l'espulsione dei ticket è possibile solo con i modelli che

stampano con un ulteriore arrotolamento della carta su un secondo rullo interno.

Inoltre, buona parte dei modelli è disponibile con tutti i tre tipi di collegamento al

PC su cui si è svolto questo lavoro, e quasi tutti i comandi sono identici per ogni

collegamento. Ci sono però alcune eccezioni, come per esempio l'acquisizione di

immagini con la Kube II Scanner, che è possibile con solo una delle interfacce

logiche disponibili sul collegamento USB, oppure via Ethernet o seriale.

2.5 Comunicazione Seriale, Ethernet, USB in LinuxOra si vedrà come è possibile comunicare con questi diversi tipi di collegamento

all'interno di un programma per i sistemi operativi della famiglia Linux, in cui i

dispositivi presenti sul PC (i “device” ) sono trattati come i file.

2.5.1 Comunicazione seriale

In ambiente Linux i dispositivi seriali sono normalmente identificati con nomi

del tipo /dev/ttySxxxx dove al posto della xxxx si trova un numero progressivo che

parte da 0 e arriva 9 (anche se di norma le porte seriali sono al massimo due, vale

14

Page 15: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

a dire /dev/ttyS0 e /dev/ttyS1).

Per l'accesso in fase di programmazione si possono utilizzare le chiamate di

sistema “tcsetattr()”, “open()”, “close()”, “write()”, “read()” e “select()”, incluse

nell'header file <fcntl.h>. Ecco i prototipi di queste funzioni di sistema e la

descrizione delle più interessanti:

int open(const char* pathname, int flags);

int tcsetattr(int fd, int flags, const struct termios* options);

int close(int fd);

ssize_t write(int fd, const void* buf, size_t count);

ssize_t read(int fd, void* buf, size_t count);

int select(int nfds, fd_set* readfds, fd_set* writefds,

fd_set* exceptfds, struct timeval* timeout);

La “open()”, il cui primo parametro dev'essere una stringa come “/dev/ttyS0”

ed il secondo una maschera di bit che permette di specificare opzioni quali la

modalità d'accesso (lettura/scrittura), in caso di successo restituisce un

identificatore per il dispositivo aperto, che sarà poi utilizzato con le altre funzioni.

La “tcsetattr()” permette di impostare le opzioni della porta seriale, cioè baud-

rate (velocità di trasferimento), controllo di parità, bit di stop, controllo di flusso e

lunghezza dei pacchetti. Questo avviene impostando opportunamente una struct

termios, definita nel file <termios.h> e composta di maschere di bit, e passandola a

questa funzione.

La “select()” è stata utilizzata in fase di lettura, con un timeout, per evitare che il

programma vada in blocco se non ci sono dati da leggere: infatti con la

comunicazione seriale i dati vengono subito trasferiti dal dispositivo fisico ad un

buffer interno al computer, che può essere letto con la funzione di sistema

“read()”, la quale rimane in attesa di dati finché non ce ne sono: senza l'uso della

“select()” si rischierebbe di bloccare il programma.

15

Page 16: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.5 Comunicazione Seriale, Ethernet, USB in Linux

2.5.2 Comunicazione Ethernet

In Linux la comunicazione Ethernet-LAN (applicabile a dire il vero anche a

connessioni senza fili Wi-Fi, se presenti sul sistema) utilizza i “socket”, che

identificano connessioni virtuali ad altri dispositivi sulla rete locale o Internet. Per

stabilire una connessione di rete tramite socket è necessario conoscere l'indirizzo

di rete (IP) o il nome dell'host del dispositivo a cui ci si vuole connettere, oltre al

numero di porta e al tipo di protocollo da utilizzare (TCP o UDP). I dispositivi

della Custom fanno da server, ed ascoltano per accettare connessioni, quindi

vedremo solo le funzioni di sistema lato client utilizzate, definite in <netdb.h> e

<sys/socket.h>, ovvero:

int socket(int socket_family, int socket_type, int protocol);

int connect(int sockfd, const struct sockaddr* serv_addr,

socklen_t addrlen);

int setsockopt(int s, int level, int optname, const void* optval,

socklen_t optlen);

int bind(int sockfd, const struct sockaddr* local_addr,

socklen_t addrlen);

Per quanto riguarda il protocollo TCP è necessaria la chiamata di

“socket(AF_INET, SOCK_STREAM, 0)” che restituisce il numero identificativo per

la connessione, da passare poi alle altre funzioni che dovranno utilizzarlo. È anche

necessaria una chiamata a “setsockopt()” con appositi parametri per attivare il

sistema di “keep-alive”, ovvero una tecnica che si occupa di mantenere attiva la

connessione col server (cioè col dispositivo della Custom Engineering) anche in

assenza di attività per lunghi periodi di tempo. La connessione TCP è bilaterale,

cioè il traffico può viaggiare dal computer al dispositivo e viceversa, ed è sempre

attiva: ciò garantisce il corretto ordine di ricezione dei pacchetti, perché seguono

tutti lo stesso percorso. La ricezione e l'invio di dati avvengono con le funzioni:

ssize_t recv(int socket, void* buf, size_t len, int flags);

ssize_t send(int socket, const void* buf, size_t len, int flags);

16

Page 17: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

A differenza del TCP, una connessione UDP è stabilita in modo unilaterale,

quindi per comunicare con i dispositivi Custom servono due socket: uno per

ricevere ed uno per inviare. Non è necessaria l'impostazione della tecnica di

“keep-alive”, che non esiste affatto dato che le connessioni UDP si attivano e

disattivano ad ogni trasferimento di dati. Le creazioni dei socket UDP avvengono

con due chiamate a “socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)” che

restituiscono due identificatori. Nel caso del socket per la ricezione serve poi una

chiamata a “bind()” che collega il nome al socket fisico, mentre per l'invio bisogna

distinguere due casi: una chiamata a “bind()” si usa in caso di normale

connessione verso un dispositivo, mentre per l'invio di dati come broadcast di rete

serve la chiamata:

setsockopt(iSocket, SOL_SOCKET, SO_BROADCAST, &iOpt, sizeof(int));

Il broadcast serve se si vuole che i dati siano inviati a tutti i dispositivi collegati

alla rete locale che si trovano in ascolto sulla porta UDP specificata e può essere

utile con certi dispositivi Custom come i Mini Player, per effettuare operazioni su

un numero elevato di essi. L'invio di dati in broadcast di norma non si effettua con

comandi che richiedono una risposta da parte dei dispositivi, in quanto per

ricevere le risposte si dovrebbero creare numerosi socket UDP. Per le connessioni

UDP la ricezione e l'invio di dati avvengono con le funzioni:

ssize_t recvfrom(int socket, void* buf, size_t length, int flags,

struct sockaddr* from, socklen_t* fromlen);

ssize_t sendto(int socket, const void* buf, size_t length, int flags,

const struct sockaddr* to, socklen_t tolen);

Sia per TCP, sia per UDP, si può impostare un “timeout” di ricezione per non

bloccare il programma in caso di mancata risposta da parte del dispositivo. Un

modo per farlo è il seguente, dove “Timeout” è una struct timeval:

setsockopt(iSocket,SOL_SOCKET,SO_RCVTIMEO,&Timeout,sizeof(Timeout));

Allo stesso modo si può impostare un timeout di scrittura per evitare blocchi del

17

Page 18: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.5 Comunicazione Seriale, Ethernet, USB in Linux

programma se c'è stata un'improvvisa disconnessione.

2.5.3 Comunicazione USB

I dispositivi della Custom Engineering supportano, in generale, tre tipi logici di

connessione USB, a seconda del modello e delle funzionalità che si vogliono

utilizzare, e che distingueremo con questi nomi:

• “interfaccia 0”: la comunicazione è a basso livello, molto simile ad un

driver, ed utilizza gli URB (USB Request Block) forniti dal kernel Linux;

• “interfaccia 1”: normale accesso tramite “device” di sistema, che hanno

nomi del tipo /dev/usb/lpxxxx dove la xxxx è un numero progressivo che

parte da 0;

• “interfaccia 2”: comunicazione di basso livello effettuata in questo

progetto tramite la libreria “libusb-1.0”.

Le comunicazioni con interfaccia 0 richiedono la selezione del dispositivo a cui

connettersi in base a VID (Vendor ID, numero di 2 byte che identifica il produttore

del dispositivo USB, che dovrebbe essere univoco su scala mondiale: 0DD4 in

esadecimale per la Custom Engineering), PID (Product ID, sempre di 2 byte, per

identificare il modello) e Serial Number (stringa testuale di lunghezza variabile

che cambia per ogni esemplare). Tutto ciò richiede una serie di operazioni molto

complesse, che a grandi linee sono queste: si richiede al sistema la lista dei

dispositivi USB presenti, si estraggono da ognuno VID, PID e Serial Number e li

si confrontano con quelli forniti dall'utente (può specificarne anche solo uno);

quando si trova una corrispondenza viene aperta una connessione tramite la

funzione di sistema “open()” passando come parametro una stringa che è il nome

di file che ha nel sistema il device corrispondente ai dati richiesti: questo nome

però non è noto all'utente. La funzione di sistema utilizzata per le operazioni di

18

Page 19: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2 Introduzione

lettura e scrittura con l'interfaccia 0 è la “ioctl()”, definita in <sys/ioctl.h>; eccone

il prototipo:

int ioctl(int id, int request, ...);

Il primo parametro è l'identificatore restituito dalla “open()”, mentre il secondo

deve essere la costante IOCTL_USB_READURB in caso di lettura oppure

IOCTL_USB_SUBMITURB in caso di scrittura; come terzo parametro si è utilizzato

l'indirizzo di una struct che contiene, in un opportuno formato, i dati trasferiti in

caso di lettura, ed i dati da trasferire in caso di scrittura; “ioctl()” non è una

funzione bloccante, per cui la gestione dei timeout di lettura o scrittura va

effettuata manualmente: in particolare per la lettura è necessario gestire

correttamente il timeout (ripetendo ad oltranza la lettura nei limiti di tempo

imposti, se non è stato letto ancora alcun dato) in quanto sull'interfaccia 0 le

risposte dei dispositivi Custom Engineering devono necessariamente essere lette

immediatamente dopo l'invio di dati, motivo per cui la lettura viene effettuata

subito nel programma, ed i dati vengono salvati in un buffer e forniti all'utente

solo quando anch'egli ne richiederà la lettura.

Con l'interfaccia 1 invece la comunicazione avviene in modo analogo alla porta

seriale, anche se non è necessario impostare parametri come la velocità di

trasferimento. Le funzioni di sistema da utilizzare sono quindi “open()”, “close()”,

“write()”, “read()” e “select()”.

Le comunicazioni con l'interfaccia 2 utilizzano le funzioni fornite dalla “libusb-

1.0”, che per lettura e scrittura si comportano in modo simile alle “read()” e

“write()” di sistema; la selezione del dispositivo con cui comunicare è invece

basata su VID, PID e Serial Number, come per l'interfaccia 0, ma

l'implementazione tramite “libusb-1.0” è completamente diversa e si vedrà nel

prossimo paragrafo.

19

Page 20: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

2.5 Comunicazione Seriale, Ethernet, USB in Linux

2.5.4 La libreria “libusb-1.0”

Questa libreria (www.libusb.org), fornita con licenza GNU Lesser General

Public License version 2.1, è scritta in linguaggio C e questo non la rende ideale

per l'utilizzo in un framework ad oggetti. Infatti non è possibile istanziare più volte

la “libusb” ma solo chiamare le sue funzioni. Fortunatamente gli sviluppatori

hanno previsto la possibilità di gestire più collegamenti contemporanei: questa

caratteristica è stata implementata con l'uso, in tutte le funzioni, di un parametro

identificativo (che nella versione C della libreria prodotta era lasciato a NULL in

quanto non serviva) che permette di indicare alla “libusb” su quale connessione

effettuare qualunque operazione. Questo parametro è un puntatore ad una struct di

tipo libusb_device_handle che è definita dalla “libusb”.

Le funzioni e le strutture dati fornite iniziano tutte col suffisso “libusb_”. Per

l'attivazione della connessione sono state utilizzate molte funzioni che servono ad

ottenere VID, PID e Serial Number, come “libusb_get_device_descriptor()”,

“libusb_get_string_descriptor_ascii()”, “libusb_open()” e “libusb_close()”, ma

non le vedremo nel dettaglio. La più interessante è la funzione di trasferimento dei

dati:

int libusb_bulk_transfer(struct libusb_device_handle *dev_handle,

unsigned char endpoint, unsigned char *data,

int length, int *transferred,

unsigned int timeout);

che si utilizza sia per la lettura sia per la scrittura, con la differenza che si deve

indicare un differente endpoint in base all'operazione da effettuare. I due endpoint

di lettura e di scrittura devono essere ricavati chiamando la funzione

“libusb_get_active_config_descriptor()” in fase di apertura della connessione, e

sono restituiti all'interno di una struct libusb_config_descriptor.

20

Page 21: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3 Il problema della migrazione da C a C++

3 Il problema della migrazione da C a C++

3.1 Motivazioni e vantaggi

Le principali motivazioni che hanno spinto l'azienda alla richiesta di una

versione in linguaggio C++ sono la volontà di fornire un software facilmente

estensibile, scritto in un linguaggio moderno orientato agli oggetti, e la necessità

di poter gestire senza complicazioni l'esistenza di più connessioni contemporanee.

La struttura a classi rende un software di interfacciamento come questo più

adatto ad essere utilizzato da altri programmi, rispetto ad una semplice libreria di

funzioni scritta in linguaggio C.

3.2 Framework software e librerie softwareLe librerie di funzioni, tipiche delle implementazioni procedurali con linguaggi

imperativi come il C, sono un insieme di funzioni fornite all'utente senza una

struttura che definisca che cosa può fare l'utente e quando può farlo, per cui sono

necessari maggiori controlli “manuali” nel codice per evitare un uso non corretto.

I framework si differenziano dalle librerie perché, grazie ad una struttura a classi

opportunamente realizzata, con interfacce separate, danno all'utente la possibilità

di effettuare solo le operazioni che egli dovrebbe poter fare in quel momento. In

pratica in un framework il flusso che ne definisce l'utilizzo dovrebbe essere

comandato dal framework stesso, invece che dal suo utente.

Inoltre una eventuale futura estensione per supportare altri dispositivi o

funzionalità è semplice: è sufficiente aggiungere le opportune classi e fare poche

modifiche all'interfaccia-utente esistente.

21

Page 22: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3.3 Le differenze nell'approccio

3.3 Le differenze nell'approccio

Si vedranno ora le principali differenze, a livello concettuale, tra i linguaggi di

programmazione C e C++, che sono state sfruttate nella realizzazione del

framework. Nei capitoli successivi si vedranno più nel dettaglio le loro

implicazioni pratiche.

3.3.1 Le classi

Le classi devono essere presenti in un linguaggio di programmazione perché

esso possa dirsi “orientato agli oggetti”.

Le classi sono la più importante estensione del linguaggio C++ rispetto al C:

sono simili alle strutture di dati struct già presenti nel C, ma possono contenere

funzioni, che prendono il nome di metodi, mentre le variabili prendono il nome di

attributi. Questo consente, a tempo di esecuzione, di avere degli oggetti di una

classe, i cui attributi possono essere manipolati dall'utente solo utilizzando i

metodi pubblici forniti dalla classe stessa.

La programmazione ad oggetti consente quindi una scomposizione del codice in

moduli (rappresentati dalle diverse classi) che interagiscono tra loro.

L'incapsulamento è un concetto secondo cui un oggetto, contenendo al suo

interno tutti i suoi metodi ed attributi, è visto dall'esterno come una scatola nera.

Solo i metodi definiti dalla classe come pubblici potranno essere visti ed utilizzati

dall'esterno. In linguaggio C è sempre possibile utilizzare una funzione con dati

anche mal formati, forniti dall'utente; invece in C++, programmando ad oggetti,

sono i metodi della classe che devono occuparsi del mantenimento di uno stato

ben formato e questo rende più facile l'utilizzo corretto del software dall'esterno e

più difficile, o meglio impossibile, il raggiungimento di stati inconsistenti. Una

classe può incapsulare al suo interno anche altre classi, che non sono viste

22

Page 23: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3 Il problema della migrazione da C a C++

dall'esterno, e gestirne direttamente le istanze a tempo di esecuzione.

L'ereditarietà è ciò che permette di creare classi derivate da altre, che ne

ereditano tutte le caratteristiche, ne possono modificare una parte e ne possono

aggiungere altre. Una classe “B” eredita da una classe “A” quando si può dire che

ogni oggetto o istanza di tipo “B” è anche di tipo “A”.

Il polimorfismo si applica al concetto di ereditarietà e consente di ottenere, in

esecuzione, la possibilità di inviare lo stesso messaggio (chiamare lo stesso

metodo) a diversi oggetti di classi derivate da una classe comune ed ottenere un

comportamento differente (a causa della differente implementazione dello stesso

metodo nelle diverse classi).

Per l'allocazione di istanze (oggetti) di classi viene utilizzato il relativo metodo

costruttore, che permette di inizializzare gli attributi e svolgere alcune operazioni

di avvio in automatico. Una classe può avere più di un costruttore (grazie

all'overloading).

Il distruttore è invece il metodo che si occupa dell'eliminazione di ogni oggetto

di una classe e può essere definito in modo personalizzato quando è necessario

liberare aree di memoria istanziate dinamicamente dalla classe.

Le classi possono anche essere copiate o duplicate tramite il costruttore di copia

o l'overloading dell'operatore di assegnamento (caratteristiche che non sono state

utilizzate in questo framework perché non adatte).

3.3.2 L'overloading dei metodi

L'overloading (sovraccaricamento) dei metodi è ciò che consente di avere più di

un metodo con lo stesso nome, ma con tipo o numero di parametri differente: il

compilatore determinerà quale metodo invocare in base alla rispondenza dei

parametri con uno dei metodi che hanno lo stesso nome.

23

Page 24: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3.3 Le differenze nell'approccio

3.3.3 I namespace

Un problema del linguaggio C nella realizzazione di librerie di funzioni è il fatto

che tutte le funzioni, variabili globali ed anche define, a tempo di esecuzione si

trovano nello stesso spazio dei nomi: ad esempio se la libreria sarà utilizzata da

un'altra applicazione che ha una funzione con lo stesso nome di una funzione della

libreria, la compilazione risulterà impossibile e costringerà alla modifica del

codice.

In C++ parte del problema, se si programma strettamente ad oggetti, è risolto

perché i metodi appartengono solo alle istanze della loro classe; invece per

eventuali variabili globali e per le define si possono utilizzare i namespace, che

sono dei contenitori che limitano ad una parte di programma la validità dei nomi.

3.3.4 I reference

Un reference (riferimento) è la possibilità offerta dal C++ di riferirsi ad un

oggetto con un altro nome, oltre a quello originale, ed è utile come alternativa ai

puntatori per il passaggio di parametri se il metodo li deve poter modificare.

All'interno del metodo che riceve il parametro per riferimento, questo viene

utilizzato come se fosse un attributo locale.

3.3.5 I casting

Nel linguaggio C++ è stato aggiunta una nuova sintassi per le conversioni dei

tipi di dati (casting), con quattro diverse tipologie:

• const_cast: cambia l'essere o non essere “const” di un oggetto;

• static_cast: cambia il tipo controllando la validità solo a compile-time;

• dynamic_cast: come lo static_cast, ma controlla a tempo di esecuzione

che la qualificazione sia corretta;

24

Page 25: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3 Il problema della migrazione da C a C++

• reinterpret_cast: cambia il tipo del dato in un altro tipo, anche puntatori

con non puntatori ad esempio.

3.3.6 Le eccezioni

Le eccezioni sono un'importante strumento che consente di gestire la consistenza

degli attributi di una classe, e sono un'alternativa alla gestione manuale degli

errori. Quando si verifica un evento imprevisto l'istanza della classe può lanciare

un'eccezione (identificata da un nome) e terminare. L'eccezione viene propagata

all'oggetto che conteneva quello terminato, che può intercettarla (ed intraprendere

una certa azione, compreso eventualmente propagarla al livello superiore come

una diversa eccezione) oppure non gestirla ed essere terminato a sua volta, il che

può portare fino alla terminazione dell'intero programma.

3.3.7 La Standard Template Library

La STL è una libreria inclusa nel linguaggio C++ che fornisce contenitori,

iteratori, algoritmi e oggetti funzione (oggetti con un solo metodo utilizzabili

come chiamate a funzione sfruttando il loro costruttore).

Nel framework realizzato sono stati utilizzati i contenitori “map” e “vector” e gli

“iterator”:

• le “map” (mappe) sono contenitori associativi ordinati di chiavi uniche,

associate a dei dati: in pratica ogni elemento della mappa è una coppia

<“indice”, “dato”>; le mappe sono di dimensione variabile, l'indice

(intero) è univoco e il tipo di “dato” è uniforme all'interno di una mappa.

• i “vector” sono contenitori che implementano array dinamici, ovvero che

possono variare la propria dimensione, e possono contenere qualunque

tipo di dato; i “vector” sono uniformi: tutti gli elementi di un “vector”

sono dello stesso tipo ed è possibile accedervi anche grazie all'overloading

25

Page 26: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3.3 Le differenze nell'approccio

dell'operatore “[indice]”, implementato dalla STL;

• gli “iterator” sono oggetti che permettono di spostarsi all'interno di

contenitori: dopo aver istanziato un iteratore è possibile utilizzarlo per

accedere ai diversi elementi del contenitore agendo sull'iteratore come se

si trattasse di un indice intero, ad esempio incrementandolo ed usandolo

per controllare cicli. Ecco come si può dichiarare e definire un iteratore

che punta al primo elemento di una mappa:

MyMapType::iterator i = MyMapInstance.begin();

3.4 UMLL'Unified Modeling Language è un linguaggio di modellazione nato per

supportare la progettazione di software orientato agli oggetti ed ha assunto un

ruolo chiave nell'ingegneria del software, evolvendosi assieme ai linguaggi di

programmazione ad oggetti e giungendo oggi alla versione 2.3.

I tipi di diagrammi sono suddivisibili in due macro-categorie:

• strutturali (o statici): mostrano la struttura statica di un sistema, le classi

che lo compongono, la loro composizione e le relazioni statiche tra esse;

• comportamentali (o dinamici): mostrano il comportamento del sistema in

esecuzione, sia rappresentando le collaborazioni con l'utente, sia quelle

interne al sistema.

Attualmente UML definisce ben 14 tipi di diagrammi, suddivisi equamente tra le

due categorie appena descritte. Nei capitoli 4 “Analisi funzionale” e 5

“Progettazione del framework C++” si vedranno i tipi di diagramma UML

utilizzati per questo software durante tali fasi dello sviluppo.

26

Page 27: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3 Il problema della migrazione da C a C++

3.5 Design patternNella progettazione dei software ad oggetti è facile incontrare problemi

ricorrenti, per risolvere i quali è utile trovare soluzioni riutilizzabili: a questo

servono i design pattern. Sono stati definiti nel libro “Design Patterns: Elements

of Reusable Object-Oriented Software” da un gruppo di quattro progettisti

software (la “Gang of Four”) nel 1991.

Con i design pattern è possibile trovare un aiuto per la strutturazione del

progetto, riducendo il tempo necessario alla progettazione ed evitando la possibile

creazione di soluzioni azzardate con architetture poco consistenti o che non

rispettano i paradigmi della programmazione orientata agli oggetti.

Un pattern deve essere definito dettagliando almeno queste caratteristiche:

• Nome

• Tipo di problema che risolve, con eventuali esempi noti

• Struttura e descrizione della soluzione che fornisce

• Conseguenze del suo impiego

Inoltre dovrebbe anche fornire suggerimenti per l'implementazione, codice di

esempio in vari linguaggi di programmazione e i nomi di alcuni pattern correlati.

Le categorie in cui si possono suddividere i design pattern sono tre:

• creazionali: gestiscono la creazione dinamica degli oggetti nel sistema;

• strutturali: permettono il riutilizzo di micro-architetture statiche;

• comportamentali: descrivono il comportamento di un insieme di oggetti.

Nel capitolo 5 si vedranno i design pattern utilizzati nel framework prodotto.

27

Page 28: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

3.5 Design pattern

28

Page 29: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4 Analisi funzionale

4 Analisi funzionale

L'analisi funzionale è la prima fase del ciclo di vita di un software, e si occupa di

definire le specifiche del progetto da realizzare, partendo dai requisiti di

funzionamento: in questo caso sono stati definiti all'interno dell'azienda, in base

all'esperienza che il marketing ha riportato riguardo le richieste dei clienti.

Questa fase include anche lo studio di fattibilità, che in questo caso era già stato

fatto per la versione C, in cui è stata raccolta la documentazione necessaria per

utilizzare le funzioni di sistema, che poi sono state usate per implementare le

comunicazioni tramite le porte seriale, Ethernet ed USB.

4.1 Requisiti di funzionamentoIl software realizzato doveva principalmente presentarsi come una libreria a

collegamento dinamico che permettesse all'utente di interfacciarsi con le

stampanti, gli scanner e gli altri prodotti della Custom Engineering, senza doversi

preoccupare del tipo di collegamento utilizzato (seriale, Ethernet-UDP, Ethernet-

TCP o USB, in quest'ultimo caso con tre diversi tipi di interfacce), fatto salvo

naturalmente per la scelta in fase di inizializzazione del collegamento.

Ad esempio un programmatore che dovesse trovarsi a sviluppare un'applicazione

con interfaccia grafica per comunicare con i prodotti della Custom, non dovrà

preoccuparsi di quale porta sarà utilizzata per le comunicazioni: dovrà solo

predisporre la possibilità di scegliere la porta da usare (o anche solo forzarne un

determinato tipo): il framework ComLayer C++ gestirà la comunicazione a basso

livello, senza mostrare, al livello superiore, differenze al variare della connessione

fisica utilizzata.

29

Page 30: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4.1 Requisiti di funzionamento

Un altro requisito era la possibilità di poter connettere più dispositivi

contemporaneamente allo stesso PC o sistema embedded, eventualmente su porte

di tipo differente: riguardo questo aspetto la versione C++ ha presentato in

maniera naturale la possibilità di istanziare più volte la classe principale del

framework realizzato. Tuttavia si è scelto di integrare direttamente nella classe

“ComLayer” la possibilità di gestire autonomamente un numero arbitrario (nei

limiti fisici e software della macchina) di connessioni contemporanee, attraverso

appositi metodi che utilizzano alcuni tipi di dato della C++ Standard Template

Library (std::vector, std::map), in cui vengono memorizzate le istanze

corrispondenti ai diversi collegamenti. Nel capitolo 7.2, riguardante le

applicazioni di esempio, si vedranno dei programmi in grado di sfruttare anche

questa caratteristica.

La versione che sarà fornita ai clienti della Custom Engineering non prevederà la

consegna del codice sorgente, ma solamente della libreria “libComLayer.so” già

compilata e dell'header file “ComLayer.h”, oltre ovviamente alla documentazione,

di cui si parlerà più avanti.

4.2 La scelta di non realizzare un driverGià prima dello sviluppo della versione C si è scelto di non realizzare un driver,

che potrebbe invece sembrare la scelta più logica visto che stiamo parlando

principalmente di stampanti e scanner, ma non è così.

I motivi sono i seguenti:

• La differente tipologia dei prodotti con cui interfacciarsi (stampanti,

scanner, mini-player per la riproduzione di banner pubblicitari, etc...) che

hanno comandi in comune, ma anche comandi differenti, che avrebbero

causato la necessità di creare tanti driver diversi;

30

Page 31: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4 Analisi funzionale

• La possibilità di funzionare comunicando con prodotti futuri non ancora

sviluppati;

• Il fatto che i prodotti Custom Engineering hanno comandi di

formattazione integrati e la possibilità di avere loghi grafici pre-caricati da

richiamare semplicemente con un'istruzione senza appesantire il lavoro

per il sistema: questo tipo di comandi è di facile utilizzo anche senza un

driver;

• Il funzionamento in sistemi “embedded”, ovvero molto specifici per scopi

industriali o commerciali, dotati a volte di poca potenza e quindi di

distribuzioni Linux piuttosto scarne: il framework così realizzato non ha la

necessità di appoggiarsi ad un gestore delle code di stampa come CUPS,

che potrebbe non essere presente nel sistema;

• Il fatto che non sono stampanti e scanner di uso comune ma sono da

utilizzarsi in ambito professionale e specializzato;

• La richiesta da parte di molti clienti di avere il controllo diretto della porta

sia per motivi prestazionali (i driver sono più lenti) sia di compatibilità.

4.3 Modalità di utilizzo del frameworkIn base allo studio effettuato, con l'aiuto della versione “Windows” fornitami

dall'azienda, è stato stabilito nel dettaglio come l'utenza del framework avrebbe

dovuto interfacciarsi con esso, ovvero come avrebbe dovuto fare per comunicare

con i prodotti della Custom Engineering utilizzando questo prodotto software.

Tutte le informazioni di utilizzo, di cui si tratterà sinteticamente in questo

paragrafo e nei successivi, sono disponibili nella documentazione in formato

HTML che è stata generata per i clienti dell'azienda, ed è fondamentale per poterla

utilizzare.

31

Page 32: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4.3 Modalità di utilizzo del framework

Il framework “ComLayer C++”, come già detto, permette di comunicare su tre

diversi tipi di connessione fisica, alcuni dei quali hanno diversi tipi di connessione

logica.

La classe “ComLayer” è quella che dovrà essere istanziata dall'utente del

framework e fornisce tutti i metodi necessari per gestire le connessioni e per

comunicare tramite tutte le porte supportate dai prodotti Custom Engineering:

buona parte di questi metodi sono in comune a tutti i tipi di collegamento (ad

esempio i metodi di apertura, chiusura, lettura, scrittura, impostazione timeout),

mentre altri sono specifici per un singolo collegamento (fondamentalmente i

metodi di inizializzazione/configurazione della connessione, da invocare prima di

tentare l'apertura vera e propria della comunicazione).

Per quanto riguarda i metodi comuni, la classe “ComLayer” va a comportarsi

allo stesso modo con tutte le connessioni, ovvero svolge il ruolo di interfaccia tra

l'utente ed il livello inferiore, utilizzando i metodi dichiarati nella classe astratta

“ComBase”, da cui sono state derivate le classi “ComSerial”, “ComEth” e

“ComUsb”; queste implementano in modo differente i metodi virtuali della classe

base, alcuni dei quali sono virtuali puri (ovvero devono necessariamente essere

implementati da ogni classe derivata), mentre altri sono virtuali semplici (ovvero

possono essere re-implementati da ogni classe derivata).

Riguardo, invece, le funzionalità specifiche per i vari tipi di connessione, la

classe “ComLayer” implementa metodi appositi che si interfacciano con i

corrispondenti metodi delle tre classi derivate dalla “ComBase”; questi metodi non

sono stati dichiarati nella classe base “ComBase”, ma sono aggiunti dalle diverse

classi derivate. La classe “ComLayer” gestisce al suo interno le istanze delle classi

“ComSerial”, ComEth” e “ComUsb” (derivate dalla “ComBase”), ed è quindi in

grado di verificare (con dynamic_cast) se una chiamata da parte dell'utente è

corretta o meno: ad esempio è restituito errore nel caso in cui un utente invochi il

32

Page 33: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4 Analisi funzionale

metodo di inizializzazione per porta seriale su di una connessione Ethernet, dato

che quel metodo non esiste affatto nelle istanze della classe “ComEth”.

4.3.1 UML: Use-case diagram

Questo diagramma “dei casi d'uso” in UML rappresentata come l'utente

(“attore”, in questo caso si tratta di un altro software) del framework (“sistema”)

interagisce con esso. E' anche presente una minimale schematizzazione dei

processi interni conseguenti all'azione dell'utente.

A questi diagrammi è associata una descrizione tabellare per ogni tipo d'azione.

In questo caso c'è sempre un solo attore, quindi sarà omesso nelle descrizioni:

• Caso d'uso: “crea porta”. Tipo: “essenziale”.

Descrizione: l'utente crea una nuova porta indicandone il tipo ed eventuali

dettagli riguardanti il file di log, che è facoltativo.

Azione attore Risposta sistema1. Richiede la creazione di una porta,specificandone il tipo (seriale, Ethernet oUSB) e le eventuali impostazioni del log.

33

Page 34: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4.3 Modalità di utilizzo del framework

2. Crea la porta, la rende attiva e salva leimpostazioni per l'eventuale file di log.

• Caso d'uso: “elimina porta”. Tipo: “primario”.

Descrizione: l'utente elimina una porta esistente indicando l'identificatore.

Azione attore Risposta sistema1. Richiede di eliminare una porta,specificandone l'id univoco.

2. Distrugge tutte le istanze associate allaporta. Eventuali connessioni attivevengono chiuse in modo non corretto.

• Caso d'uso: “seleziona porta”. Tipo: “primario”.

Descrizione: l'utente sceglie quale porta rendere attiva, se ne esiste più di

una, per effettuarvi comunicazioni.

Azione attore Risposta sistema1. Scelta della porta tramite l'id univoco.

2. Cambia la porta attiva per operazionidi configurazione e comunicazione.

• Caso d'uso: “configura porta”. Tipo: “essenziale”.

Descrizione: l'utente invia informazioni di configurazione per la porta

attiva, anche modificandola se già configurata in precedenza, ma non è

possibile cambiarne il tipo (ad esempio da USB a seriale).

Azione attore Risposta sistema1. Invia parametri di configurazione comeil nome del device con cui comunicare.

2. Salva la configurazione relativamentealla porta attiva, per le successive fasi dicomunicazione con essa

3. Se il log è attivo registra su log-file.

34

Page 35: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4 Analisi funzionale

• Caso d'uso: “comunica con la porta”. Tipo: “essenziale”.

Descrizione: l'utente effettua operazioni di scrittura e lettura sulla porta

attualmente attiva.

Azione attore Risposta sistema1. Invia dati posti in un buffer,specificando la quantità da inviare, oppurerichiede una lettura di dati dal dispositivoindicando un buffer e la sua dimensione.

2. Tenta invio o ricezione dei dati coldispositivo (seriale, Ethernet o USB)

3. Se il log è attivo registra su log-file.

4.3.2 UML: Sequence diagram

“Comunica con la porta” è un'attività

che è stata semplificata nel diagramma

precedente e la si vede in dettaglio in

questo sequence diagram, che mostra

come avviene una fase di

comunicazione con la porta, tra utente e

framework (rappresentato dalla classe

“ComLayer”).

4.3.3 UML: Activity diagram

Il seguente diagramma d'attività è stato sviluppato per evidenziare l'ordine con

cui le azioni possono essere svolte dall'utente del framework.

35

Page 36: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

4.3 Modalità di utilizzo del framework

All'inizio si deve necessariamente creare almeno

una nuova porta per poter eseguire altre operazioni,

dopodiché l'ordine degli eventi è molto flessibile: ad

esempio si può configurare una porta ma attivarne o

crearne un'altra prima di effettuare comunicazioni.

I rombi con più di una freccia in uscita indicano le

scelte (da parte dell'utente), mentre quelli con più di

una freccia in entrata indicano ricongiunzioni di

eventi.

36

Page 37: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

5 Progettazione del framework C++

La seconda fase prevista nel ciclo di vita di un software, dopo l'analisi, è la

progettazione, in cui si utilizzano i requisiti definiti nella fase precedente per

stabilire il modo in cui raggiungere lo scopo, a livello di struttura del software e di

caratteristiche comportamentali dei suoi componenti.

Un ruolo importante per la scelta della struttura del framework è stato svolto

dalla versione in linguaggio C precedentemente realizzata, che suddivideva

l'implementazione del codice in un file di interfacciamento “ComLayer.c”, un file

per ogni tipo di porta (più un altro file per l'interfaccia 0 USB) ed un file per il log

(senza contare gli header file).

Partendo da questa base si è cercato di ottenere un framework C++ rispondente

alle caratteristiche della programmazione orientata agli oggetti, per cui sono stati

utilizzati “Design Pattern” e diagrammi “UML” allo scopo di trasformare la

semplice struttura della versione C, senza complicarla più del necessario, per

arrivare ad un insieme di classi ben strutturato.

Come già detto, nella versione C non era fornita la possibilità di gestire più

connessioni contemporanee e si è scelto di inserirla nella versione C++ sfruttando

la struttura ad oggetti e i tipi di dato forniti dalla C++ Standard Template Library.

5.1 Scelte architetturali con l'uso di Design Patt ernIn fase di progettazione, alcuni aspetti del framework sono stati ricondotti a

forme tipiche definite in alcuni Design Pattern noti, aiutando così a definire in

modo semplice e convenzionale il modo in cui implementare le relazioni tra le

classi.

37

Page 38: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.1 Scelte architetturali con l'uso di Design Pattern

5.1.1 Façade

Questo design pattern di tipo strutturale, il cui nome significa “facciata”,

definisce come creare un'interfaccia uniforme per un insieme di interfacce

correlate tra loro.

Ciò significa, nella programmazione orientata agli oggetti, che si dovrà creare

una classe che farà da interfaccia (layer) tra l'utente e le classi che ereditano da

una classe comune (presumibilmente astratta).

Nel caso del framework realizzato si aveva la necessità di fornire all'utente

un'interfaccia simile per comunicare su tipi di connessione differente, anche

fisicamente, per cui è stata creata la classe “ComLayer” che avrebbe dovuto fare

da interfaccia per l'utente con le classi “ComSerial”, “ComEth” e “ComUsb”, che

ereditano da “ComBase”.

Un accesso di tipo normale ai tre tipi di porta si potrebbe

schematizzare con il diagramma a destra: il client ha una

dipendenza da tutti e tre i tipi di porta differenti, e deve

comunicare direttamente con essi in modo differenziato.

Invece, con il pattern “façade” si aggiunge una

classe (in questo caso “Layer”) e si ottiene un

accesso semplificato da parte del client, che può

accedere alle risorse di Serial, Ethernet e Usb in

modo uniforme, dipendendo solo da Layer.

5.1.2 Lazy Initialization

Questo pattern comportamentale consiste nell'istanziare un oggetto di una classe

solo quando esso è effettivamente richiesto.

38

Page 39: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

In questo caso si applicata la “lazy initialization” all'implementazione del

sistema di log su file, la cui istanza viene creata solamente quando viene attivato il

log con l'apposito metodo “StartLog()”, e solo se era stato configurato durante la

creazione della porta.

Per l'implementazione è stata inserita, nella classe astratta “ComBase”, una

verifica sull'effettiva esistenza dell'istanza della classe “ComLog” ad ogni

chiamata di metodi di questa classe; se oggetto non esiste (il suo puntatore è

NULL), non viene eseguito nulla. É un'implementazione alternativa a quella

standard, che prevede di istanziare l'oggetto appena la classe che lo contiene ne ha

bisogno: in questo caso invece l'istanziazione avviene quando è richiesta la prima

attivazione del log da parte dell'utente.

5.2 Struttura del framework con uso di UMLIn questa fase i diagrammi UML sono utili per stabilire e documentare con

precisione quali classi comporranno il progetto, che metodi e che attributi esse

conterranno, quali relazioni le legheranno e in che modo interagiranno tra loro.

In questo caso è stato realizzato un “Class diagram”, che descrive le classi e le

relazioni tra esse, vale a dire ereditarietà (derivazione) e contenimento.

Inoltre sono stati realizzati numerosi “Communication diagram”, che permettono

di definire come le classi dovranno comunicare tra loro per svolgere le diverse

azioni richieste dall'utente.

39

Page 40: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.2 Struttura del framework con uso di UML

5.2.1 UML: Class diagram

Il class diagram UML qui visualizzato è una rappresentazione di tutte le classi

che compongono il framework progettato; per migliorare la leggibilità, per ogni

classe sono mostrati solo alcuni dei metodi protected (nel caso della classe astratta

“ComBase”, che è l'unica da cui altre classi ereditano) e public, mentre sono stati

omessi gli attributi, che sono quasi tutti protected (“ComBase”) o private.

La classe “ComLayer” è quella che dev'essere istanziata dall'utente del

framework e contiene tutti i metodi public che servono per usare ogni funzionalità,

dalla gestione delle connessioni, alle impostazioni specifiche per le varie porte, per

arrivare alle funzionalità di invio e ricezione dei dati. L'elenco completo dei

metodi pubblici forniti da “ComLayer” è riportato nel capitolo 5.3: “Elenco dei

metodi forniti all'utente”. La classe “ComLayer” si comporta quindi anche da

layer (ovvero da traduttore) tra l'utente e le varie connessioni, ed è in grado di

40

Page 41: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

istanziare, memorizzando nell'attributo privato “portsMap”, un numero arbitrario

di oggetti derivati dalla classe astratta “ComBase”, che si dice quindi essere

contenuta nella classe “ComLayer” (ciò è indicato dal rombo vuoto con freccia

verso la classe contenuta). Dato che si utilizzano puntatori, si parla di

aggregazione.

Nel diagramma appaiono altre due relazioni di tipo contenitivo con

aggregazione (uso di puntatori), che però ammettono una sola istanza della classe

contenuta: queste classi sono la “ComLog” e la “ComUsbIF0”. La classe

“ComLog” è contenuta in “ComBase” e la sua istanza è protected, scelta obbligata

per renderla accessibile alla classe “ComUsb”, che deve renderla disponibile alla

classe contenuta “ComUsbIF0”. Quest'ultima aggregazione è stata realizzata per

mantenere separato il codice di gestione della “interfaccia 0 USB” dal resto del

codice per l'USB, in quanto sia l'utilizzo reale sia l'implementazione differiscono

in modo significativo. Nel caso di utilizzo della “interfaccia 0 USB”, quindi, la

classe “ComUsb” si occupa dell'istanziazione di “ComUsbIF0” e di fare da layer

tra quest'ultima e “ComLayer”.

La classe “ComBase” è astratta, motivo per cui il suo nome è scritto inclinato

nel grafico UML, poiché possiede dei metodi virtuali puri che devono essere

implementati da ogni classe derivata. La derivazione (ereditarietà), che indica una

specializzazione, è rappresentata con una freccia vuota che va dalle classi

specializzate alla classe base. Il suo significato è che una classe specializzata è un

sotto-tipo della classe da cui deriva. In questo framework ci sono tre classi che

ereditano da “ComBase”, e sono “ComSerial”, “ComEth” e “ComUsb”. È stata

utilizzata l'ereditarietà pubblica, poiché nella classe base sono presenti dei metodi

pubblici che devono restare tali anche nelle istanze delle classi derivate.

È da notare la presenza di alcuni distruttori nel diagramma, identificati con nomi

che iniziano con “~”, che indica che è stato scritto un distruttore personalizzato

41

Page 42: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.2 Struttura del framework con uso di UML

per la classe; non sono stati inclusi i distruttori delle classi “ComSerial”,

“ComEth” e “ComUsbIF0” in quanto esse non istanziano altre classi né aree di

memoria di dimensione variabile, quindi non hanno nulla da de-istanziare quando

vengono distrutte. Un'importante considerazione è che il distruttore, sebbene

debba essere dichiarato virtual per una classe astratta, non viene sovrascritto dai

distruttori delle classi da essa derivate (in effetti hanno nome diverso), ma viene

eseguito dopo l'esecuzione del distruttore della classe derivata. Ad esempio in

questo framework, quando viene distrutta un'istanza della classe “ComUsb”, sarà

chiamato il suo distruttore e poi il distruttore della classe “ComBase”.

5.2.2 UML: Communication diagram

Questo è il diagramma che mostra le collaborazioni tra le classi interne al

framework quando l'utente richiede l'inizializzazione di una porta USB nella

modalità “interfaccia 0”, con il sistema di log attivo:

I numeri indicano l'ordine con cui avvengono le comunicazioni tra le classi,

mentre l'etichetta identifica il metodo della classe destinataria del messaggio,

utilizzato per lo scopo.

Diagrammi di questo tipo possono essere decine per un progetto di questo tipo,

per cui si è scelto di riportare solamente questo a scopo dimostrativo.

42

Page 43: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

5.3 Elenco dei metodi forniti all'utenteAl termine della fase di progettazione è già definito il dettaglio dei metodi che

comporranno le classi. Ora si vedrà l'elenco dei metodi a disposizione dell'utente

del framework, ovvero i metodi pubblici forniti dalla classe “ComLayer”, divisi

per tipologia di operazione.

5.3.1 Istanziazione del framework

Il costruttore non accetta parametri in quanto il framework viene istanziato

sempre allo stesso modo. Riguardo il costruttore di copia e l'operatore di

assegnamento bisogna sottolineare che essi non sono forniti all'utente, in quanto il

concetto di duplicazione applicato ad una connessione fisica con un dispositivo

non ha senso; comunque, anche nel caso in cui si fosse deciso di fornire questa

possibilità, dato che il codice scritto fa uso di occupazione dinamica della

memoria, sarebbe stato necessario implementare la copia manuale delle aree di

memoria, cosa non semplice e rischiosa a livello di sicurezza del software.

Ecco i semplici prototipi del costruttore e del distruttore:

ComLayer();

~ComLayer();

5.3.2 Gestione di connessioni multiple contemporanee

Riguardo la creazione, l'eliminazione e la selezione delle connessioni, i metodi

messi a disposizione dell'utente sono:

int CreateCommunicationPort(int iPortType, const char* strLogFile,

int iLogVerbosity, bool bLogPrintDate,

unsigned int& iId);

int CreateCommunicationPort(int iPortType, const char* strLogFile,

int iLogVerbosity, bool bLogPrintDate);

43

Page 44: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.3 Elenco dei metodi forniti all'utente

int CreateCommunicationPort(int iPortType, unsigned int& iId);

int CreateCommunicationPort(int iPortType);

void GetCommunicationPorts(CommunicationPortsVector&);

int SelectCommunicationPort(unsigned int);

unsigned int GetCurrentCommunicationPortId();

int DeleteCommunicationPort(unsigned int);

Si noti che ci sono quattro metodi che hanno lo stesso nome ma numero di

parametri diverso, sfruttando l'overloading permesso dal C++. In linguaggio C

sarebbe stato necessario utilizzare quattro nomi differenti.

5.3.3 Inizializzazione di comunicazioni seriali

Metodi specifici per configurare le porte create con tipo seriale; se la porta

attualmente attiva non è di tipo seriale, viene restituito errore:

int InitSerial(const char* strPortName, int iBaudRate,

int iDataLength, int iParity, int iStop,

int iFlowControl);

int InitSerialStrings(const char* strPortName,

const char* strBaudRate,

const char* strDataLength,

const char* strParity, const char* strStop,

const char* strFlowControl);

int InitSerialAutoBaudRate();

size_t GetMaxSerialPacketSize();

void SetMaxSerialPacketSize(size_t iSz);

int GetSerialParameters(char* strPortName, int* iBaudRate,

int* iDataLength, int* iParity,

44

Page 45: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

int* iStop, int* iFlowControl);

int GetSerialParametersStrings(char* strPortName, char* strBaudRate,

char* strDataLength, char* strParity,

char* strStop, char* strFlowControl);

int GetSerialSignals(int* iPtrStatus);

void SetSerialDTR(bool bFlag);

void SetSerialRTS(bool bFlag);

5.3.4 Inizializzazione di comunicazioni Ethernet

Metodi specifici per configurare le porte create con tipo Ethernet; nel caso in cui

la porta attualmente attiva non sia Ethernet, viene restituito errore:

int InitEthTCP(const char* strIPAddress, int iIPPort);

int InitEthTCPStrings(const char* strIPAddress,

const char* strIPPort);

int InitEthUDP(const char* strIPAddress, unsigned int iPortSend,

unsigned int iPortRecv);

int InitEthUDPStrings(const char* strIPAddress,

const char* strPortSend,

const char* strPortRecv);

int GetEthTCPParameters(char* strAddress, unsigned int* iPtrPort);

int GetEthTCPParametersStrings(char* strAddress, char* strPort);

int GetEthUDPParameters(char* strAddress,

unsigned int* iPtrPortSend,

unsigned int* iPtrPortRecv);

int GetEthUDPParametersStrings(char* strAddress,

char* strPortSend,

char* strPortRecv);

45

Page 46: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.3 Elenco dei metodi forniti all'utente

size_t GetMaxEthPacketSize();

void SetMaxEthPacketSize(size_t iSz);

5.3.5 Inizializzazione di comunicazioni USB

Metodi specifici per configurare le porte create con tipo USB; nel caso in cui la

porta attualmente attiva non sia USB, viene restituito errore:

int InitUsb(const char* strUsb); // interface 1

int InitUsbIf2(int iVID, int iPID, const char* strSN);

int InitUsbIf0(int iVID, int iPID, const char* strSN);

int GetUsbParameters(char* strName); // interface 1

int GetUsbIf2Parameters(int* iPtrVID, int* iPtrPID, char* strSN);

int GetUsbIf0Parameters(int* iPtrVID, int* iPtrPID, char* strSN);

size_t GetMaxUsbPacketSize(); // interface 1

void SetMaxUsbPacketSize(size_t iSz); // interface 1

size_t GetMaxUsbIf2ReadPacketSize();

size_t GetMaxUsbIf2WritePacketSize();

I metodi per l'interfaccia 1 (quella “default”, tramite device di sistema) non

portano nel nome un identificatore, per cui è stato aggiunto un commento a fianco.

5.3.6 La comunicazione vera e propria coi dispositivi

Questi metodi sono validi qualunque sia il tipo di porta attiva, ed il

comportamento effettivo ottenuto sul dispositivo con cui si comunica è

equivalente, ma la loro implementazione può ovviamente essere diversa per ogni

tipo di porta. Ecco l'elenco:

46

Page 47: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5 Progettazione del framework C++

int Open();

int Close();

int Write(const unsigned char* strBuffer, size_t iMax,

size_t* iPtrWritten);

int Write(const unsigned char* strBuffer, const size_t iMax);

int WriteFile(const char* strFileName);

int Read(unsigned char* strBuffer, const size_t iMax,

size_t* iPtrRead);

int StartLog();

int StopLog();

int ChangeVerbosity(int iVerbosity);

int GetVerbosity();

int GetCommunicationPortType();

int GetConnectionStatus();

int GetReadTimeout(struct timeval* ptrTimeout);

int SetReadTimeout(const struct timeval* ptrTimeout);

int Clean();

“WriteFile()” merita un piccolo appunto: si tratta di un metodo in grado di

inviare un interno file (di comandi o un'immagine da stampare) al dispositivo,

implementato nella classe “ComBase()” e che fa uso di numerose chiamate a

“Write()” per completare l'operazione.

47

Page 48: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

5.3 Elenco dei metodi forniti all'utente

48

Page 49: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

6 Implementazione del framework C++

Dopo analisi e progettazione il ciclo di vita di un software prevede la fase di

implementazione, ovvero la realizzazione fisica del progetto nel linguaggio di

programmazione scelto, in questo caso il C++.

6.1 I sorgenti realizzati

6.1.1 Elenco dei sorgenti che compongono il framework

Per l'implementazione del framework C++ con le sue 7 classi definite in fase di

progettazione, sono stati creati i seguenti file:

• 9 header file: uno per la dichiarazione di ognuna delle 7 classi (con nomi

del tipo <classe>.h), uno per le defines globali (ComDefines.h) ed uno

per i codici di errore (ComErrors.h);

• 6 file sorgente C++: uno per l'implementazione di ognuna delle classi non

astratte (con nomi del tipo <classe>.cpp).

La lunghezza totale del codice che compone il framework è di poco superiore

alle 16mila righe, includendo la documentazione delle classi e dei metodi,

presente in tutti gli header file corrispondenti all'interfaccia di una classe.

6.1.2 Alcuni dettagli sull'implementazione del codice

Riguardo l'utilizzo delle chiamate di sistema a basso livello, sono stati riutilizzati

gli ormai assodati algoritmi prodotti in precedenza per la “libreria C”. Ecco una

interessante porzione di codice dell'algoritmo che si occupa, per le connessioni

Ethernet TCP, di ricevere i pacchetti di dati fino allo riempimento del buffer

fornito al metodo di lettura, o fino alla fine dei dati disponibili da leggere, il tutto

49

Page 50: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.1 I sorgenti realizzati

senza superare la dimensione massima dei pacchetti specificata né il timeout

impostato:

iRd_ = recv(m_iSocket, (strBuffer+*iPtrRead),

( (iMax-*iPtrRead) < m_iMaxEthPacketSize) ?

(iMax-*iPtrRead) : m_iMaxEthPacketSize, 0);

Un ciclo si occupa di verificare se viene letta una quantità di dati pari alla

dimensione massima dei pacchetti, ma con il buffer fornito non ancora pieno, e in

quel caso causa la ripetizione del pezzo di codice per tentare un'ulteriore lettura,

che sarà accodata a quelle già effettuate. Ci sono svariati algoritmi simili a questo

per i vari tipi di connessione, sia in lettura sia in scrittura.

In tutto il progetto, per le parti di codice prelevate dalla versione “C” della

libreria, sono stati effettuati alcuni adattamenti riguardanti i casting dei tipi di dato

per adeguarsi agli standard del C++.

Ora si vedranno alcune interessanti porzioni di codice relative

all'implementazione “orientata agli oggetti” del framework.

Tutte le funzionalità aggiunte nella versione C++, come la possibilità di gestire

connessioni multiple internamente al framework, sono state create con massiccio

utilizzo di funzionalità specifiche di questo linguaggio ad oggetti, come i tipi

“templatici” della Standard Template Library (STL), le eccezioni e naturalmente le

tecniche per gestire le relazioni tra classi.

Ecco alcune delle linee di codice di “ComLayer.h”, che si occupano di dichiarare

e definire le strutture dati necessarie a gestire più connessioni contemporanee:

private:

typedef std::map<unsigned int, ComBase*> CommunicationPortsMap;

CommunicationPortsMap portsMap;

Viene creato un tipo di dato “CommunicationPortsMap” che è una mappa STL,

ovvero un insieme di coppie ordinate <indice, oggetto>: gli elementi di tipo

50

Page 51: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

“oggetto” in questo caso sono puntatori alla classe astratta “ComBase”;

ovviamente solo le classi non astratte derivate da essa possono essere istanziate:

questo significa che a tempo di esecuzione la variabile “portsMap” conterrà

puntatori ad istanze (ovvero a connessioni) delle classi “ComSerial”, “ComEth” e

“ComUsb”, che sono trattabili dinamicamente con puntatori a “ComBase”. Questa

utile caratteristica è ottenuta grazie alle tecniche di polimorfismo dinamico

disponibili in C++ ed ha la conseguenza di permettere la chiamata di metodi

diversi nelle classi derivate da “ComBase”, senza alcuna distinzione nel codice

chiamante. Questi metodi hanno un'implementazione completamente differente

nelle tre classi derivate: quello che è comune è solo il prototipo, ovvero la

dichiarazione che si trova nella classe “ComBase”. Ad esempio se l'utente richiede

una lettura di dati sulla porta attualmente attiva, invocando il metodo “Read()”

fornito dalla classe “ComLayer”, quest'ultima, senza controllare se si tratta di

porta seriale, Ethernet o USB, esegue queste semplici istruzioni:

if (ptrActive)

return ptrActive->Read(strBuffer, iMax, iPtrRead);

dove ptrActive è una copia, mantenuta per comodità e velocità di esecuzione,

del puntatore all'istanza attualmente attiva. E' concettualmente equivalente a:

portsMap.find(iActiveId)->second

dove iActiveId è l'indice, nella mappa STL, della porta attualmente attiva.

Ecco invece come è stata implementata la chiamata ai metodi specifici per ogni

tipo di porta (in questo esempio è mostrato il caso di uno dei metodi specifici per

le porte seriali):

int ComLayer::InitSerialAutoBaudRate()

{

ComSerial* ptrSerial;

if ( (ptrSerial = dynamic_cast<ComSerial*>(ptrActive)) )

return ptrSerial->InitSerialAutoBaudRate();

51

Page 52: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.1 I sorgenti realizzati

else

return ERR_NOT_SERIAL_PORT;

}

Nell'istruzione di “if” è presente un assegnamento al puntatore ptrSerial; se il

“dynamic_cast” fallisce, l'assegnamento restituisce NULL, per cui non viene

eseguita l'istruzione richiesta. Questo succede nel caso in cui la porta attiva al

momento (ptrActive, che è un puntatore a “ComBase”) non sia di tipo seriale.

A seguire, ecco l'implementazione del metodo per la richiesta dell'elenco delle

porte esistenti con alcuni dettagli per ogni porta, in “ComLayer.cpp”:

void ComLayer::GetCommunicationPorts(CommunicationPortsVector&

vectorPorts)

{

struct CommunicationPort CommPort;

CommunicationPortsMap::const_iterator i = portsMap.begin();

CommunicationPortsMap::const_iterator i_end = portsMap.end();

vectorPorts.clear();

while (i != i_end)

{

CommPort.iId = i->first; // first is the Id,second is ComBase*

CommPort.iType = (i->second)->GetCommunicationPortType();

CommPort.iStatus = (i->second)->GetConnectionStatus();

vectorPorts.insert(vectorPorts.end(), CommPort);

++i;

}

}

Per fornire il risultato all'utente è stato definito un nuovo tipo di dato, chiamato

CommunicationPortsVector (l'utente deve passare alla funzione il riferimento

ad una variabile di questo tipo); si tratta di un vector della STL i cui elementi sono

strutture dati appositamente create. Eccone le definizioni, prese dal file

“ComLayer.h”:

public:

52

Page 53: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

struct CommunicationPort

{

unsigned int iId; // same IDs as in internal map

int iType;

int iStatus;

};

typedef std::vector<struct CommunicationPort>

CommunicationPortsVector;

L'utente potrà poi analizzare il vector per scegliere la porta desiderata in base al

tipo ed allo stato attuale (non entreremo nei dettagli dei possibili valori che

possono avere questi attributi, basti sapere che sono delle defines create nel file

“ComDefines.h” e ben descritte nella documentazione per l'utente).

Vediamo ora il codice del metodo di “ComLayer” che permette all'utente di

cambiare la porta attualmente attiva:

int ComLayer::SelectCommunicationPort(unsigned int iId)

{

CommunicationPortsMap::iterator i = portsMap.find(iId); // search

if (i != portsMap.end()) // if the chosen Id was found

{

iActiveId = i->first;

ptrActive = i->second;

return SUCCESS;

}

else // a not existing Id was passed:

return ERR_INVALID_ID;

}

L'utente deve indicare l'identificatore della porta che vuole attivare; questo viene

cercato ed inserito in un iterator che è un costrutto fornito dalla STL e che

permette di lavorare con gli indici dei tipi vettoriali; i->firts e i->second

identificano rispettivamente l'indice e il puntatore alla connessione della porta ora

selezionata. Come già detto, non era strettamente necessario memorizzare anche il

53

Page 54: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.1 I sorgenti realizzati

puntatore all'oggetto attivo, ma era sufficiente il suo indice; tuttavia si è scelto di

conservarlo per comodità nell'implementazione del codice che lo utilizza e per una

maggiore velocità di esecuzione specie riguardo le operazioni di lettura e scrittura,

che possono essere molto numerose.

Ecco le definizioni del costruttore e del distruttore della classe “ComLayer”:

// constructor:

ComLayer::ComLayer()

{

iActiveId = 0;

ptrActive = NULL;

}

// destructor:

ComLayer::~ComLayer()

{

while (!portsMap.empty()) // while there are elements

{

if ((portsMap.begin()->second) != NULL)// (safety reasons)

{

delete (portsMap.begin()->second); // call port destructor

}

portsMap.erase(portsMap.begin());

}

}

Il distruttore si occupa di distruggere iterativamente tutte le istanze di porte

esistenti, chiamando (tramite la parola chiave delete) i relativi distruttori,

dopodiché elimina la coppia dalla mappa portsMap. Questo è necessario per

evitare i memory leak ovvero le perdite del riferimento a certe aree di memoria,

che resterebbero occupate durante l'esecuzione del programma, ma non

utilizzabili.

Ecco come sono stati disabilitati il costruttore di copia e l'operatore di

assegnamento per la classe “ComLayer” (operazione in realtà effettuata in tutte le

54

Page 55: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

classi del framework):

private:

ComLayer(const ComLayer&);

ComLayer& operator=(const ComLayer&);

E' la tecnica normalmente utilizzata per questo scopo, che dichiara questi metodi

come privati, impedendone quindi l'accesso al software-utente.

Le funzionalità per il sistema di log su file sono state implementate dalla classe

“ComLog”, che viene istanziata, tramite puntatore (aggregazione), da oggetti delle

classi derivate da “ComBase” solo quando necessario, secondo quanto specificato

con il design pattern “Lazy initialization”. Per la scrittura fisica sul file di log si è

scelto di implementare un metodo comune a tutte le classi derivate e definito nella

classe astratta “ComBase”, che chiama i metodi della classe “ComLog” solo se il

log è attivo per l'istanza della porta che è in uso. Ecco il metodo in questione,

preso dal file “ComBase.h”:

inline int PrintLog(char* strClass, char* strMethod,

char* strAction, int iVerb)

{

if (m_Log != NULL)

return m_Log->PrintLog(strClass, strMethod, strAction, iVerb);

else

return SUCCESS; // ok also if called with log not active

}

La parola inline indica espansione in linea (cioè sostituzione del codice del

metodo al posto di ogni sua chiamata) a tempo di compilazione, cosa che porta ad

una maggiore velocità computazionale a tempo di esecuzione, importante dato che

si tratta di un metodo chiamato di frequente.

55

Page 56: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.2 La gestione del progetto

6.2 La gestione del progetto

6.2.1 Le librerie a collegamento dinamico

La forma finale del framework compilato e pronto all'uso è quella di un file di

libreria a collegamento dinamico per ambiente Linux; queste librerie sono

contraddistinte dal suffisso “.so” e non sono eseguibili di per sé, ma ad esse si

collegano, a tempo di esecuzione, altri programmi (o anche altre librerie)

opportunamente compilati. Ciò porta a dei vantaggi rispetto alle librerie a

collegamento statico:

• possibilità di condividere una libreria tra più programmi che la usano,

caricandola in memoria una sola volta;

• possibilità di aggiornare la libreria senza dover ricompilare o aggiornare

tutti i programmi e le librerie che la usano.

6.2.2 Il comando “make” per la compilazione

Per compilare il codice sorgente si utilizza il comando “make” con le relative

direttive contenute nel makefile. Il programma “make” è uno strumento per il

controllo del processo di costruzione (o ricostruzione) del software: ad esempio

permette di minimizzare i tempi di rielaborazione attraverso una mirata gestione

dei file da coinvolgere nel processo di ricompilazione.

Un makefile è un file di testo, chiamato di norma “Makefile”, contenente le

regole che indicano a “make” cosa produrre e in che modo. Ogni singola regola è

composta da:

• Un target (destinazione), cioè l'elemento che “make” cerca di creare;

generalmente è un file;

• Una lista di dependency (dipendenze), solitamente rappresentate da file,

56

Page 57: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

necessarie per la ricostruzione;

• Una lista di command (comandi) la cui esecuzione è vincolata alle regole

di dipendenza prima citate.

Le regole di scrittura di un makefile sono così formate:

target: dependency dependency [...]

command

command

[…]

Le regole devono essere separate tra loro da una riga vuota.

In testa ai makefile possono essere dichiarate delle variabili che permettono una

costruzione modulare delle regole, ad esempio per permettere in modo semplice di

modificare la directory di destinazione dei file prodotti dalla compilazione.

Il makefile per l'applicazione prodotta è stato scritto completamente in modo

manuale, per avere il massimo controllo sulle modalità di compilazione e sul

linking, e per fornire una serie di opzioni personalizzate per il “make”, che

permettono al programmatore di compilare anche le applicazioni di esempio e la

documentazione con un solo comando. Sono state anche definite delle regole per

effettuare la pulizia facoltativa dei file non necessari, come i file di backup creati

in automatico o i file oggetto.

La compilazione della versione definitiva prodotta, che crea un file di libreria a

collegamento dinamico “libComLayer.so”, non causa l'emissione di warning in

quanto è stata prestata molta attenzione nella scrittura del codice, per avere un

software aderente agli standard e il più possibile privo di malfunzionamenti. Ecco

alcune righe estratte dal makefile creato per la compilazione del framework, che

avviene con GCC/G++ . Definizione di due variabili (le \ a fine riga indicano un

“a-capo” per questioni di spazio, che non è presente nel makefile originale):

57

Page 58: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.2 La gestione del progetto

COMPILER = g++ -O2 -W -Wall -pedantic

LINKING = -L$(OUT)/ -Wl,-rpath -Wl,../$(OUT)/ -lComLayer \

-L$(LIBUSB_INSTALL_DIR) -Wl,-rpath \

-Wl,$(LIBUSB_INSTALL_DIR) -lusb-1.0

Compilazione di un file oggetto e del file di libreria dinamica:

$(OUT)/ComLayer.o: $(SRC)/ComDefines.h $(SRC)/ComErrors.h \

$(SRC)/ComLayer.cpp $(SRC)/ComLayer.h \

$(OUT)/ComSerial.o $(OUT)/ComEth.o $

(OUT)/ComUsb.o

$(COMPILER) -fPIC -c $(SRC)/ComLayer.cpp -o $(OUT)/ComLayer.o

$(OUT)/libComLayer.so: $(O_FILES)

$(COMPILER) -shared -Wl,-soname,libComLayer.so -o \

$(OUT)/libComLayer.so $(O_FILES) -lc

Compilazione di un programma di test, con opzioni per il linking dinamico al

framework, il quale è sotto forma del file “libComLayer.so”:

$(EXAMPLE)/test: $(EXAMPLE)/test.cpp $(OUT)/libComLayer.so

$(COMPILER) $(EXAMPLE)/test.cpp -o $(EXAMPLE)/test $(LINKING)

Compilazione del programma di test in ambiente grafico:

$(EXAMPLE)/graphic: $(EXAMPLE)/graphic.cpp $(EXAMPLE)/graphic.h \

$(EXAMPLE)/Makefile MakeGraphic.sh libComLayer

sh MakeGraphic.sh

da notare che viene chiamato uno script per la shell di Linux, che richiama la

compilazione dell'applicazione grafica, che avviene con un altro “Makefile” che è

stato generato dall'utility “Qmake”. Per “Qmake” è usato un file di progetto scritto

manualmente chiamato “graphic.pro”, il cui contenuto è il seguente:

TEMPLATE = app

TARGET = graphic

CONFIG += qt warn_on release

58

Page 59: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

LIBS += -L../out/ -Wl,-rpath -Wl,../out/ -lComLayer

LIBS += -L/usr/local/lib/ -Wl,-rpath -Wl,/usr/local/lib/ -lusb-1.0

HEADERS = graphic.h

SOURCES = graphic.cpp

6.2.3 Il debugging

Il debugging è stato effettuato principalmente con la funzionalità di “logging”

(registrazione) inclusa nel framework: la classe “ComLog” infatti si occupa della

scrittura su di un file a scelta (il che naturalmente include anche la console di

sistema) delle operazioni che sono svolte dal software. Sono disponibili sei livelli

di “verbosity”, ovvero di “quanto dovrà scrivere” il sistema di “logging”, che

vanno da NO_VERBOSITY=0 a MAX_VERBOSITY=5. La cosa interessante e

molto utile (almeno nel caso di questo software) di questo sistema di debugging è

la possibilità di registrare anche l'orario in cui avviene un determinato evento, con

una precisione al millesimo di secondo, e volendo anche superiore. Ecco un

esempio del log che si ottiene con “verbosity” al massimo livello utilizzando la

ComLayer C++ per inizializzare e tentare di aprire un collegamento USB (senza

successo):

2010-06-13 11:02:06.686 ComUsb::GetConnectionStatus - return: PORT_NOT_INITIALIZED

2010-06-13 11:02:19.508 ComUsb::InitUsb - Start, parameter: /dev/usb/lp0

2010-06-13 11:02:19.508 ComUsb::InitUsb - Begin device name checking

2010-06-13 11:02:19.508 ComUsb::InitUsb - End, returning: SUCCESS

2010-06-13 11:02:19.508 ComUsb::GetConnectionStatus - return: PORT_INITIALIZED

2010-06-13 11:02:19.509 ComUsb::GetUsbParameters - Function start

2010-06-13 11:02:19.509 ComUsb::GetUsbParameters - End, returning: SUCCESS

2010-06-13 11:02:19.509 ComUsb::GetConnectionStatus - return: PORT_INITIALIZED

2010-06-13 11:02:20.925 ComUsb::Open - Function start

2010-06-13 11:02:20.925 ComUsb::Open - Try to open port (interface 1)

2010-06-13 11:02:20.925 ComUsb::Open - Error, returning: ERR_OPENING_USB

2010-06-13 11:02:20.925 ComUsb::GetConnectionStatus - return: PORT_INITIALIZED

2010-06-13 11:02:20.925 ComUsb::GetUsbParameters - Function start

2010-06-13 11:02:20.925 ComUsb::GetUsbParameters - End, returning: SUCCESS

2010-06-13 11:02:20.926 ComUsb::GetConnectionStatus - return: PORT_INITIALIZED

Ad esempio la seguente riga del log:

59

Page 60: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.2 La gestione del progetto

2010-06-13 11:02:19.508 ComUsb::InitUsb - Start, parameter: /dev/usb/lp0

è stata generata con questo pezzo di codice:

char strLog[MAX_USB_LOG_LENGTH+1];

strLog[MAX_USB_LOG_LENGTH]=0; // safety reasons

snprintf(strLog, MAX_USB_LOG_LENGTH, "Start, parameter: %s",

strUsb);

PrintLog("ComUsb", "InitUsb", strLog, 3);

dove “PrintLog()”, metodo interno alla classe “ComBase” e non ridefinito nelle

classi derivate come la “ComUsb”, prende come parametri tre stringhe di tipo C

ed un intero che indica il livello minimo a cui la “verbosity” dev'essere impostata

perché la riga sia effettivamente registrata, e poi invoca un metodo dell'istanza

della classe “ComLog”, che si occupa della scrittura su file vera e propria.

Durante la fase di debugging è stato alzato il livello massimo di “verbosity” a 6,

in modo da poter temporaneamente aggiungere delle chiamate speciali a

“PrintLog()” per verificare certi dettagli in caso di problemi, o per sicurezza.

6.3 La documentazioneLa documentazione ha rivestito naturalmente un ruolo importante nella

creazione del framework anche perché sarà utilizzato sia da clienti dell'azienda

che dovranno affidarsi alle informazioni fornite per installare la libreria e

interagire con essa mediante altri programmi, sia dalla stessa Custom Engineering

per eventuali estensioni o aggiornamenti futuri.

Tutta la documentazione è quindi stata scritta in lingua inglese ed è disponibile

ai clienti in formato di pagine web HTML, generate con Doxygen, di cui

parleremo tra poco. La parte HTML riguarda solamente la classe “ComLayer” che

è quella che dev'essere istanziata dall'utilizzatore del framework, mentre

naturalmente tutte le altre classi sono state corredate di commenti standard,

fondamentali in caso di future estensioni o manutenzione.

60

Page 61: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

Anche i commenti all'interno del codice sono in lingua inglese, seguendo così un

precetto aziendale, fondamentale quando ci sono molti dipendenti anche all'estero

(nonostante, per il momento, la ricerca e lo sviluppo si effettuino interamente in

Italia, ovvero nella sede di Fontevivo - PR).

6.3.1 Istruzioni per la compilazione

Le istruzioni per la compilazione del framework, sempre in lingua inglese, sono

state incluse in un file di testo semplice, chiamato “README”. Eccone una breve

sintesi: in sostanza è sufficiente lanciare il comando “make” nella directory

principale del progetto per ottenere nella sotto-directory “out/” il file di libreria

“libComLayer.so”. Vengono anche creati i file oggetto intermedi, che è possibile

eliminare lanciando il comando “make clean”. Nella sotto-directory “example/” si

troveranno dei programmi di esempio in ambiente testuale. Se si vuole compilare

anche l'applicazione grafica di test inclusa (operazione che richiede la presenza del

framework grafico “Qt”) è sufficiente il comando “make all” o “make graphic”.

Per la versione già compilata da fornire ai clienti, le istruzioni per il

collegamento dinamico al framework sono presenti nella home-page HTML della

documentazione generata con Doxygen a partire dai file sorgenti. Qui è anche

specificato che il framework dipende dalla libreria “libusb-1.0” e sono presenti

informazioni su come recuperarla e come installarla nel sistema.

6.3.2 Lo strumento “Doxygen” per la documentazione

Doxygen (www.doxygen.org) è un potente strumento freeware (licenza GNU

GPL) per generare la documentazione in diversi formati e con un layout grafico

gradevole, a partire dai file sorgenti opportunamente commentati. La

documentazione può essere generata in formato HTML, LateX, RTF, PostScript o

anche per pagine di manuale Linux (man).

61

Page 62: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.3 La documentazione

Per il framework realizzato si è scelto di generare la documentazione soltanto in

formato ipertestuale HTML, che grazie ai collegamenti inseriti e al gradevole

aspetto grafico (di cui si occupa interamente Doxygen) risulta di facile utilizzo per

l'utente. Ciò non toglie che sia possibile generare la documentazione in altri

formati: è sufficiente fare poche modifiche al Doxyfile, che è un po' l'equivalente

per Doxygen del Makefile per “make”. Il Doxyfile iniziale può essere generato in

automatico, ed è piuttosto complesso, ma è standard per tutte le applicazioni; è

sufficiente lanciare il comando “doxygen -g”: successivamente possono essere

fatte modifiche manuali per cambiare il comportamento di Doxygen, come la

scelta dei formati in cui generare la documentazione.

L'invocazione di Doxygen avviene in automatico da parte di “make”, in quanto è

stato specificato così nel Makefile di questo progetto. Se Doxygen non è presente

nel sistema semplicemente sarà saltata la generazione della documentazione.

La documentazione formato HTML del framework “libComLayer” è stata

pensata per l'uso da parte degli utenti, quindi è stata generata solamente per la

classe “ComLayer”, a partire dal file “ComLayer.h”. Ora si vedranno alcune

dettagli dei commenti in formato supportato da Doxygen presenti in questo file (in

totale occupano più di 1000 righe).

Prima parte della creazione della home-page della documentazione:

/**

\mainpage Custom Engineering SPA - ComLayer C++ library

Linux C++ library for communication with Serial, Ethernet, USB printers

Copyright Custom Engineering SPA - (c) 2009

\section intro Introduction

This library has the utility to allow communication with Custom products

on different communication ports, in the most similar possible way.

[ … ]

*/

Ecco la parte che crea le informazioni di base riguardanti il file “ComLayer.h”:

62

Page 63: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6 Implementazione del framework C++

/** \file ComLayer.h

\brief This file contains declaration of the class (ComLayer) that must

\brief be instantiated to manage the whole library

*/

Si vedrà ora come avviene la creazione della documentazione riguardante la

classe “ComLayer” in generale; è mostrato anche l'inizio della dichiarazione della

classe, che segue il commento per Doxygen:

/** \class ComLayer

This class, defined in \ref ComLayer.h manages all the library, creating

and handling instances of communication ports of these three types

[ … ]

\brief This is the class to instantiate to manage the whole library

*/

class ComLayer {

[ … ]

Di seguito, la creazione di un gruppo per la documentazione, che corrisponderà

ad una pagina del manuale e conterrà tutti i metodi che saranno assegnati (con la

clausola \ingroup) a quel gruppo:

/** @defgroup commons Input/Output common functionalities: open/close, \

communication and port control

These methods have almost the same behaviour with all the

[ … ]

*/

Infine, un estratto dei commenti del metodo “Read()” della classe “ComLayer”,

che è stato assegnato al gruppo “commons”:

/** \ingroup commons

Reads data from device (serial, eth, usb - non blocking, timeout):

[ … ]

\param strBuffer buffer where to store read data (allocated by user)

[ … ]

\returns

- SUCCESS if buffer is filled or if reading queue is empty

- ERR_SERIAL_NOT_OPEN if serial port is not currently open

[ … ]

*/

63

Page 64: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

6.3 La documentazione

6.3.3 La documentazione risultante in formato HTML

Ecco lo screenshot della prima parte di una pagina HTML della documentazione

del framework; si tratta della sezione che si occupa dei metodi del gruppo

“commons”, cioè quelli comuni tra tutti i tipi di connessione supportati:

L'elenco dei metodi (chiamati impropriamente “Function” da Doxygen) è

nell'ordine in cui si trovano nel file sorgente, mentre le relative spiegazioni

dettagliate sono in ordine alfabetico, ed accessibili direttamente cliccando sui

nomi nell'elenco in alto alla pagina. Nello screenshot, per ovvie questioni di

spazio, è visibile la descrizione di un solo metodo di questa sezione.

64

Page 65: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

7 Testing e applicazioni di esempio

7 Testing e applicazioni di esempio

7.1 Il testing durante l'implementazioneLe tecniche di ingegneria del software sono utili anche per limitare al massimo

gli errori di programmazione, fornendo una serie di regole per la corretta scrittura

del codice. Tuttavia non c'è modo di avere la certezza che un software sia scritto

correttamente a livello algoritmico, ma si può cercare di avvicinarsi il più

possibile alla perfezione: per raggiungere questo scopo è indispensabile effettuare

continue verifiche sul codice prodotto, provando ad utilizzarlo in casi particolari

ed estremi, alla ricerca di comportamenti anomali.

Per questo, sia durante lo sviluppo, sia al termine del periodo di tirocinio, il

software è stato verificato con lunghe fasi di prova e nella versione finale non

sono stati trovati difetti anche dopo ore di testing, anche da parte dell'apposito

reparto in azienda.

7.2 Applicazioni di esempioPer i test di collaudo, ma in parte anche per utilizzo da parte di clienti della

Custom, sono stati scritti alcuni programmi di esempio che utilizzano il

framework. Alcuni sono in ambiente testuale, altri in ambiente grafico.

Le funzionalità presenti permettono di cominciare ad utilizzare i prodotti della

Custom Engineering semplicemente eseguendo uno di questi programmi. Le

applicazioni testuali che vengono fornite ai clienti sono due, una delle quali

permette di provare tutte le funzionalità, mentre l'altra effettua dei test avanzati

pre-impostati sulla comunicazione USB.

L'applicazione più interessante e di facile utilizzo è sicuramente quella con

65

Page 66: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

7.2 Applicazioni di esempio

interfaccia grafica, che rende molto facile la gestione di più connessioni

contemporanee, il passaggio da una all'altra, la modifica, chiusura e apertura di

una porta, la lettura e scrittura di dati liberi o l'invio di comandi preimpostati nelle

stampanti.

Ecco alcuni screenshot dell'applicazione in questione:

66

Page 67: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

7 Testing e applicazioni di esempio

L'applicazione grafica di esempio sviluppata si appoggia al framework grafico

C++ “Qt”, che permette lo sviluppo di applicazioni grafiche su molteplici

piattaforme, tra cui naturalmente “X11”, che è l'ambiente grafico normalmente

utilizzato nei sistemi Linux.

“Qt” è di proprietà di Nokia Corporation, ed è rilasciato sotto la licenza GNU

Lesser General Public License 2.1, che permette lo sviluppo di software

commerciale facente uso della libreria/framework, purché non ci siano modifiche

al codice di “Qt”.

67

I pop-up mostrati quando si preme Initialize, rispettivamente per le porte seriale, Ethernet, USB

Page 68: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

7.2 Applicazioni di esempio

68

Page 69: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

8 Conclusioni

8 Conclusioni

Lo sviluppo di un software commerciale, di una certa complessità, per

un'azienda, si è rivelato un lavoro molto interessante nonché impegnativo, ma per

questo in grado di dare molte soddisfazioni. Le conoscenze acquisite all'università

nei corsi di programmazione, di ingegneria del software e di sistemi operativi si

sono rivelate fondamentali per riuscire a completare il lavoro in modo

indipendente ed adattandosi alle richieste aziendali in termini di qualità e

documentazione del software.

Si può dire che questa esperienza è stata positiva sia per l'azienda, che ha

arricchito il supporto che è in grado di fornire ai clienti per il sistema operativo

Linux, sia per lo studente, per l'aver messo in pratica le conoscenze acquisite con

lo studio, e per l'approfondimento che è stato necessario su alcune tematiche come

la programmazione per la comunicazione con le periferiche collegate al sistema e

alcune tecniche di ingegneria del software.

Un framework software nasce come un pezzo di codice che può essere esteso ma

non modificato dall'utente, e così sarà per i clienti dell'azienda che ne

richiederanno l'utilizzo come base per scrivere software di alto livello per la

comunicazione con i dispositivi prodotti dalla Custom Engineering. Sotto questo

punto di vista quindi i possibili utilizzi sono molteplici e vanno dal controllo di

scanner collegati a personal computer, all'utilizzo in sistemi embedded (con

sistema operativo Linux) per gestire le stampanti su carta termica.

Il framework sarà probabilmente utilizzato anche all'interno dell'azienda per

fornire ai clienti applicativi Linux di alto livello, anche personalizzati, pronti per

l'uso con i prodotti Custom.

69

Page 70: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

8 Conclusioni

La prima possibile estensione del framework potrebbe riguardare il supporto alle

comunicazioni senza fili Bluetooth, tecnologia che si sta diffondendo tra molti dei

prodotti della Custom Engineering, soprattutto quelli portatili.

Un altro possibile sviluppo è quello di rendere il framework C++ indipendente

dalla libreria “libusb-1.0”, andando a scrivere direttamente le complesse parti di

codice che permettono la comunicazione di basso livello via USB tramite la

cosiddetta “interfaccia 2”. Tuttavia per il momento si è scelto di non effettuare

questo gravoso compito dando la priorità ad altre funzionalità, anche perché la

“libusb-1.0” è un software molto leggero e veloce, e non ha finora evidenziato

problemi di compatibilità anche con sistemi embedded poco potenti.

70

Page 71: UNIVERSITÀ DEGLI STUDI DI PARMA€¦ · UNIVERSITÀ DEGLI STUDI DI PARMA FACOLTÀ DI S CIENZE MATEMATICHE F ISICHE E NATURALI CORSO DI L AUREA IN I NFORMATICA Progettazione e realizzazione

8 Conclusioni

8.1 Bibliografia

K. Wall, M. Waston, M. Whitis – “Programmare in Linux Tutto e oltre” –

Apogeo – 2000 – ISBN: 9788873036197

K. Davis, J. W. Turner, N. Yocom – “The Definitive Guide to Linux Network

Programming” – Apress – 2004 – ISBN: 1-59059-322-7

http://www.cplusplus.com/reference/ – C++ Documentation

Daniel Drake – Libusb documentation – http://www.libusb.org/

Nokia Corporation – Qt3 documentation – http://doc.qt.nokia.com/

Simone Piccardi – “GaPiL – Guida alla Programmazione in Linux” – 2002-2008

– http://gapil.truelite.it/

Robert C. Martin – “UML Tutorial” – 1997-98 – http://www.objectmentor.com/

E. Gamma, R. Helm, R. Johnson, J. Vlissides – “Design Patterns: elementi per il

riuso di software a oggetti” – Paerson Education Italia – 2002 – ISBN: 88-7192-

150-X

71