universita` degli studi di urbino - Istituto di Scienze e Tecnologie dell

Transcript

universita` degli studi di urbino - Istituto di Scienze e Tecnologie dell
UNIVERSITA’ DEGLI STUDI DI URBINO
CORSO DI LAUREA IN
INFORMATICA APPLICATA
RELAZIONE DEL PROGETTO DI LABORATORIO
Implementazione di un programma per la gestione
di un distributore automatico per il noleggio di dvd
per l’esame di INGEGNERIA DEL SOFTWARE
Professore Tommaso Pagnini
Nome: Togni Gabriele
Matricola: 200857
Nome: La Gatta Francesco Matricola: 201003
SPECIFICA DEL PROBLEMA
Il programma deve gestire un distributore automatico di dvd. Il distributore dovrà gestire il noleggio
e la riconsegna degli articoli già presenti in archivio da parte di utenti anch’essi già registrati
precedentemente. Tutte le informazioni costituenti sia l’archivio degli articoli sia quello dei clienti
sono da considerare precedentemente memorizzate su file.
Ogni cliente, di cui sono memorizzati anche nome e cognome, possiede un credito che va gestito dal
programma e che viene usato per il pagamento dei noleggi. Ciascun utente viene identificato da un
codice (che sostituisce la reale tessera magnetica di un qualsiasi distributore) a cui viene associato
un codice segreto (pin) da utilizzare per l’accesso al sistema.
Per quel che riguarda gli articoli presenti in archivio si specifica che questi sono suddivisi in tre
principali categorie: film, documentari e concerti. Ciascuna categoria è sua volta divisa in generi:
• Film: drammatico, azione, thriller, drammatico, commedia…
• Documentari: scienza, natura, storia…
• Concerti: rock, pop, reggae, soul…
Inoltre per quanto riguarda gli articoli si tenga conto che quelli presenti in archivio da non più di tre
mesi sono etichettati come ‘novità’ indipendentemente sia dalla categoria che dal genere.
Per quel che riguarda la gestione del credito si specifica il seguente listino dei prezzi:
• Riconsegna entro 12 ore: 2
• Riconsegna entro 1 giorno: 3
• Ogni giorno successivo: 2
Si consideri che un cliente può noleggiare un articolo anche se ne deve ancora restituire degli altri.
Il credito minimo per effettuare un noleggio è pari a 2 per ogni articolo che si vuol noleggiare.
Si consideri il programma come una macchina a stati operante secondo il seguente schema:
Specifiche dei vari stadi:
• ‘Idle’: è lo stato di stanby del sistema in cui si resta in attesa dell’arrivo di un cliente che
dovrà digitare il proprio codice identificativo (questo nel caso reale corrisponderebbe
all’inserimento della tessera magnetica) accedendo così allo stato successivo.
• ‘Riconoscimento’: è lo stato in cui si chiede all’utente identificato il proprio codice segreto
che permette l’effettivo accesso al sistema in caso di inserimento corretto o il ritorno allo
stato ‘idle’ in caso di inserimento errato.
• ‘Scelta’: è uno stato che costituisce il menù principale dell’applicazione e da cui è possibile
accedere alle tre opzioni principali o tornare allo stato ‘idle’:
• ‘Gestione credito’: in questo stato viene visualizzato il credito residuo; si può quindi
scegliere di ricaricarlo o ritornare allo stato ‘scelta’. Nella fase di ricarica, nel caso reale il
sistema rimarrebbe in attesa dell’inserimento di una o più banconote; nel nostro caso questa
operazione si compierà digitando l’importo desiderato e verrà visualizzato il nuovo credito.
• ‘Riconsegna’: in questo stato si resta in attesa dell’inserimento del codice dell’articolo da
restituire; nel caso reale questa operazione corrisponderebbe all’attesa dell’inserimento nel
distributore dell’articolo noleggiato, è comunque possibile ritornare allo stato ‘scelta’. La
riconsegna di un articolo coincide con la detrazione del prezzo del noleggio. Qualora
l’utente non avesse un credito sufficiente si passa automaticamente allo stato ‘gestione
credito’. Se l’operazione va a buon fine si passa allo stato di ‘restituzione’.
• ‘Restituzione’: in questa fase viene aggiornato il sistema (sia per quel che riguarda la
disponibilità dell’articolo restituito sia per quel che riguarda l’utente). Da questo stato si
ritorna automaticamente allo stato ‘scelta’.
• ‘Noleggio’: in questo stato l’utente sceglie l’articolo da noleggiare sulla base di varie
tecniche di visualizzazione degli articoli disponibili. In particolare potranno essere
visualizzati tutti gli articoli di una categoria (film, documentari, concerti), tutti gli articoli
relativi ad un determinato genere oppure tutte le novità. È comunque possibile ritornare allo
stato ‘scelta’. La scelta definitiva dell’articolo da accesso allo stato di ‘erogazione’ qualora
l’utente abbia il credito necessario; viceversa si ritorna allo stato ‘scelta’.
• ‘Erogazione’: in questa fase viene aggiornato il sistema (sia per quel che riguarda la
disponibilità dell’articolo noleggiato sia per quel che riguarda l’utente, in particolare una
parte del prezzo del noleggio, pari a 2 , viene detratta immediatamente). Da questo stato si
ritorna automaticamente allo stato ‘scelta’.
SPECIFICA DEI REQUISITI
Diagramma degli Use Cases:
!! %% 1 0,0/ <()**
%% 1 0,0/ <()**
!! %% 1 0,0/ <()**
" !
" !
#$ !! %% ' &( ()**
+ ,(- . -/ / ,0. 0)1 -32
!! " !
45 6
7 8 !9 45 6
! 7 45 6
8: ;
%% 3 &( 0)**
+ ,0-' . -/ / ,0. )1 -'2
Use Case: Identificazione
Attori:
Utente
Punti di estensione:
Precondizioni:
1. L’utente non ha inserito il proprio codice cliente.
Corso d’azione base:
1. L’utente inserisce il proprio codice cliente
2. Viene chiesto all’utente di inserire il proprio PIN
3. L’utente inserisce il PIN
4. In caso di inserimento corretto vengono visualizzati nome e cognome dell’utente
5. Si passa al menù principale
Postcondizioni:
1. L’utente è stato identificato dal sistema.
Corso d’azione alternativo:
In caso di inserimento errato si ritorna al punto “1” del corso d’azione base
Use Case: Gestione Credito
Attori:
Utente
Punti di estensione:
Precondizioni:
1. L’utente è stato identificato dal sistema.
Corso d’azione base:
1. Viene visualizzato il credito dell’utente.
2. Viene visualizzato un menù che prevede 2 scelte: “1” Ricarica credito, “2” Ritorno al menù
principale.
3. Se l’utente digita “2” si esce dallo use case.
4. Se l’utente digita “1” vengono visualizzati gli importi corrispondenti alle banconote accettate
(5, 10 e 20 ).
5. L’utente digita l’importo della ricarica (0 per tornare al punto ”1” del corso d’azione base).
6. Viene aggiornato il credito dell’utente(credito massimo consentito 999 ).
7. Si torna al punto “4” del corso d’azione base.
Postcondizioni:
Corso d’azione alternativo:
In caso di inserimento errato si ritorna al punto precedente del corso d’azione base.
Use Case: Noleggio
Attori:
Utente
Punti di estensione:
1. L’utente deve avere almeno 2 per noleggiare un articolo
Precondizioni:
1. L’utente è stato identificato dal sistema.
Corso d’azione base:
1. Viene visualizzato un menù che prevede 4 scelte: “1” Visualizza per categoria, “2” Visualizza
per genere, “3” Visualizza novità, “4” Ritorno al menù.
2. L’utente sceglie una delle 4 opzioni.
3. Se l’utente digita “4” si esce dallo use case.
4. Vengono visualizzati gli articoli disponibili sulla base della scelta effettuata.
5. L’utente sceglie l’articolo da prenotare inserendo il relativo codice (0 per tornare alle opzioni di
visualizzazione).
6. Viene visualizzata una scheda contenente tutte le informazioni sull’articolo
7. L’utente scegli se confermare la prenotazione o tornare al menù precedente
8. Viene verificato che l’utente abbia almeno 2 sul proprio conto.
9. In caso positivo vengono detratti i 2 .
10. Il sistema decrementa di una unità la disponibilità dell’articolo.
Postcondizioni:
Corso d’azione alternativo:
In caso di inserimento errato si ritorna al punto precedente del corso d’azione base. In caso di
credito insufficiente viene comunicato all’utente un messaggio d’errore e si ritorna al menù di
visualizzazione scelto.
Use Case: Riconsegna
Attori:
Utente
Punti di estensione:
1. L’utente deve avere almeno un credito pari al prezzo del noleggio
Precondizioni:
1. L’utente è stato identificato dal sistema.
Corso d’azione base:
1. Viene chiesto all’utente il codice dell’articolo da restituire (0 per uscire dallo use case)
2. Viene calcolato il prezzo del noleggio
3. Se il credito dell’utente è sufficiente viene detratto il prezzo del noleggio dal credito.
4. Viene incrementata di una unità la disponibilità dell’articolo.
Postcondizioni:
Corso d’azione alternativo:
Se il credito dell’utente è insufficiente si passa allo use case “Gestione Credito”.
Use Case: Uscita
Attori:
Utente
Punti di estensione:
Precondizioni:
1. L’utente è stato identificato dal sistema.
Corso d’azione base:
1. Il sistema torna nello stato di attesa dell’identificazione di un utente.
Postcondizioni:
Corso d’azione alternativo:
4
^
#
"#
# '(
"
$$
%
a
B
#
# &
&#
'(
b(
D(
# Y"
#B
&
&# %
B
&
&# $
#$
&#%
Y
&
) (
D(
c
'(
_` _` %
+( & $$%
, #
'(
) ( # !"
!* #
-.
d
e
4
^
8
9
;<
8:7
89
<
@=
?
7>
=
6
7 56
2
/01
-.
4 3
4
^
# (
B (
#
X# VW
3
e
4 ff
_
d
STU
B
&#
YZ
(( \]% $
WX (
\
B[ (
IK
L QFR
N
LM F
K
IK JF
PO
N
(%C
GH EF
B&
%
&%
&
%
&%
&
(
D(
(
D(
(
A
# '(
"
#B
3
ANALISI E PROGETTAZIONE
Diagramma degli stati dell’intera applicazione:
Questo diagramma descrive il funzionamento completo dell’applicazione immaginando di navigare
tutti i menù del distributore di dvd che vogliamo implementare.
Decidiamo di implementare solo il lato utente dell’applicazione, quindi dobbiamo immaginare il
nostro distributore perennemente acceso(solo per comodità prevediamo di “spegnere” il sistema nel
caso in cui al posto del codice cliente venga inserita la parola “stop”). A questo punto risulta
immediatamente chiaro il motivo per cui non si parlerà mai ne di come accendere o spegnere il
distributore, ne di come inserire o rimuovere gli articoli o i clienti dal database del sistema.
Il database sopra citato è costituito da alcuni file di testo contenenti le informazioni riguardanti i
clienti, gli articoli e i noleggi:
- per i clienti(Clienti.txt) memorizziamo Codice_cliente, Pin, Nome, Cognome, Credito;
- per i noleggi(Noleggi.txt) memorizziamo Istante_riconsegna(scritto ovviamente solo dopo
l’avvenuta restituzione), Codice_cliente, Codice_articolo, Istante_noleggio;
Per quanto riguarda gli articoli abbiamo già detto che si suddividono in 3 categorie(Film, Concerti,
Documentari) che hanno alcune informazioni in comune; tuttavia decidiamo di memorizzare tutte le
informazioni riguardanti il concetto di Articolo in 3 file separati(Film.txt, Concerti.txt,
Documentari.txt):
- per i film(Film.txt) memorizziamo Codice_articolo, Titolo, Data_arrivo, Anno_uscita, Genere,
Copie_disponibili, Regista, Attore_protagonista;
- per i concerti(Concerti.txt) memorizziamo Codice_articolo, Titolo, Data_arrivo, Anno_uscita,
Genere, Copie_disponibili, Cantante, Luogo;
- per i documentari(Documentari.txt) memorizziamo Codice_articolo, Titolo, Data_arrivo,
Anno_uscita, Genere, Copie_disponibili, Luogo, Breve_descrizione;
Per rendere più efficiente la visualizzazione degli articoli in base al loro genere abbiamo deciso di
memorizzare in 3 file separati(Generi_film.txt, Generi_concerti.txt, Generi_documentari.txt) tutti i
generi degli articoli presenti in archivio suddivisi in base alla categoria.
Anche se la nostra applicazione implementa solo il lato utente, è chiaro che le operazioni di
amministrazione del sistema(aggiunta e rimozione dei clienti e degli articoli) possono essere
facilmente effettuate editando manualmente i files di testo.
Come già detto l’applicazione risulta essere sempre in esecuzione, quindi un qualsiasi utente deve
come prima operazione, inserire direttamente il proprio Codice_cliente; dopo l’inserimento
l’applicazione confronta il codice inserito con tutti quelli presenti in archivio ed in caso di successo
chiede all’utente il proprio Pin confrontandolo con quello memorizzato in archivio. Se anche
questo confronto va a buon fine il cliente accede di fatto all’applicazione e verranno quindi
memorizzati tutti i sui dati. A questo punto si visualizzano nome e cognome dell’utente
riconosciuto il quale si trova mediante un menù a dover scegliere tra 4 opzioni(Gestione credito,
Noleggio, Restituzione e Uscita):
- Se viene scelta l’opzione Gestione credito l’applicazione visualizza il credito attuale dell’utente
e chiede allo stesso se vuole effettuare una ricarica; in caso affermativo si esegue l’operazione
di ricarica e si torna alla fase di visualizzazione del credito mentre nel caso in cui non si voglia
effettuare la ricarica si ritorna al menù.
- Se viene scelta l’opzione Noleggio il cliente sceglie il tipo di visualizzazione degli
articoli(verranno sempre visualizzati solo gli articoli con disponibilità maggiore o uguale ad 1):
nella visualizzazione per categoria, una volta scelta una delle 3 categorie, si accede al
corrispondente file visualizzando Codice e Titolo di ogni articolo presente in archivio. Nella
visualizzazione delle novità si accede a tutti e 3 i file contenenti gli articoli visualizzando tutti
quelli presenti in archivio da non più di 3 mesi. Infine nella visualizzazione per genere verranno
visualizzati tutti i diversi generi presenti in archivio suddivisi in base alla categoria di
appartenenza(verranno quindi letti i 3 file Generi_film.txt, Generi_concerti.txt,
Generi_documentari.txt), una volta scelto un genere verranno visualizzati all’utente tutti gli
-
-
articoli (Codice e Titolo) che vi appartengono. Comune a tutte e 3 le visualizzazioni è la fase di
scelta dell’articolo da noleggiare, fase che può essere vista come una prenotazione dell’articolo
durante la quale vengono visualizzate tutte le informazioni presenti su quel articolo. Se l’utente
sceglie di ritirare l’articolo verrà effettuato un controllo che permetterà l’effettivo noleggio solo
se l’utente possiede almeno 2 che in caso positivo verranno immediatamente detratti insieme
al decremento della disponibilità dell’articolo, in caso negativo sarà comunicata all’utente
l’insufficienza del suo credito ma saranno comunque concessi tutti i tipi di visualizzazione. Il
noleggio si conclude con la scrittura sul file Noleggi.txt delle informazioni sull’operazione
appena eseguita(fatta ovviamente eccezione per il campo Istante_riconsegna sostituito
momentaneamente da una stringa del tipo GG/MM/AAAA HH:MM:SS)
Se viene scelta l’opzione Restituzione il cliente deve inserire il codice dell’articolo che vuole
restituire, verrà quindi cercata nel file Noleggi.txt la coppia di informazioni Codice_cliente,
Codice_articolo corrispondente ed in cui manca l’istante della restituzione(questo specifica che
un utente può ovviamente noleggiare più volte lo stesso articolo). Se l’articolo risulta
effettivamente da riconsegnare viene calcolato il prezzo del noleggio. Se questo risulta inferiore
a 2 l’operazione risulta completata aggiornando la disponibilità dell’articolo,se invece risulta
superiore a 2
viene confrontato con il credito residuo del cliente. A questo punto per
completare la riconsegna l’utente è obbligato a ricaricare il suo credito qualora risultasse
insufficiente e ripetere l’operazione di riconsegna, mentre in caso contrario l’operazione si
conclude con successo aggiornando la disponibilità dell’articolo. In ogni caso la restituzione si
conclude aggiornando l’archivio dei noleggi con l’istante della riconsegna.
Se viene scelta l’opzione Uscita si ritorna allo stato “Idle” in cui si resta in attesa di un nuovo
cliente da identificare.
Diagramma delle classi:
or
Classe “Clienti”:
è la classe che contiene le informazioni del cliente che accede al sistema. I suoi attributi
corrispondono quindi alle informazioni che abbiamo memorizzato su un file nella forma:
Clienti.txt
Codice_cliente | Pin | Nome | Cognome $ Credito
Codice cliente e pin sono dichiarati come string ma per convenzione stabiliamo che questi siano
formati da 5 caratteri decimali; nome e cognome sono delle normali string ed infine credito è un int.
Nel file è contraddistinto da un segnaposto speciale in modo da poter facilmente modificare solo
questo campo, inoltre al credito assegniamo sempre uno spazio di 3 cifre decimali(credito massimo
999 ) per non creare problemi di sovrapposizione tra le righe del file.
Tutti i 5 attributi sono privati ed a ciascuno di essi corrisponde quindi un metodo pubblico chiamato
GetAttributo (es GetCredito) usato per leggere il valore dello stesso anche dalle altre classi. La
classe possiede un altro metodo pubblico AggCredito usato sia per incrementare(nella fase di
ricarica) che per decrementare(durante le operazioni di noleggio/restituzione) il credito dell’utente.
Il valore degli attributi è assegnato dal costruttore al momento della creazione dell’oggetto di tipo
Clienti sulla base della stringa corrispondente letta dal file di testo Clienti.txt. Il distruttore
implementa la fase di aggiornamento dei dati del cliente in base all’attributo booleano Modificato
che rivela se è necessaria una riscrittura della riga.
Classe “Articoli”:
è la classe base che contiene le informazioni comuni a tutti gli articoli che da luogo ad una
generalizzazione nelle 3 classi derivate “Film”, “Concerti” , “Documentari”.
Per la memorizzazione degli attributi degli articoli esistono 3 file separati corrispondenti alle 3
classi derivate(che includono quindi anche gli attributi della classe base):
Film.txt
Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere $ Copie_disponibili | Regista | Attore_protagonista
Concerti.txt
Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere $ Copie_disponibili | Cantante | Luogo
Documentari.txt
Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere $ Copie_disponibili | Luogo_registrazione |
Breve_descrizione
Va specificato che il codice articolo anche se dichiarato come string è formato da un carattere
minuscolo nella prima posizione(che ne identifica la categoria di appartenenza) seguito da 4 cifre
decimali(es. f0023 identifica un film, c0012 identifica un concerto …). Anche in questo caso il
segnaposto speciale è usato per individuare facilmente l’unico campo che va aggiornato, ed anche
in questo caso lasciamo uno spazio fisso di due caratteri per non creare problemi di sovrascrittura.
Tutti gli attributi della classe base sono privati e ognuno ha il corrispondente metodo GetAttributo
(GetAttributo, es. GetTitolo) per leggere il valore anche dalla classe Distributore. Gli attributi delle
classi derivate sono tutti privati, anch’essi con il relativo metodo per la lettura. Siccome gli attributi
delle classi derivate devono essere accessibili dalla classe base, abbiamo dotato tale classe di due
metodi virtuali puri implementati diversamente in ciascuna delle classi derivate. Inoltre la classe
base possiede un metodo pubblico AggCopie_disponibili usato per decrementare e incrementare il
numero delle copie disponibili al momento del noleggio e della restituzione.
Il valore di tutti gli attributi è assegnato anche in questo caso dai costruttori delle 3 classi derivate
che si differenziano per l’accesso a file diversi.
In particolare specifichiamo che il costruttore della classe base, invocato esplicitamente dai
costruttori delle classi derivate, inizializza tutti i suoi attributi mentre i due attributi che
differenziano le classi derivate sono inizializzati dai relativi costruttori.
Classe “StateMgr”:
è la classe utilizzata per controllare i cambi di stato del nostro sistema(Distributore). Questa
classe ha un solo attributo privato che corrisponde allo stato corrente (CurrState di tipo State da noi
definito come enum) in cui si trova il nostro sistema ed il cui valore può essere letto con il metodo
pubblico GetCurrState.
Questa classe è dotata di altri due metodi: uno pubblico (SetNewState) che implementa il passaggio
da uno stato all’altro del sistema controllando che esso sia consentito, ed uno privato
(PrintCurrState) che stampa a video il nuovo stato dopo ogni transizione.
Classe “Distributore”:
è la classe centrale del nostro sistema. Avendo implementato solo il lato utente del
distributore possiamo assumere che vi sia istanziato sempre uno ed un solo oggetto di questa classe.
Questa classe possiede 3 attributi privati che sono:
- Utente, è un puntatore ad un oggetto della classe Clienti che rappresenta l’utente acceduto al
sistema. La cardinalità della relazione tra la classe Distributore e la classe Clienti (0..1) esplicita che
il nostro sistema ammette al massimo la presenza di un utente alla volta. Alla creazione dell’oggetto
della classe distributore questo puntatore verrà settato a NULL, successivamente, dopo
l’identificazione dell’utente verrà effettivamente allocata l’istanza del cliente. Questa sarà poi
distrutta al momento dell’uscita del cliente dal menù principale.
- Sm, è un oggetto della classe StateMgr. La cardinalità della relazione di aggregazione(1..1) ci
indica che è sempre presente uno ed un solo oggetto della classe StateMgr istanziato al momento
della creazione dell’oggetto della classe Distributore.
- Dvd, è un vettore di puntatori ad oggetti della classe Articoli usato per permettere la creazione di
un contenitore dinamico di oggetti. La cardinalità di questa relazione(0..*) esplicita che nel sistema
possono essere istanziati da 0 a più oggetti della classe articoli; questo perché nelle fasi di
visualizzazione preleviamo dall’archivio tutti gli articoli desiderati. La scelta di utilizzare un vettore
di puntatori associata al design pattern “factory” ci permette di inserire nel vettore puntatori ad
oggetti differenti, in particolare il vettore durante le visualizzazioni verrà dinamicamente riempito
di puntatori ad oggetti delle classi derivate Film, Concerti e Documentari. L’unico metodo pubblico
della classe(oltre al costruttore e al distruttore) è il metodo Idle il quale, invocato dal costruttore
della classe, rappresenta lo stato iniziale del sistema. Questo metodo si occupa dell’acquisizione del
codice del cliente e della chiamata al metodo Riconoscimento che verifica l’esistenza in archivio del
cliente corrispondente al codice immesso e chiede il relativo Pin. Se anche questo risulta corretto
viene istanziato l’oggetto della classe Clienti e passa il controllo al metodo Scelta.
Questo metodo rappresenta il menù principale dell’applicazione, e sulla base della scelta effettuata
dall’utente invoca il relativo metodo.
Il metodo Gestione_credito visualizza il credito residuo dell’utente e permette ad esso di effettuare
l’operazione di ricarica. Il metodo Noleggio rappresenta un menù che permette all’utente di
scegliere il tipo di visualizzazione tra i 3 disponibili cui corrispondono altrettanti metodi:
- Visual_categoria visualizza tutti gli articoli disponibili di una determinata categoria scelta tramite
un altro menù e permette all’utente di selezionarne uno in particolare;
- Visual_genere visualizza tutti gli articoli disponibili di un determinato genere scelto tramite un
altro menù creato dinamicamente sulla base dei file dei generi delle tre categorie, quindi permette
all’utente di selezionarne un particolare articolo;
- Visual_novita visualizza tutti gli articoli disponibili presenti in archivio da non più di tre
mesi(confrontando la data odierna con quella d’arrivo letta dal file) e permette all’utente di
selezionarne uno in particolare;
Tutti e tre i metodi, come già detto, permettono di selezionare un articolo sulla base del suo codice,
il sistema visualizzerà quindi una scheda contenente tutte le informazioni su quell’articolo e
chiederà se lo si vuole noleggiare (metodo Visualizza_scheda).
In caso affermativo sarà invocato il metodo Ritira il quale dopo aver controllato l’effettiva
disponibilità dell’articolo e che l’utente abbia almeno i 2 necessari, aprirà l’archivio degli articoli
e vi inserirà le informazioni relative a quel noleggio. Questo metodo aggiorna inoltre il campo
delle copie disponibili dell’articolo noleggiato.
Il metodo Restituzione acquisisce il codice dell’articolo che si vuole restituire e cerca una
corrispondenza codice cliente / codice articolo nell’archivio dei noleggi considerando solo le righe
relative agli articoli non restituiti. Una volta trovata questa corrispondenza viene calcolato il prezzo
del noleggio e verificata la disponibilità di credito. Qualora questa risulti insufficiente l’operazione
di riconsegna viene interrotta invocando il metodo Gestione_credito, viceversa, se l’utente ha un
già detratti e viene
credito sufficiente, viene detratto il prezzo del noleggio a meno dei 2
aggiornato l’archivio inserendo nei rispettivi file la data ed ora di riconsegna e la nuova
disponibilità dell’articolo.
Infine il metodo Uscita non fa altro che invocare il distruttore della classe Clienti il quale apporta le
modifiche all’archivio dei clienti solo se necessario.
Classe “MyException”:
è la classe che permette di gestire gli errori che possono verificarsi durante le operazioni di
di accesso ai files. In particolare in caso di errore il programma stampa a video il tipo di errore, e se
possibile anche lo specifico file che ha generato l’errore, terminando con un’eccezione.
Libreria “TimeMgr”:
questa libreria è utilizzata per gestire il tempo(data ed ora di noleggi e restituzioni). Essa è
composta da tre funzioni che implementano tutte le operazioni che dobbiamo eseguire per la
gestione del tempo, sia sotto forma di stringa(scrittura/lettura su file) che sotto forma di variabile
del tipo predefinito tm (libreria <ctime> con relative funzioni).
- GetCurrTime: questa funzione si preoccupa di prendere l’ora di sistema e restituirla sotto forma
di stringa nel formato GG/MM/AAAA HH:MM:SS.
- Converti: questa funzione prende come parametro una stringa contenente data e ora nel formato
sopra citato e restituisce una variabile del tipo tm contenente le stesse informazioni.
- OreNoleggio: questa funzione prende come parametri due stringhe contenenti data e ora nel
formato sopra citato e restituisce la differenza in ore tra queste due.
IMPLEMENTAZIONE
File Videonolo.cpp
#include "Distributore.h"
int main(void) {
Distributore d1;
return 0;
}
//Il nostro sistema è da immaginare SEMPRE funzionante, quindi l'unica cosa che fa' il main
//è istanziare un oggetto della classe distributore.
//Per comodità il sistema è dotato di una possibilità di uscita: basta inserire la parola "stop"
//al posto del codice cliente
File Articoli.h
//dichiarazione della classe articoli
#ifndef ARTICOLI_H
#define ARTICOLI_H
#include <iostream>
#include <ctime>
using namespace std;
//classe articoli
class Articoli {
string Codice_articolo;
string Titolo;
int Anno_uscita;
string Data_arrivo;
int Copie_disponibili;
string Genere;
public:
Articoli(string DatiArticoli, int& i, int& j);
virtual ~Articoli() {}
Articoli* factory(char scelta, string Datidvd, int& i, int& j);
string GetCodice_articolo() const { return Codice_articolo; }
string GetTitolo() const { return Titolo; }
int GetAnno_uscita() const { return Anno_uscita; }
tm GetData_arrivo() const;
int GetCopie_disponibili() const { return Copie_disponibili; }
string GetGenere() const { return Genere; } //metodi per la lettura degli attributi
bool AggCopie_disponibili(bool Aggiornamento); //metodo per aggiornare il num delle copie disponibili
//funzioni virtuali pure! =0
virtual string GetInfo_specifica1() const = 0;
//ciascuna delle classi derivate possiede due attibuti
virtual string GetInfo_specifica2() const = 0; //privati contenenti le informazione specifiche di quella categoria
};
//classe derivata film
class Film: public Articoli {
string Regista;
string Attore_protagonista;
public:
Film(string DatiFilm, int& i, int& j);
string GetInfo_specifica1() const { return Regista; }
string GetInfo_specifica2() const { return Attore_protagonista; }
};
//classe derivata concerti
class Concerti: public Articoli {
string Cantante;
string Luogo;
public:
Concerti(string DatiConcerto, int& i, int& j);
string GetInfo_specifica1() const { return Cantante; }
string GetInfo_specifica2() const { return Luogo; }
};
//classe derivata documentari
class Documentari: public Articoli {
string Luogo_registrazione;
string Breve_descrizione;
public:
Documentari(string DatiDocumentario, int& i, int& j);
string GetInfo_specifica1() const { return Luogo_registrazione; }
string GetInfo_specifica2() const { return Breve_descrizione; }
};
#endif
File Articoli.cpp
#include "Articoli.h"
//costruttore articoli
Articoli::Articoli(string DatiArticoli, int& i, int& j) {
i = DatiArticoli.find('|');
j = 0; //indici temporanei per segnaposto nella stringa
Codice_articolo = DatiArticoli.substr(j, i-1);
//prendo i-1 caratteri di stringa dall'inizio (escludo anche lo spazio)
j = DatiArticoli.find('|', i+2);
i += 2; //fisso i al primo carattere del Titolo
Titolo = DatiArticoli.substr(i, (j - 1) - i);
i = DatiArticoli.find('|', j+2);
j += 2; //fisso j al primo carattere della data di arrivo
Data_arrivo = DatiArticoli.substr(j, (i - 1) - j);
j = DatiArticoli.find('|', i+2);
i += 2; //fisso i al primo carattere dell'anno di uscita
Anno_uscita = atoi(DatiArticoli.substr(i, (j - 1) - i).c_str());
i = DatiArticoli.find('$');
j += 2; //fisso j al primo carattere del Genere
Genere = DatiArticoli.substr(j, (i - 1) - j);
j = DatiArticoli.find('|', i+2);
i += 2; //fisso i al primo carattere delle Copie disponibili
Copie_disponibili = atoi(DatiArticoli.substr(i, (j - 1) - i).c_str());
}
//Fino a Copie_disponibili i 3 file sono identici(stessi dati per le 3 categorie)
////////////////////////////////////////////////////////////////////////////////
//metodo factory
Articoli* Articoli::factory(char scelta, string Datidvd, int& i, int& j) {
if (scelta == '1')
return new Film(Datidvd, i, j);
else if (scelta == '2')
return new Concerti(Datidvd, i ,j);
else if (scelta == '3')
return new Documentari(Datidvd, i, j);
else
return NULL;
} //in base a scelta istanzio un oggetto diverso(film, concerto o documentario)
////////////////////////////////////////////////////////////////////////////////
//metodo per incrementare o decrementare le copie disponibili
bool Articoli::AggCopie_disponibili(bool Aggiornamento) {
bool AggOk = true;
if(Aggiornamento) ++Copie_disponibili; //se agg = 1 viene incrementato il numero di copie disponibili (riconsegna)
else {
if(Copie_disponibili) --Copie_disponibili; //se agg = 0 viene decrementato il numero di copie disponibili (noleggio)
else AggOk = false;
}
return AggOk; //si ritorna false solo se si cerca di noleggiare un articolo con disponibilità nulla
}
////////////////////////////////////////////////////////////////////////////////
//metodo che legge la stringa della data di arrivo (GG/MM/AAAA) e costruisce una variabile di tipo tm
tm Articoli::GetData_arrivo() const {
tm temp;
temp.tm_mday = atoi((Data_arrivo.substr(0,2)).c_str());
temp.tm_mon = atoi((Data_arrivo.substr(3,2)).c_str())-1;
temp.tm_year = atoi((Data_arrivo.substr(6,4)).c_str())-1900;
temp.tm_hour = 0;
temp.tm_min = 0;
temp.tm_sec = 0;
//supponiamo che gli articoli siano arrivati alle ore 00:00:00 indipendentemente dal giorno
temp.tm_isdst = -1;
return temp;
}
////////////////////////////////////////////////////////////////////////////////
//classe derivata Film
Film::Film(string DatiFilm, int& i, int& j)
: Articoli(DatiFilm, i, j) {
i = DatiFilm.find('|', j+2);
j += 2; //fisso j al primo carattere del Regista
Regista = DatiFilm.substr(j, (i - 1) - j);
j = DatiFilm.find('\n', i+2);
i += 2; //fisso i al primo carattere dell'attore protagonista
Attore_protagonista = DatiFilm.substr(i, (j - 1) - i);
}
//Film.txt
//Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere | Copie_disponibili
Attore_protagonista
////////////////////////////////////////////////////////////////////////////////
|
Regista
|
//classe derivata concerti
Concerti::Concerti(string DatiConcerto, int& i, int& j)
: Articoli(DatiConcerto, i, j) {
i = DatiConcerto.find('|', j+2);
j += 2; //fisso j al primo carattere del Cantante
Cantante = DatiConcerto.substr(j, (i - 1) - j);
j = DatiConcerto.find('\n', i+2);
i += 2; //fisso i al primo carattere del luogo
Luogo = DatiConcerto.substr(i, (j - 1) - i);
}
//Concerti.txt
//Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere | Copie_disponibili
////////////////////////////////////////////////////////////////////////////////
| Cantante | Luogo
//classe derivata documentari
Documentari::Documentari(string DatiDocumentario, int& i, int& j)
: Articoli(DatiDocumentario, i, j) {
i = DatiDocumentario.find('|', j+2);
j += 2; //fisso j al primo carattere del Cantante
Luogo_registrazione = DatiDocumentario.substr(j, (i - 1) - j);
j = DatiDocumentario.find('\n', i+2);
i += 2; //fisso i al primo carattere del luogo
Breve_descrizione = DatiDocumentario.substr(i, (j - 1) - i);
}
//Documentari.txt
//Codice_articolo | Titolo | Data_arrivo | Anno_uscita | Genere | Copie_disponibili
Breve descrizione
////////////////////////////////////////////////////////////////////////////////
| Luogo di registrazione |
File Clienti.h
//Dichiarazione della classe Clienti
#ifndef CLIENTI_H
#define CLIENTI_H
#include <iostream>
#include <fstream>
#include "MyException.h"
using namespace std;
//Classe clienti
class Clienti {
string Codice_cliente;
string Pin;
string Nome;
string Cognome;
//attributi privati
int Credito;
bool Modificato; //l'archivio viene aggiornato solo se è variato il credito
public:
Clienti(string DatiCliente);
~Clienti();
string GetCodice_cliente() const { return Codice_cliente; }
string GetPin() const { return Pin; }
string GetNome() const { return Nome; }
string GetCognome() const { return Cognome; }
int GetCredito() const { return Credito; }
//metodi pubblici per la lettura degli attributi
void AggCredito(int Aggiornamento); //metodo per l'aggiornamento del credito
};
#endif
File Clienti.cpp
//definizione della classe clienti
#include "Clienti.h"
//costruttore
Clienti::Clienti(string DatiCliente) {
int i = DatiCliente.find('|'), j = 0; //indici temporanei per segnaposto nella stringa
Codice_cliente = DatiCliente.substr(j, i-1);
//prendo i-1 caratteri di stringa dall'inizio (escludo anche lo spazio)
j = DatiCliente.find('|', i+2);
i += 2; //fisso i al primo carattere del Pin
Pin = DatiCliente.substr(i, (j - 1) - i);
i = DatiCliente.find('|', j+2);
j += 2; //fisso j al primo carattere del nome
Nome = DatiCliente.substr(j, (i - 1) - j);
j = DatiCliente.find('$', i+2);
i += 2; //fisso i al primo carattere del cognome
Cognome = DatiCliente.substr(i, (j - 1) - i);
i = DatiCliente.find('\n', j+2);
j += 2; //fisso j al primo carattere del Credito
Credito = atoi(DatiCliente.substr(j, (i - 1) - j).c_str());
cout << Nome << ' ' << Cognome << endl;
Modificato = false;
}
////////////////////////////////////////////////////////////////////////////////
//metodo che aggiorna il credito; nel file di testo sono riservate 3 cifre decimali
void Clienti::AggCredito(int Aggiornamento) {
if(Credito + Aggiornamento > 999)
cout << "\nLa ricarica non verrà effettuata, il sistema non gestisce un credito maggiore a 999
else {
Credito += Aggiornamento;
cout << "\nIl suo nuovo credito è " << Credito << " \n";
Modificato = true;
}
!\n";
}
////////////////////////////////////////////////////////////////////////////////
//distruttore
Clienti::~Clienti() {
bool aggiornato = false;
if (Modificato) {
try {
fstream File_utenti;
File_utenti.open("c:/Progetto/Clienti.txt");
if (File_utenti.fail())
throw("Errore durante l'apertura del file Clienti.txt");
while(!File_utenti.eof() && !aggiornato) {
char temp[200];
long posizione = File_utenti.tellp();
File_utenti.getline(temp,200);
string appoggio(temp);
int i = appoggio.find('$') + 2; //i = indice del $ + 2 per scrivere sopra al vecchio credito
if(appoggio.substr(0, 5) == Codice_cliente) { //cerco la riga del cliente che è nel sistema
File_utenti.seekp(posizione + i); //mi posiziono sopra al vecchio credito...
File_utenti.width(3);
//scrivo sempre 3 cifre..
File_utenti.setf(ios::left); //..allineate a sinistra
File_utenti << Credito;
//modifico solo il campo credito delimitato da un segnaposto diverso $
//setto la condizione di uscita dal while
aggiornato = true;
}
}
File_utenti.close();
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
}
}
File Distributore.h
//Dichiarazione della classe distributore
#ifndef DISTRIBUTORE_H
#define DISTRIBUTORE_H
#include <vector>
#include <typeinfo>
#include "Articoli.h"
#include "Clienti.h"
#include "StateMgr.h"
#include "TimeMgr.h"
#include "MyException.h"
//classe distributore
class Distributore {
vector<Articoli *> dvd;
// vettore per gli articoli
//oggetto utente della classe Clienti
Clienti* Utente;
StateMgr Sm;
//oggetto della classe StateMgr
//metodi privati
bool Riconoscimento(string Cod_cli_inserito); //metodo per il riconoscimento dell'utente
void Scelta();
//metodo per la visualizzazione del menu principale
void Gestione_credito(); //implementazione stato gestione credito
void Noleggio();
bool Visual_categoria();
bool Visual_genere();
bool Visual_novita();
//prende come paramentro il codice dell'articolo da visualizzare, visual l'articolo e ritorna il suo indice nel vettore
int Visualizza_scheda(string Codice);
bool Ritira(int Codice);
//prendo come paramentro il codice articolo noleggiato
void Restituzione();
void Uscita();
public:
Distributore();
~Distributore();
void Idle();
};
#endif
//costruttore
//distruttore
File Distributore.cpp
//definizione della classe distributore
#include "Distributore.h"
//costruttore
Distributore::Distributore() {
cout << "\nAvvio del sistema in corso.....";
Utente = NULL; //punto utente a NULL
Idle(); //chiamo il metodo Idle che implementa l'omonimo stato ed il sistema è avviato!
}
////////////////////////////////////////////////////////////////////////////////
//distruttore
Distributore::~Distributore() {
cout << "\nUscita dal sistema ad opera dell'amministratore...\n";
cout << "Il distributore stà per essere spento.\n";
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato IDLE
void Distributore::Idle() {
bool uscita, acceso = true;
string cod_utente;
while(acceso) { //il distributore è sempre acceso
cout << "\n\n**********************************************************************************\n";
cout << "** Sistema VIDEONOLO disponibile, effettuare l'accesso per le vostre operazioni **\n";
cout << "**********************************************************************************\n";
uscita = false;
do {
//visualizzazione del menu per l'accesso utente
cout << '\n' << "\nInserire codice utente: ";
cin >> cod_utente;
if (cod_utente == "stop") { //per convenzione l'uscita dal sistema si effettua digitando 'stop'
acceso = false;
uscita = true;
}
else if (cod_utente.length() == 5) { //se la lunghezza della stringa digitata è esatta
for (int flag = 1, i = 0;((i < 5) && (flag)); i++)
if (isdigit(cod_utente[i])) //si controlla che sono stati digitate tutte cifre decimali
uscita = true;
else {
uscita = false;
flag = 0;
}
}
} while (!uscita); //si esce dal ciclo se è stato inserito 'stop' o un possibile codice
if (acceso && Sm.SetNewState(RICONOSCIMENTO)) //si passa allo stato riconoscimento
if (Riconoscimento(cod_utente)) //se il riconoscimento va a buon fine
if (Sm.SetNewState(SCELTA))
Scelta(); //si entra nel menù principale dell'applicazione
}
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dell stato RICONOSCIMENTO
bool Distributore::Riconoscimento(string Cod_cli_inserito) {
bool Cod_Pin = true; //flag per il codice del pin
bool Riconosciuto = false; //flag utente riconosciuto
string Pin; //variabile per il confronto del pin inserito con quello memrizzato
char Temp[6]; //appoggio per il codice cliente e pin
char DatiUtente[150]; //stringa per la lettura da file
try {
ifstream FileUtenti("C:/Progetto/Clienti.txt"); //istanzio una variabile per la lettura di Clienti.txt ed apro in lettura
if(FileUtenti.fail())
throw MyException("Errore in apertura del file Clienti.txt");
//leggo il file confrontando il codice cliente inserito con il pin corrispondente
while(!FileUtenti.eof() && Cod_Pin && !Riconosciuto) {
FileUtenti.getline(DatiUtente, 150);
strncpy(Temp, DatiUtente, 5);
Temp[5] = '\0';
//completo l'array di char per l'appoggio del cod cliente letto dal file
if (Cod_cli_inserito == Temp) {
//se il codice inserito ha una corrispondenza nell'archivio dei clienti
cout << "\nInserisci il Pin: ";
cin >> Pin;
for(int i = 0;(i < 5); i++)
Temp[i] = DatiUtente[i+8];
Temp[5] = '\0';
//prelevo il pin dalla stringa letta dal file
if (Temp == Pin) {
//controllo che il pin sia corretto
cout << "\nAccesso Effettuato: ";
//chiamo il costruttore per istanziare il cliente appena riconosciuto
Utente = new Clienti(DatiUtente);
Riconosciuto = true;
}
else { //il pin inserito non corrisponde a quello in archivio
cout << "\n" << "Errore!! Pin Errato!!";
Sm.SetNewState(IDLE); //torno allo stato idle -> pin errato
Cod_Pin = false;
}
}
}
if (!Riconosciuto && Cod_Pin) { //se il codice cliente inserito non è in archivio
Sm.SetNewState(IDLE); //torno allo stato idle -> codice cliente errato
Cod_Pin = false;
}
FileUtenti.close();
//chiudo il file Clienti.txt
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
return Riconosciuto;
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato SCELTA
void Distributore::Scelta() {
bool flag = true;
do { //visualizzo il menù principale e attendo una scelta
char scelta;
string tmp_scelta;
cout << "\n----- MENU PRINCIPALE -----\n";
cout << " 1 - GESTIONE CREDITO\n";
cout << " 2 - NOLEGGIO\n";
cout << " 3 - RESTITUZIONE\n";
cout << " 4 - USCITA\n\n";
cout << "Scelta: ";
cin >> tmp_scelta;
//controllo dell'inserimento!
if (tmp_scelta.length() == 1)
scelta = tmp_scelta[0];
else
scelta = '5';
switch(scelta) {
case '1': {
if (Sm.SetNewState(GESTIONE_CREDITO))
Gestione_credito();
break;
}
case '2': {
if (Sm.SetNewState(NOLEGGIO))
Noleggio();
break;
}
case '3': {
if (Sm.SetNewState(RESTITUZIONE))
Restituzione();
break;
}
case '4': {
if (Sm.SetNewState(IDLE)){
Uscita();
flag = false;
}
break;
}
default:
cout << "\nERRORE DI INSERIMENTO!!\n";
}
} while(flag);
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato GESTIONE CREDITO
void Distributore::Gestione_credito() {
char scelta;
int credito = 1;
do { //visualizzazione del menù per la gestione del credito
string tmp_scelta;
cout << "\nCredito Residuo = " << Utente->GetCredito() << "
cout << "\nScegliere l'operazione da effettuare: \n";
cout << "1 - Ricarica credito\n";
cout << "2 - Torna al menù principale\n";
cout << "Scelta: ";
cin >> tmp_scelta;
.\n";
if (tmp_scelta.length() == 1) {
//controllo dell'inserimento!
scelta = tmp_scelta[0];
if (scelta == '1') {
do {
cout << "\nRICARICA DEL CREDITO:\n";
cout << "Importi consentiti 5, 10, 20 Euro\n";
cout << "Inserire l'importo (0 per uscire): ";
cin >> tmp_scelta;
//controllo dell'inserimento!
if (tmp_scelta.length() <= 2 && isdigit(tmp_scelta[0]))
if (isdigit(tmp_scelta[1]) || tmp_scelta.length() == 1) {
credito = atoi(tmp_scelta.c_str());
if (credito == 5 || credito == 10 || credito == 20) //se è stato inserito un importo corretto
Utente->AggCredito(credito); //viene aggiornato il credito
else if (credito != 0)
cout << "\nImporto non consentito, la ricarica non verrà effettuata!\n";
}
} while(credito);
}
}
} while (scelta != '2');
Sm.SetNewState(SCELTA);
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato NOLEGGIO
void Distributore::Noleggio() {
bool uscita = false;
bool art_ritirato = false; //teniamo traccia dell'avvenuto noleggio perchè in caso affermativo
//anzichè rivisualizzare il seguente menù torniamo al menù principale
do {
char scelta;
string tmp_scelta;
cout << "\n---------- NOLEGGIO ----------\n";
cout << " 1 - Visualizza per categoria\n";
cout << " 2 - Visualizza per genere\n";
cout << " 3 - Visualizza novità\n";
cout << " 4 - Torna al menù principale\n";
cout << "\n Scelta: ";
cin >> tmp_scelta;
//controllo dell'inserimento!
if (tmp_scelta.length() == 1)
scelta = tmp_scelta[0];
else
scelta = '5';
switch(scelta) {
case '1': {
if (Sm.SetNewState(VISUAL_CATEGORIA))
art_ritirato = Visual_categoria();
break;
}
case '2': {
if (Sm.SetNewState(VISUAL_GENERE))
art_ritirato = Visual_genere();
break;
}
case '3': {
if (Sm.SetNewState(VISUAL_NOVITA))
art_ritirato = Visual_novita();
break;
}
case '4': {
if (Sm.SetNewState(SCELTA))
uscita = true;
break;
}
default:
cout << "\nERRORE DI INSERIMENTO!!\n";
}
}
while (!uscita && !art_ritirato);
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato VISUALIZZA PER CATEGORIA
bool Distributore::Visual_categoria() {
char scelta;
bool uscita = false;
bool art_ritirato = false;
do { //visualizzo il menù di scelta della categoria
string tmp_scelta;
ifstream File; //istanzio una variabile per la lettura del file scelto
cout << "\n---- Scegli la categoria ----\n";
cout << " 1 - Film\n";
cout << " 2 - Concerti\n";
cout << " 3 - Documentari\n";
cout << " 4 - Ritorna al menù precendente\n\n";
cout << "Scelta: ";
cin >> tmp_scelta;
//controllo dell'inserimento!
if (tmp_scelta.length() == 1)
scelta = tmp_scelta[0];
else
scelta = '5';
try {
switch(scelta) { //sulla base della categoria scelta apro il relativo file
case '1': {
File.open("C:/Progetto/Film.txt"); break;
}
case '2': {
File.open("C:/Progetto/Concerti.txt"); break;
}
case '3': {
File.open("C:/Progetto/Documentari.txt"); break;
}
case '4': {
if (Sm.SetNewState(NOLEGGIO))
uscita = true;
break;
}
default:
cout << "\nERRORE DI INSERIMENTO!!\n";
}
if (!uscita && scelta != '5') { //se non ho scelto di tornare al menù precedente o non ho sbagliato inserimento...
if(File.fail())
throw MyException("Errore in apertura del file della categoria desiderata");
int n_dvd = 0;
bool disponibili = false;
while(!File.eof()) {
char appoggio[200];
int i = 0, j = 0; //indici usati per l'elaborazione della stringa appoggio
File.getline(appoggio, 200);
string str_appoggio(appoggio); //metto temporaneamente appoggio in una stringa...
if (!str_appoggio.empty()) { //per evitare di elaborare l'ultima riga che è una stringa vuota e genera un errore anomalo!
Articoli* articolo = articolo->factory(scelta, appoggio, i, j); //istanzio l'oggetto relativo alla categoria scelta
dvd.push_back(articolo); //inserisco l'oggetto in un vettore di puntatori ad articoli
if (dvd[n_dvd]->GetCopie_disponibili() > 0) { //vengono visualizzati solo gli articoli disponibili
cout << '\n' << dvd[n_dvd]->GetCodice_articolo() << ' ' << dvd[n_dvd]->GetTitolo();
disponibili = true;
}
n_dvd++;
}
}
if(!disponibili) //se non ho visualizzato nemmeno un articolo
cout << "\nLa ricerca effettuata non ha trovato alcun risultato!";
cout << "\n\nScegli un articolo sulla base del codice(0 per tornare al menù precedente): ";
string codice;
cin >> codice;
if (codice != "0") { //se non ho digitato 0 per tornare indietro
unsigned int indice_dvd = Visualizza_scheda(codice); //visualizzo l'articolo,mem l'indice cui corrisponde sul vettore
if (indice_dvd <= dvd.size()) //se voglio noleggiare l'articolo visualizzato
if (Sm.SetNewState(RITIRA)) {
art_ritirato = Ritira(indice_dvd); //concludo l'operazione di noleggio
if (art_ritirato)
Sm.SetNewState(SCELTA);
else
Sm.SetNewState(VISUAL_CATEGORIA);
}
}
File.close(); //chiudo il file
//dealloco la memoria precedentemente allocata per contenere gli articoli
for(unsigned int i = 0; i < dvd.size(); i++)
delete dvd[i];
dvd.clear(); //svuoto il vettore per permettere visualizzazioni successive o per uscire
}
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
} while (!uscita && !art_ritirato);
return art_ritirato;
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato VISUALIZZA PER GENERE
bool Distributore::Visual_genere() {
bool art_ritirato = false;
int num_genere = 0;
string codice; //variabile che usero per scegliere un genere
do {
char scelta;
bool disponibili = false;
//queste variabili tengono conto del numero dei generi per ogni categoria per creare un menù numerato dinamicamente
int film = 0, concerti = 0, documentari = 0;
vector <string> Generi; //vettore che contiene tutti i generi trovati nei tre file
codice = "0";
num_genere = 0;
cout << "\n------ Visualizzazione degli articoli per genere ------\n";
cout << "\nI genere disponibili sono:\n";
try {
//Lettura dei generi dei film
cout << " Film:\n";
ifstream File_f("c:/Progetto/Generi_film.txt");
if (File_f.fail())
throw("Errore durante l'apertura del file Generi_film.txt");
while(!File_f.eof()) {
char appoggio[15];
File_f.getline(appoggio, 15);
cout << " " << ++film << " - " << appoggio << '\n';
Generi.push_back(appoggio);
}
File_f.close();
//Lettura dei generi dei concerti
cout << "\n Concerti:\n";
ifstream File_c("c:/Progetto/Generi_concerti.txt");
if (File_c.fail())
throw("Errore durante l'apertura del file Generi_concerti.txt");
while(!File_c.eof()) {
char appoggio[15];
File_c.getline(appoggio, 15);
cout << " " << film + (++concerti) << " - " << appoggio << '\n';
Generi.push_back(appoggio);
}
File_c.close();
//Lettura dei generi dei documentari
cout << "\n Documentari:\n";
ifstream File_d("c:/Progetto/Generi_documentari.txt");
if (File_d.fail())
throw("Errore durante l'apertura del file Generi_documentari.txt");
while(!File_d.eof()) {
char appoggio[15];
File_d.getline(appoggio, 15);
cout << " " << film + concerti + (++documentari) << " - " << appoggio << '\n';
Generi.push_back(appoggio);
}
File_d.close();
//scelta del genere desiderato
int n_dvd = 0;
do {
string temp;
bool ok = true;
cout << "\nScegli un genere inserendo il numero(0 per tornare al menù precedente): ";
cin >> temp;
for (unsigned int i = 0; i < temp.length(); i++) //controllo dell'input
if (!isdigit(temp[i]))
ok = false;
if (ok)
num_genere = atoi(temp.c_str());
else
num_genere = -1;
} while(num_genere < 0 || (num_genere > (film + concerti + documentari)));
if (num_genere != 0) { //se non si è scelto di tornare al menù precedente
ifstream FileArt;
if (num_genere <= film) { //apro il file a seconda del genere scelto
FileArt.open("c:/Progetto/Film.txt");
scelta = '1';
}
else if (num_genere <= concerti + film) {
FileArt.open("c:/Progetto/Concerti.txt");
scelta = '2';
}
else {
FileArt.open("c:/Progetto/Documentari.txt");
scelta = '3';
}
if (FileArt.fail())
throw("\nErrore in apertura del file degli articoli");
while(!FileArt.eof()) {
char appoggio[200];
int i = 0, j = 0; //indici usati per l'elaborazione della stringa appoggio
FileArt.getline(appoggio, 200);
string str_appoggio(appoggio); //metto temporaneamente appoggio in una stringa...
if (!str_appoggio.empty()) {
//per evitare di elaborare l'ultima riga che è una stringa vuota e genera un errore anomalo!
Articoli* articolo = articolo->factory(scelta, appoggio, i, j);
dvd.push_back(articolo);
if (dvd[n_dvd]->GetCopie_disponibili() > 0 && (dvd[n_dvd]->GetGenere() == Generi[num_genere-1])) {
//vengono visualizzati solo gli articoli disponibili tra quelli del genere scelto
cout << "\n" << dvd[n_dvd]->GetCodice_articolo() << ' ' << dvd[n_dvd]->GetTitolo();
disponibili = true;
}
n_dvd++;
}
}
FileArt.close();
if(!disponibili)
cout << "\nLa ricerca effettuata non ha trovato alcun risultato!";
cout << "\n\nScegli un articolo sulla base del codice(0 per tornare al menù precedente): ";
cin >> codice;
if (codice != "0") { //se non ho digitato 0 per tornare indietro
unsigned int indice_dvd = Visualizza_scheda(codice); //visualizzo l'articolo e mem l'indice a cui corrisponde sul vettore
if (indice_dvd <= dvd.size()) //se voglio noleggiare l'articolo visualizzato
if (Sm.SetNewState(RITIRA)) {
art_ritirato = Ritira(indice_dvd); //concludo l'operazione di noleggio
if (art_ritirato)
Sm.SetNewState(SCELTA);
else
Sm.SetNewState(VISUAL_GENERE); //se non riesco a ritirare per mancanza di credito
}
}
else //se voglio tornare al menù precedente
Sm.SetNewState(VISUAL_GENERE);
}
else //se voglio tornare al menù del noleggio
Sm.SetNewState(NOLEGGIO);
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
//dealloco la memoria precedentemente allocata per contenere gli articoli
for(unsigned int i = 0; i < dvd.size(); i++)
delete dvd[i];
dvd.clear(); //svuoto il vettore per permettere visualizzazioni successive o per uscire
Generi.clear(); //rimozione degli elementi dal vettore
} while((codice == "0" && num_genere != 0) || (!art_ritirato && num_genere != 0));
return art_ritirato;
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato VISUALIZZA NOVITA'
bool Distributore::Visual_novita() {
bool art_ritirato = false;
string codice;
const int LIMITE_NOVITA = 24 * 90 * 3600; //sono novità gli articoli presenti da non più di tre mesi espressi
in secondi(24 ore * 90 giorni * 3600 secondi)
do {
cout << "\nVerranno visualizzati tutti gli articoli presenti in archivio da non più di tre mesi\n";
try {
char scelta;
int n_dvd = 0;
bool disponibili = false;
for(int i = 1; i <= 3; i++) {
ifstream FileArt;
if (i == 1) { //apro il file a seconda della categoria
FileArt.open("c:/Progetto/Film.txt");
scelta = '1';
}
else if (i == 2) {
FileArt.open("c:/Progetto/Concerti.txt");
scelta = '2';
}
else {
FileArt.open("c:/Progetto/Documentari.txt");
scelta = '3';
}
//lettura del file aperto
if (FileArt.fail())
throw("\nErrore in apertura del file degli articoli!");
while(!FileArt.eof()){
char appoggio[200];
int i = 0, j = 0; //indici usati per l'elaborazione della stringa appoggio
FileArt.getline(appoggio, 200);
string str_appoggio(appoggio); //metto temporaneamente appoggio in una stringa...
if (!str_appoggio.empty()) {
//per evitare di elaborare l'ultima riga che è una stringa vuota e genera un errore anomalo!
Articoli* articolo = articolo->factory(scelta, appoggio, i, j);
dvd.push_back(articolo);
time_t temp = time(NULL); //prendo l'ora corrente
//vengono visualizzate le novità disponibili
if ((dvd[n_dvd]->GetCopie_disponibili() > 0) && (difftime(temp, mktime(&dvd[n_dvd]->GetData_arrivo())) < LIMITE_NOVITA )) {
cout << "\n" << dvd[n_dvd]->GetCodice_articolo() << ' ' << dvd[n_dvd]->GetTitolo();
disponibili = true;
}
n_dvd++;
}
}
FileArt.close();
}
//scelta dell'articolo
if(!disponibili)
cout << "\nLa ricerca effettuata non ha trovato alcun risultato!";
cout << "\n\nScegli un articolo sulla base del codice(0 per tornare al menù precedente): ";
cin >> codice;
if (codice != "0") { //se non ho digitato 0 per tornare indietro
unsigned int indice_dvd = Visualizza_scheda(codice); //visualizzo l'articolo e mem l'indice a cui corrisponde sul vettore
if (indice_dvd <= dvd.size()) //se voglio noleggiare l'articolo visualizzato
if (Sm.SetNewState(RITIRA)) {
art_ritirato = Ritira(indice_dvd); //concludo l'operazione di noleggio
if (art_ritirato)
Sm.SetNewState(SCELTA);
else
Sm.SetNewState(VISUAL_NOVITA);
}
}
for(unsigned int i = 0; i < dvd.size(); i++)
delete dvd[i];
dvd.clear(); //svuoto il vettore per permettere visualizzazioni successive o per uscire
if (codice == "0")
Sm.SetNewState(NOLEGGIO);
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
} while(!art_ritirato && codice != "0");
return art_ritirato;
}
////////////////////////////////////////////////////////////////////////////////
//metodo per la visualizzazione della scheda di un articolo contenente tutte le sue informazioni
int Distributore::Visualizza_scheda(string Codice) {
bool trovato = false;
for (unsigned int indice = 0; indice < dvd.size(); indice++) {
if (Codice == dvd[indice]->GetCodice_articolo()) {
trovato = true;
cout << "\nTitolo: " << dvd[indice]->GetTitolo();
cout << "\nAnno di uscita: " << dvd[indice]->GetAnno_uscita();
cout << "\nGenere: " << dvd[indice]->GetGenere();
//identificazione run-time del tipo di articolo per stampare le diverse informazioni specifiche
if (typeid(*dvd[indice]) == typeid(Film)) {
cout << "\nRegista: " << dvd[indice]->GetInfo_specifica1();
cout << "\nAttore Protagonista: " << dvd[indice]->GetInfo_specifica2();
}
else if (typeid(*dvd[indice]) == typeid(Concerti)) {
cout << "\nCantante: " << dvd[indice]->GetInfo_specifica1();
cout << "\nLuogo del concerto: " << dvd[indice]->GetInfo_specifica2();
}
else {
cout << "\nLuogo di registrazione: " << dvd[indice]->GetInfo_specifica1();
cout << "\nBreve descrizione: " << dvd[indice]->GetInfo_specifica2();
}
char risp;
string tmp_risp;
do {
cout << "\n\nVuoi noleggiare questo articolo? s/n\n Scelta: ";
cin >> tmp_risp;
if (tmp_risp.length() == 1)
//controllo dell'input
risp = tmp_risp[0];
else
risp = '1';
} while (risp != 's' && risp != 'n');
if (risp == 's')
return indice;
}
}
if (!trovato)
cout << "\nCodice errato! Ripetere l'operazione!\n";
return dvd.size()+1;
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato RITIRA
bool Distributore::Ritira(int Codice) {
bool noloOK = true;
if(dvd[Codice]->AggCopie_disponibili(0)) {//decremento la disponibilità dell'articolo noleggiato
if (Utente->GetCredito() >= 2) {
cout << "\n\nArticolo Noleggiato! Verranno detratti 2
dal suo credito";
Utente->AggCredito(-2);
try {
//scrittura sul file noleggi.txt le informazioni relative al noleggio
ofstream Noleggi;
Noleggi.open("c:/Progetto/Noleggi.txt", ios::app);
if (Noleggi.fail())
throw("Errore durante l'apertura del file Noleggi.txt");
string data_nol(GetCurrTime()); //prendo l'ora attuale
Noleggi << "GG/MM/AAAA HH:MM:SS | " << Utente->GetCodice_cliente() << " | " << dvd[Codice]->GetCodice_articolo() << " | "
<< data_nol << endl;
Noleggi.close();
//aggiornamento dell'archivio degli articoli
fstream FileDvd;
//apro il file identificando il tipo runtime
if(typeid(*dvd[Codice]) == typeid(Film))
FileDvd.open("c:/Progetto/Film.txt");
else if(typeid(*dvd[Codice]) == typeid(Concerti))
FileDvd.open("c:/Progetto/Concerti.txt");
else
FileDvd.open("c:/Progetto/Documentari.txt");
if(FileDvd.fail())
throw("Errore in apertura del file di archivio");
//leggo il file aperto e cerco l'articolo noleggiato per aggiornare le informazioni(copie disponibili)
bool aggiornato = false;
while(!FileDvd.eof() && !aggiornato){
char temp[200];
long posizione = FileDvd.tellp(); //memorizzo il puntatore alla posizione corrente
FileDvd.getline(temp,200); //leggo una riga
string appoggio(temp);
int p = appoggio.find('$') + 2; //mi posiziono per scrivere sopra la vecchia disponibilità
if(appoggio.substr(0, 5) == dvd[Codice]->GetCodice_articolo()) {
//se la riga letta corrisponde al file noleggiato
FileDvd.seekp(posizione + p); //mi riposiziono all'inizio della riga appena letta
FileDvd.width(2); //sono riservate due cifre decimali per il campo delle copie disponibili
FileDvd.setf(ios::left); //allineate a sinistra
FileDvd << dvd[Codice]->GetCopie_disponibili();
//riaggiorno solo il campo della disponibilità
aggiornato = true;
//una volta aggiornato il file esco dal ciclo
}
}
FileDvd.close();
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
}
else {
cout << "\nIl suo credito non è sufficiente per il noleggio!\n";
noloOK = false;
}
}
else {
cout << "\nArticolo non disponibile, ripetere l'operazione";
noloOK = false;
}
return noloOK;
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato RESTITUZIONE
void Distributore::Restituzione() {
string codice;
bool restituito = false;
cout << "\nInserisci il codice dell'articolo che vuoi restituire(0 per tornare al menù principale): ";
cin >> codice;
if (codice != "0") {
cout << "\nRicerca dell'articolo in corso...";
bool uscita = false;
try {
fstream FileNoleggi("c:/Progetto/Noleggi.txt");
if (FileNoleggi.fail())
throw("\nErrore in apertura del file Noleggi.txt");
while(!FileNoleggi.eof() && !uscita) {
char appoggio[60];
long posizione = FileNoleggi.tellp();
//memorizzo la posizione
FileNoleggi.getline(appoggio, 60); //leggo i dati dal file dei noleggi
if (appoggio[0] == 'G') { //indica che l'articolo non è stato ancora restituito
string datinolo(appoggio); //copio i dati letti su una stringa per l'elaborazione
string cliente = datinolo.substr(22, 5); //estraggo il codice del cliente
if (cliente == Utente->GetCodice_cliente()) {
string cod_art = datinolo.substr(30, 5); //estraggo il codice dell'articolo
if (cod_art == codice) {
cout << ".TROVATO!\n";
string DataNolo = datinolo.substr(38, 19); //copio i dati relativi al momento del noleggio
//calcolo del prezzo del noleggio
string DataCorr(GetCurrTime()); //variabile in cui memorizzo la data della riconsegna
int ore_nolo = OreNoleggio(DataNolo, DataCorr);
cout << "\nL'articolo è stato noleggiato per " << ore_nolo << " ore";
int prezzo = ((ore_nolo >= 12) && (ore_nolo <= 24) || (ore_nolo > 24)) + 2 * (ore_nolo / 24) * (ore_nolo > 24);
cout << "\nIn totale hai speso " << prezzo + 2 << "
(2
erano stati detratti al momento del noleggio!)";
if (prezzo <= Utente->GetCredito()) { //se il credito è sufficiente
restituito = true;
Utente->AggCredito(-prezzo);
FileNoleggi.seekp(posizione); //mi riposiziono all'inizio della riga
//Inserisco data e ora della restituzione
FileNoleggi << DataCorr;
//aggiorno la disponibilità del file dell'articolo restituito
fstream FileArticoli;
char c = codice[0];
char scelta;
switch (c) { //in base al primo carattere del codice articolo conosco la categoria di appartenenza
case 'f': {
FileArticoli.open("c:/Progetto/Film.txt");
scelta = '1';
break;
}
case 'c':{
FileArticoli.open("c:/Progetto/Concerti.txt");
scelta = '2';
break;
}
case 'd':{
FileArticoli.open("c:/Progetto/Documentari.txt");
scelta = '3';
break;
}
}
if (FileArticoli.fail())
throw ("\nErrore in apertura del file degli articoli!\n");
while (!FileArticoli.eof() && !uscita) {
char temp[200];
long posizione = FileArticoli.tellp();
FileArticoli.getline(temp,200);
string appoggio(temp);
//mi posiziono per scrivere sopra la vecchia disponibilita
int p = appoggio.find('$') + 2;
if(appoggio.substr(0, 5) == codice) {
//ho trovato l'articolo noleggiato in archivio
int i = 0, j = 0;
Articoli* articolo = articolo->factory(scelta, appoggio, i, j); //istanzio un oggetto corrisp all'art da rest
strftime(temp, 11, "%d/%m/%Y", &articolo->GetData_arrivo());
articolo->AggCopie_disponibili(1);
FileArticoli.seekp(posizione + p);
//incremento la disponibilità dell'articolo restituito
FileArticoli.width(2); //sono riservate due cifre decimali per il campo delle copie disponibili
FileArticoli.setf(ios::left); //allineate a sinistra
FileArticoli << articolo->GetCopie_disponibili();
//riaggiorno solo il campo delle disponibilita
delete articolo;
uscita = true;
}
}
FileArticoli.close();
Sm.SetNewState(SCELTA);
}
else {
cout << "\nL'operazione non può essere completata!!\nRicaricare il credito\n";
if(Sm.SetNewState(GESTIONE_CREDITO)) {
Gestione_credito();
uscita = true;
}
}
}
}
}
else
if (!restituito)
cout << ".";
}
if(!restituito && !uscita) {
cout << ".NON TROVATO!";
Sm.SetNewState(SCELTA);
}
FileNoleggi.close();
}
catch(MyException e) {
cout << e.getError();
exit(1); //Termino il programma!!!
}
}
else
Sm.SetNewState(SCELTA);
}
////////////////////////////////////////////////////////////////////////////////
//implementazione dello stato USCITA
void Distributore::Uscita() {
cout << "\n" << Utente->GetNome() << ' ' << Utente->GetCognome() << " è uscito dal sistema...\n";
cout << "ARRIVEDERCI E GRAZIE!!!\n\n\n";
delete Utente; //viene invocato il distruttore che apporta le modifiche al file se necessario
}
////////////////////////////////////////////////////////////////////////////////
File MyException.h
//Dichiarazione e implementazione inline della libreria di gestione delle eccezioni
#ifndef MY_EXCEPTION
#define MY_EXCEPTION
#include <exception>
using namespace std;
class MyException : public exception {
string tipoErr;
public:
MyException(string s) { tipoErr = s; }
string getError() { return tipoErr; }
};
#endif
File StateMgr.h
//dichiarazione della libreria che implementa lo state manager
#ifndef STATEMGR_H
#define STATEMGR_H
#include <iostream>
using namespace std;
//Tipo di dato enumerato che rappresenta tutti gli stati del sistema
enum State {
IDLE,
RICONOSCIMENTO,
SCELTA,
GESTIONE_CREDITO,
NOLEGGIO,
VISUAL_CATEGORIA,
VISUAL_GENERE,
VISUAL_NOVITA,
RITIRA,
RESTITUZIONE
};
//classe state manager
class StateMgr {
State CurrState; //attributo per lo stato corrente
void PrintCurrState() const; //metodo per la stampa dello stato corrente
public:
StateMgr(); //costruttore
State GetCurrState() const { return CurrState; } //metodo per la lettura dello stato corrente
bool SetNewState(State NewState); //metodo per cambiare di stato
};
#endif
File StateMgr.cpp
//Implementazione della classe State Manager
#include "StateMgr.h"
//costruttore
StateMgr::StateMgr() {
//quando istanzio un oggetto del tipo StateMgr(accensione del sistema),
//il sistema è automaticamente nello stato Idle
CurrState = IDLE;
}
////////////////////////////////////////////////////////////////////////////////
//metodo per la stampa dello stato corrente
void StateMgr::PrintCurrState() const {
switch (CurrState) {
case IDLE:
cout << "\n--- STATO
case RICONOSCIMENTO:
cout << "\n--- STATO
case SCELTA:
cout << "\n--- STATO
case GESTIONE_CREDITO:
cout << "\n--- STATO
case NOLEGGIO:
cout << "\n--- STATO
case VISUAL_CATEGORIA:
cout << "\n--- STATO
case VISUAL_GENERE:
cout << "\n--- STATO
case VISUAL_NOVITA:
cout << "\n--- STATO
case RITIRA:
cout << "\n--- STATO
case RESTITUZIONE:
cout << "\n--- STATO
}
IDLE ---\n"; break;
RICONOSCIMENTO ---\n"; break;
SCELTA ---\n"; break;
GESTIONE CREDITO ---\n"; break;
NOLEGGIO ---\n"; break;
VISUALIZZA PER CATEGORIA ---\n"; break;
VISUALIZZA PER GENERE ---\n"; break;
VISUALIZZA NOVITA' ---\n"; break;
RITIRA ---\n"; break;
RESTITUZIONE ---\n"; break;
}
////////////////////////////////////////////////////////////////////////////////
//metodo per il passaggio di stato
bool StateMgr::SetNewState(State NewState) {
bool trOK = false; //variabile per controllare la corretta transizione
switch (NewState) {
case IDLE:
if (CurrState == SCELTA || CurrState == RICONOSCIMENTO) trOK = true;
break;
case RICONOSCIMENTO:
if (CurrState == IDLE) trOK = true;
break;
case SCELTA:
if (CurrState == GESTIONE_CREDITO || CurrState == RICONOSCIMENTO || CurrState == NOLEGGIO
|| CurrState == RITIRA || CurrState == RESTITUZIONE) trOK = true;
break;
case GESTIONE_CREDITO:
if (CurrState == SCELTA || CurrState == RESTITUZIONE) trOK = true;
break;
case NOLEGGIO:
if (CurrState == SCELTA || CurrState == VISUAL_CATEGORIA
|| CurrState == VISUAL_GENERE || CurrState == VISUAL_NOVITA) trOK = true;
break;
case VISUAL_CATEGORIA:
if (CurrState == NOLEGGIO || CurrState == RITIRA) trOK = true;
break;
case VISUAL_GENERE:
if (CurrState == NOLEGGIO || CurrState == RITIRA) trOK = true;
break;
case VISUAL_NOVITA:
if (CurrState == NOLEGGIO || CurrState == RITIRA) trOK = true;
break;
case RITIRA:
if (CurrState == VISUAL_CATEGORIA || CurrState == VISUAL_GENERE || CurrState == VISUAL_NOVITA) trOK = true;
break;
case RESTITUZIONE:
if (CurrState == SCELTA) trOK = true;
break;
}
if (trOK) CurrState = NewState;
PrintCurrState();
return trOK;
}
////////////////////////////////////////////////////////////////////////////////
File TimeMgr.h
//Dichiarazione della libreria per la gestione del tempo
#ifndef TIMEMGR_H
#define TIMEMGR_H
#include <iostream>
#include <ctime>
using namespace std;
string GetCurrTime(); //funzione che scrive su una stringa data e ora correnti
int OreNoleggio(string DataNolo, string DataCorr); //funzione che esegue calcola la durata del noleggio
tm Converti(string Data); //funzione che converte una stringa in un tm
#endif
File TimeMgr.cpp
#include "TimeMgr.h"
//funzione che scrive su una stringa data e ora correnti
string GetCurrTime() {
char timer[25];
time_t temp;
temp = time(NULL);
strftime(timer, 25, "%d/%m/%Y %H:%M:%S", localtime(&temp));
string ris(timer);
return ris;
//scrivo in timer la data atturale nel formato GG/MM/AAAA HH:MM:SS
//metto la data attuale nella string di ritorno
}
////////////////////////////////////////////////////////////////////////////////
//funzione che calcola la durata del noleggio in ore
int OreNoleggio(string DataNolo, string DataCorr) {
//variabili contenenti la data attuale e di noleggio in strutture tm
tm tempoC = Converti(DataCorr), tempoN = Converti(DataNolo);
//ritorno la differenza in ore tra le due variabili
return difftime(mktime(&tempoC), mktime(&tempoN))/3600;
}
////////////////////////////////////////////////////////////////////////////////
//funzione che converte una stringa in tm
tm Converti(string Data) {
tm tempo;
string appoggio;
//giorno
appoggio = Data.substr(0,2);
tempo.tm_mday = atoi(appoggio.c_str());
appoggio.erase();
//mese
appoggio = Data.substr(3,2);
tempo.tm_mon = atoi(appoggio.c_str())-1;
appoggio.erase();
//anno
appoggio = Data.substr(6,4);
tempo.tm_year = atoi(appoggio.c_str())-1900;
appoggio.erase();
//ore
appoggio = Data.substr(11,2);
tempo.tm_hour = atoi(appoggio.c_str());
appoggio.erase();
//minuti
appoggio = Data.substr(14,2);
tempo.tm_min = atoi(appoggio.c_str());
appoggio.erase();
//secondi
appoggio = Data.substr(17,2);
tempo.tm_sec = atoi(appoggio.c_str());
appoggio.erase();
//ora legale (-1 = assenza di informazioni)
tempo.tm_isdst = -1;
return tempo;
}
////////////////////////////////////////////////////////////////////////////////
TESTING DEL PROGRAMMA
Per effettuare il testing del programma abbiamo scelto la tecnica “black-box” per evidenziare gli
aspetti funzionali dell’applicazione. Come sappiamo questa tecnica non si interessa della struttura
interna dell’applicazione (aspetto curato dalla tecnica di collaudo “white-box”), tuttavia nella fase
di codifica ad ogni modulo implementato e integrato abbiamo eseguito numerosi e specifici
controlli volti a percorrere più volte tutti i cammini logici del programma. Possiamo quindi
affermare con certezza che non esiste nessuna istruzione che non viene eseguita almeno una volta e
che non esistono cicli infiniti.
Per eseguire la tecnica “black-box” dobbiamo navigare l’interfaccia utente sottoponendo il
programma a dei sottoinsiemi di input che coprono tutta la suddivisione del dominio in classi di
equivalenza valutando anche dove è necessario i valori limite.
A programma avviato appare la schermata iniziale:
Avvio del sistema in corso.....
**********************************************************************************
** Sistema VIDEONOLO disponibile, effettuare l'accesso per le vostre operazioni **
**********************************************************************************
Inserire codice utente:
Evitiamo di ripetere questa schermata in tutte le fasi di test in modo da evidenziare soltanto le
risposte all’input immesso.
Tutte le prove sono state effettuate facendo loggare al sistema un utente creato appositamente per
effettuare il testing e modificando manualmente il file quando necessario.
11111 | 11111 | Utente
| Di Prova $ 41
Come primo test abbiamo verificato il comportamento del programma durante la fase di accesso al
sistema:
Inserire codice utente: caratteri
Inserire codice utente: 12c45
Inserire codice utente: 12345
--- STATO RICONOSCIMENTO ----- STATO IDLE --**********************************************************************************
** Sistema VIDEONOLO disponibile, effettuare l'accesso per le vostre operazioni **
**********************************************************************************
Inserire codice utente: 11111
--- STATO RICONOSCIMENTO --Inserisci il Pin:
Sopra sono riportate quattro prove di accesso al sistema comprendenti i tre inserimenti possibili:
• Inserimento di un codice con lunghezza diversa da 5 caratteri o contenente almeno un
carattere non numerico -> codice non accettato, richiesta nuovo codice.
• Inserimento di un codice con lunghezza pari a 5 caratteri o contenente almeno un carattere
non numerico -> codice non accettato, richiesta nuovo codice.
• Inserimento di un codice con lunghezza pari a 5 caratteri e contenente solo caratteri
numerici -> codice accettato, ricerca del codice nell’archivio (passaggio allo stato
riconoscimento), codice non trovato in archivio, ritorno allo stato e alla schermata iniziale.
• Inserimento di un codice corretto -> codice accettato, ricerca del codice nell’archivio
(passaggio allo stato riconoscimento), codice trovato in archivio, richiesta del pin.
Successivamente siamo passati al controllo della fase di richiesta pin:
Inserisci il Pin: ciao
Errore!! Pin Errato!!
--- STATO IDLE --**********************************************************************************
** Sistema VIDEONOLO disponibile, effettuare l'accesso per le vostre operazioni **
**********************************************************************************
Inserire codice utente: 11111
--- STATO RICONOSCIMENTO --Inserisci il Pin: 11111
Accesso Effettuato: Utente
Di Prova
--- STATO SCELTA ------- MENU PRINCIPALE ----1
2
3
4
–
GESTIONE CREDITO
NOLEGGIO
RESTITUZIONE
USCITA
Scelta:
Gli unici due casi sensati sono l’immissione di un codice errato che fa tornare il sistema alla fase
iniziale e l’immissione di un codice corretto che causa l’effettivo accesso al sistema visualizzando
nome e cognome dell’utente e il menù principale dell’applicazione.
A questo punto iniziamo l’effettiva navigazione dell’intero menu specificando la scelta di un
qualsiasi sottomenù immettendo un indice sbagliato causa in tutti i casi la rivisualizzazione del
menù come nell’esempio seguente :
--- STATO SCELTA ------- MENU PRINCIPALE ----1
2
3
4
–
GESTIONE CREDITO
NOLEGGIO
RESTITUZIONE
USCITA
Scelta: 5
ERRORE DI INSERIMENTO!!
----- MENU PRINCIPALE ----1 - GESTIONE CREDITO
2 - NOLEGGIO
3 - RESTITUZIONE
4 – USCITA
Scelta: ciao
ERRORE DI INSERIMENTO!!
----- MENU PRINCIPALE ----1 - GESTIONE CREDITO
2 - NOLEGGIO
3 - RESTITUZIONE
4 – USCITA
Scelta:
Passiamo adesso alla fase di gestione credito con il relativo menù; verranno analizzati in seguito
tutti i casi più significativi:
Scelta: 1
--- STATO GESTIONE CREDITO --Credito Residuo = 41
.
Scegliere l'operazione da effettuare:
1 - Ricarica credito
2 - Torna al menù principale
Scelta: 1
RICARICA DEL CREDITO:
Importi consentiti 5, 10, 20 Euro
Inserire l'importo (0 per uscire): 30
Importo non consentito, la ricarica non verrà effettuata!
RICARICA DEL CREDITO:
Importi consentiti 5, 10, 20 Euro
Inserire l'importo (0 per uscire): 10
Il suo nuovo credito è 51
RICARICA DEL CREDITO:
Importi consentiti 5, 10, 20 Euro
Inserire l'importo (0 per uscire): 0
Credito Residuo = 51
.
Scegliere l'operazione da effettuare:
1 - Ricarica credito
2 - Torna al menù principale
Scelta:
Come si può vedere l’applicazione si comporta coerentemente sia in caso inserimento di un importo
non consentito che in caso di inserimento di un importo valido.
Prendiamo adesso in considerazione il caso limite riguardante il massimo credito consentito (999 ).
Quindi modifichiamo manualmente il file di testo ed effettuare una ricarica:
--- STATO GESTIONE CREDITO --Credito Residuo = 990
.
Scegliere l'operazione da effettuare:
1 - Ricarica credito
2 - Torna al menù principale
Scelta: 1
RICARICA DEL CREDITO:
Importi consentiti 5, 10, 20 Euro
Inserire l'importo (0 per uscire): 10
La ricarica non verrà effettuata, il sistema non gestisce un credito maggiore a 999
!
RICARICA DEL CREDITO:
Importi consentiti 5, 10, 20 Euro
Inserire l'importo (0 per uscire): 5
Il suo nuovo credito è 995
Al logout del cliente viene aggiornato il file che modifica se necessario il campo contenete il
credito:
11111 | 11111 | Utente
| Di Prova $ 995
Nella fase di noleggio verifichiamo solo le diverse visualizzazioni degli articoli senza ripetere tutte
le volte le fasi di visualizzazioni della scheda dell’articolo selezionato e l’accettazione o meno
dell’effettivo noleggio:
---------- NOLEGGIO ---------1 - Visualizza per categoria
2 - Visualizza per genere
3 - Visualizza novità
4 - Torna al menù principale
Scelta: 1
--- STATO VISUALIZZA PER CATEGORIA ------ Scegli la categoria ---1 - Film
2 - Concerti
3 - Documentari
4 - Ritorna al menù precendente
Scelta: 1
f0001 Blow
f0002 Codice Sworfishsh
f0003 Instinct
f0004 Vacanze di natale 2004
f0005 Kill bill v.1
f0006 Kill bill v.2
f0007 Trainspotting
f0008 Platoon
f0009 L'esorcista (versione integrale)
f0010 L'era glaciale
f0011 Pulp fiction
Scegli un articolo sulla base del codice(0 per tornare al menù precedente): 0
---- Scegli la categoria ---1 - Film
2 - Concerti
3 - Documentari
4 - Ritorna al menù precendente
Scelta: 1
f0001 Blow
f0003 Instinct
f0004 Vacanze di natale 2004
f0005 Kill bill v.1
f0006 Kill bill v.2
f0008 Platoon
f0009 L'esorcista (versione integrale)
f0011 Pulp fiction
Scegli un articolo sulla base del codice(0 per tornare al menù precedente):
Tra la prima e la seconda visualizzazione dei film è stato modificato il file di archivio mettendo a 0
la disponibilità di 3 articoli che come si può notare non vengono visualizzati al secondo tentativo.
Dato che le visualizzazioni per categoria si equivalgono, passiamo alla visualizzazione per genere:
--- STATO VISUALIZZA PER GENERE -------- Visualizzazione degli articoli per genere -----I genere disponibili sono:
Film:
1 - Commedia
2 - Azione
3 - Thriller
4 - Drammatico
5 - Horror
6 - Animazione
7 - Pulp
Concerti:
8 - Rock
9 - Pop
10 - Reggae
11 - Soul
12 - Metal
Documentari:
13 - Scienza
14 - Tecnologia
15 - Storia
16 - Natura
17 - Geologia
18 - Sport
Scegli un genere inserendo il numero(0 per tornare al menù precedente): 0
--- STATO NOLEGGIO ------------ NOLEGGIO ---------1 - Visualizza per categoria
2 - Visualizza per genere
3 - Visualizza novità
4 - Torna al menù principale
Scelta: 2
--- STATO VISUALIZZA PER GENERE -------- Visualizzazione degli articoli per genere -----I genere disponibili sono:
Film:
1 - Commedia
2 - Azione
3 - Thriller
4 - Drammatico
5 - Horror
6 - Animazione
7 - Pulp
Concerti:
8 - Rock
9 - Pop
10 - Reggae
11 - Soul
12 - Metal
13 - Lirica
Documentari:
14 - Scienza
15 - Tecnologia
16 - Storia
17 - Natura
18 - Geologia
19 - Sport
Scegli un genere inserendo il numero(0 per tornare al menù precedente):
Anche in questo caso c’è stata una modifica dell’archivio tra il primo e il secondo tentativo, in
particolare è stato aggiunto un nuovo concerto appartenente ad un genere che non era ancora
presente in archivio(lirica). Come si può vedere la lista viene aggiornata dinamicamente ed il
programma funziona correttamente:
Scegli un genere inserendo il numero(0 per tornare al menù precedente): 13
c0008 Pavarotti & friend's
Scegli un articolo sulla base del codice(0 per tornare al menù precedente):
Passiamo infine alla visualizzazione delle novità:
--- STATO VISUALIZZA NOVITA' --Verranno visualizzati tutti gli articoli presenti in archivio da non più di tre mesi
f0001 Blow
f0003 Instinct
f0006 Kill bill v.2
c0003 Ben Harper Live To Mars
c0005 The song remains the same
c0008 Pavarotti & friend's
d0008 Il mio anno più bello
Scegli un articolo sulla base del codice(0 per tornare al menù precedente): 0
--- STATO NOLEGGIO ------------ NOLEGGIO ---------1 - Visualizza per categoria
2 - Visualizza per genere
3 - Visualizza novità
4 - Torna al menù principale
Scelta: 3
--- STATO VISUALIZZA NOVITA' --Verranno visualizzati tutti gli articoli presenti in archivio da non più di tre mesi
f0003 Instinct
f0006 Kill bill v.2
c0003 Ben Harper Live To Mars
c0005 The song remains the same
c0008 Pavarotti & friend's
d0008 Il mio anno più bello
Scegli un articolo sulla base del codice(0 per tornare al menù precedente):
Come al solito abbiamo effettuato una modifica manuale all’archivio tra i due tentativi cambiando
la data di arrivo dell’articolo con codice f0001 che come previsto non compare nel secondo
tentativo.
Proviamo adesso a noleggiare un articolo:
Scegli un articolo sulla base del codice(0 per tornare al menù precedente): c0005
Titolo: The song remains the same
Anno di uscita: 2004
Genere: Rock
Cantante: Led Zeppelin
Luogo del concerto: Madison square garden
Vuoi noleggiare questo articolo? s/n
Scelta: s
--- STATO RITIRA --Articolo Noleggiato! Verranno detratti 2
Il suo nuovo credito è 993
dal suo credito
--- STATO SCELTA ------- MENU PRINCIPALE ----1 - GESTIONE CREDITO
2 - NOLEGGIO
3 - RESTITUZIONE
4 - USCITA
Scelta:
Questo è il caso di un noleggio completato senza problemi cui seguono l’aggiornamento istantaneo
della disponibilità dell’articolo e la scrittura(anch’essa istantanea) di una nuova riga nell’archivio
dei noleggi:
GG/MM/AAAA HH:MM:SS | 11111 | c0005 | 29/06/2005 11:43:30
Proviamo adesso alcuni casi particolari:
1) Il cliente non ha i 2 necessari per il noleggio
--- STATO GESTIONE CREDITO --Credito Residuo = 1 .
...
c0001
c0002
c0003
c0004
c0005
c0006
c0007
c0008
Vasco Live @ S. Siro '03
Bob Marley The Legend
Ben Harper Live To Mars
Like A Virgin
The song remains the same
Live at the Hollywood bowl
Live at Forum
Pavarotti & friend's
Scegli un articolo sulla base del codice(0 per tornare al menù precedente): c0007
Titolo: Live at Forum
Anno di uscita: 1999
Genere: Metal
Cantante: Metallica
Luogo del concerto: Milano
Vuoi noleggiare questo articolo? s/n
Scelta: s
--- STATO RITIRA --Il suo credito non è sufficiente per il noleggio!
2) Si vuole noleggiare un articolo non disponibile anche se questo non era stato visualizzato
f0001
f0003
f0004
f0005
f0006
f0008
f0009
f0011
Blow
Instinct
Vacanze di natale 2004
Kill bill v.1
Kill bill v.2
Platoon
L'esorcista (versione integrale)
Pulp fiction
Scegli un articolo sulla base del codice(0 per tornare al menù precedente): f0007
Codice errato! Ripetere l'operazione!
Testiamo adesso la fase di riconsegna distinguendo 3 casi:
1) Riconsegna avvenuta senza errori:
Prima della riconsegna sul file noleggi appariva la seguente riga
GG/MM/AAAA HH:MM:SS | 11111 | c0007 | 17/06/2005 16:47:35
--- STATO RESTITUZIONE --Inserisci il codice dell'articolo che vuoi restituire(0 per tornare al menù principale): c0007
Ricerca dell'articolo in corso..............TROVATO!
L'articolo è stato noleggiato per 283 ore
(2
erano stati detratti al momento del noleggio!)
In totale hai speso 25
Il suo nuovo credito è 18
Dopo la restituzione la riga viene così modificata
29/06/2005 12:04:15 | 11111 | c0007 | 17/06/2005 16:47:35
2) Riconsegna di un articolo già restituito o mai noleggiato:
--- STATO RESTITUZIONE --Inserisci il codice dell'articolo che vuoi restituire(0 per tornare al menù principale): c0007
Ricerca dell'articolo in corso..................NON TROVATO!
3) Riconsegna di un articolo senza aver il credito necessario:
--- STATO RESTITUZIONE --Inserisci il codice dell'articolo che vuoi restituire(0 per tornare al menù principale): f0004
Ricerca dell'articolo in corso.................TROVATO!
L'articolo è stato noleggiato per 282 ore
In totale hai speso 25
(2
erano stati detratti al momento del noleggio!)
L'operazione non può essere completata!!
Ricaricare il credito
--- STATO GESTIONE CREDITO --Credito Residuo = 18
.
Scegliere l'operazione da effettuare:
1 - Ricarica credito
2 - Torna al menù principale
Scelta:
Istruzioni per la modifica manuale degli archivi
Poiché in archivio sono presenti solo un numero di articoli e clienti necessario a svolgere test
significativi può essere interessante modificare manualmente l’archivio al fine di aggiungerne degli
altri. In questo caso vanno seguite alcune semplici regole fondamentali per il corretto
funzionamento del programma. Queste regole sono dettate dal formato standard che abbiamo dato
ai file di testo e che ci ha permesso di semplificare notevolmente la logica di controllo e l’accesso ai
file(operazioni che inizialmente ci hanno creato non pochi problemi):
I file Film.txt, Documentari.txt, Concerti.txt, Clienti.txt devono essere editati separando i campi con
il carattere ‘|’ preceduto e seguito da uno spazio. Il solo campo relativo al credito del cliente ed alla
disponibilità dei vari articoli va preceduto dal carattere ‘$’ preceduto e seguito da uno spazio. Al
campo disponibilità vanno assegnati comunque due spazi con allineamento a sinistra. Al campo
credito dell’utente vanno assegnati comunque tre spazi con allineamento a sinistra. Tutte le righe
dei file sopraccitati (anche l’ultima) devono terminare con il carattere ‘\n’ (andata a capo).
Quando si aggiunge un articolo bisogna controllare se sono presenti in archivio altri articoli dello
stesso genere ed in caso negativo va aggiunto il nuovo genere nell’apposito file(Generi_film.txt,
Generi_concerti.txt o Generi_documentari.txt). In questi file l’ultima riga non va terminata con il
carattere ‘\n’.
L’intero programma, completo di archivio, è dentro la cartella progetto. Per funzionare tale cartella
deve risiedere in c:\ .