UNIVERSITA’ POLITECNICA DELLE MARCHE Facoltà di Ingegneria Corso di Laurea in Ingegneria Informatica e dell’Automazione Tesi di Laurea Progettazione e sviluppo di un modulo per il task-scheduling per il CMS Drupal 6. Laureando: Danilo del Conte Relatore: Prof. Aldo Franco Dragoni ANNO ACCADEMICO 2010-2011 1 Indice Introduzione ………………………………………………… 3 CMS ………………………………………………………………………………………4 Formato RSS e Feed Web ……………………………………………………………….10 Drupal ……………………………………………………………………………………12 Algoritmo ………………………………………………………………………………..16 Processo ………………………………………………………………………………….18 Scheduler ………………………………………………………………………………...23 Scheduling della CPU ……………………………………………………………………24 Sistema operativo Real-Time ……………………………………………………………27 Specifiche progetto …………………………………………32 Realizzazione progetto ……………………………………..35 Drupal operazioni iniziali ………………………………………………………………..36 Organizzazione del lavoro ……………………………………………………………….53 Implementazione algoritmi nel linguaggio C ……………………………………………54 Creazione della directory del modulo “algoritmi” ………………………………………69 Creazione del file algoritmi.info ………………………………………………………...70 Creazione del file algoritmi.module ……………………………………………………..75 Creazione del file crea_gantt.php ………………………………………………………100 Installazione e funzionamento del modulo algoritmi …………………………………..115 Conclusioni ………………………………………………..121 Bibliografia ………………………………………………..124 2 INTRODUZIONE 3 CMS Content Management System, in acronimo CMS, letteralmente sta per “sistema di gestione dei contenuti”. È uno strumento software installato su un web server studiato per facilitare la gestione (creazione, modifica, rimozione, ecc.) dei contenuti di un sito web (portale informativo) attraverso un’interfaccia semplice ed intuitiva, che svincola l'amministratore del sito da conoscenze tecniche di programmazione web e gli permette di ottenere il controllo sulla creazione e sulla distribuzione delle informazioni contenute in esso. In poche parole, garantisce all’amministratore la totale autonomia nella gestione dei contenuti del sito senza la necessità che esso conosca i linguaggi di programmazione web (HTML, PHP, JavaScript, ecc.) e senza il bisogno di rivolgersi ad un web master (colui che ha creato il sito o semplicemente chi conosce i linguaggi di programmazione web). Il reale valore aggiunto di un sito internet, rispetto al cartaceo, dovrebbe essere il costante aggiornamento dei contenuti e la visibilità di questi a livello mondiale. Solo un sito costantemente aggiornato può essere seguito dalle altre persone con interesse. Ogni sito web, ai suoi albori, presenta poco contenuto. Con lo scorrere del tempo, l’organizzazione responsabile del sito sente il bisogno di pubblicare nuove informazioni sul web, ed il sito deve essere aggiornato. Probabilmente il sito è content-driven, cioè fortemente basato sul contenuto; magari sul portale vengono pubblicati articoli e news con una frequenza molto alta, per cui occorre prestare molta attenzione a come possa essere aggiornato e modificato il sito stesso. Con il CMS si vuole creare il sito in modo da poter essere facilmente modificato da persone non esperte di programmazione o web development (sviluppatori di siti internet), permettendo la gestione efficace di una gran mole di informazioni di diverse tipologie. Per far fronte a queste esigenze è indispensabile appoggiare la nostra base di informazioni su un CMS, una piattaforma per la pubblicazione delle informazioni e la gestione dei contenuti di un portale. Grazie al CMS la fase di realizzazione dell'architettura del sito e della information architecture è distinta dalla fase di inserimento dei dati veri e propri. Mentre la prima fase richiede il lavoro di un esperto informatico, nella seconda fase i contenuti effettivi del portale possono essere gestiti da personale interno all'organizzazione, precedentemente istruito per l'operazione, attraverso pochi semplici passi. Facciamo qualche esempio L'attività commerciale che decide di proporre un' offerta promozionale ha la necessità di pubblicizzare l'evento sul proprio sito, quindi ha bisogno di tempestivi aggiornamenti. Oppure, l’associazione X vuole aggiornare l'elenco degli iscritti o inserire delle news nel proprio sito. O ancora, un’agenzia di viaggi vuole pubblicizzare le offerte last minute. Normalmente, per queste operazioni, come per ogni seppur minimo aggiornamento, si segue la stessa noiosa procedura. L'interessato (autore del sito, o amministratore) fornisce i contenuti da pubblicare a chi ha realizzato le pagine (web master) e dopo aver spiegato nel migliore dei modi quello che gli serve, aspetta diligentemente il suo turno. Ovviamente non sarà il solo utente che ha la necessità di aggiornare il proprio sito (perché anche altre persone potrebbero rivolgersi allo stesso webmaster), quindi l'operazione sarà lenta (il web master deve soddisfare le richieste di più 4 persone), dispendiosa (gli aggiornamenti si pagano) e soprattutto genera un rapporto di dipendenza nei confronti del web master o della web agency che ha realizzato il sito. Come altro ulteriore esempio descriviamo uno scenario molto comune tra le piccole aziende: viene commissionato il sito ad una Web Agency o ad un professionista del settore, il sito viene realizzato in HTML e CSS, e l’azienda è molto contenta del risultato. Dopo alcuni mesi la ditta sente la necessità di dover aggiornare delle informazioni, ma nessuno all’interno della ditta conosce HTML. Il contenuto del sito deve essere modificato da una persona esterna, che funge da responsabile del sito. Ora tutto il contenuto che deve essere pubblicato sul sito passa per le sue mani. Potrebbe accadere che questa persona si renda irreperibile per qualsiasi motivo ed ogni aggiornamento sarebbe impossibilitato. Oppure viene affidata la gestione del sito ad un dipendente interno all’azienda, con poche conoscenze informatiche, dopo un breve corso di HTML. Questa persona si occupa dell’inserimento dei dati, ed il procedimento funziona, ma dopo qualche tempo ci si accorge che il sito ha bisogno di un serio restyling, dato che le informazioni venivano aggiunte ogni volta in modo approssimativo, senza un vero metodo, una organizzazione dei dati. In ambedue i casi, in presenza di frequenti aggiunte di contenuto al sito, potrebbe accadere che la persona incaricata di aggiornare il sito si trovi in una situazione nota come “bottleneck” (collo di bottiglia) per il flusso delle informazioni dall’azienda al suo pubblico, con dei conseguenti ritardi negli aggiornamenti dei siti (deve soddisfare le richieste di più aziende). In sintesi: effettuare la manutenzione e l'aggiornamento di un sito può rivelarsi un’operazione molto complessa ed onerosa, ma è impossibile pensare di non farlo. Il CMS è un’ottima soluzione perché è economicamente competitivo e consente di essere autonomi e tempestivi negli aggiornamenti di un sito. Estendere la Gestione del Contenuto Il tempo in cui un’azienda creava un sito web solo per avere una “presenza” su Internet è finito; oggigiorno un sito Internet serve ad informare i clienti, risolvere i loro problemi e cercare di attirare nuove opportunità. La gestione del contenuto del sito deve essere resa disponibile anche a delle persone che non hanno un background tecnico, non sanno né come è composta una pagina HTML né come è strutturato un sito Internet. E non vogliono saperlo. Vogliono solo aggiungere contenuto al sito Web. Bisogna ricorrere ad un software che possa creare un framework (struttura di supporto su cui un software può essere organizzato e progettato) intorno al sito, in modo che, definita una struttura portante ed un design attraente, le informazioni inserite nel sito vengano strutturate in modo automatico, coerente ed intuitivo, senza che la crescita della mole di informazioni porti il sito fuori controllo. Questo software è appunto il CMS. Caratteristiche comuni di ogni CMS Un CMS: • Permette di aggiornare il sito senza essere degli esperti di html, php, ecc. • Permette di realizzare in modo facilitato l’architettura dei dati progettata, attraverso la definizione di diverse sezioni e categorie in cui classificare gli articoli. • Permette di separare nettamente i dati dalla loro presentazione. • Permette di gestire in modo semplice la pubblicazione delle informazioni e decidere quando esse devono essere tolte dal sito. 5 • • • • Permette la gestione di siti e portali informativi di ogni dimensione. Integrazione con contenuti provenienti da diverse fonti come database o RSS. Gestione degli utenti, con mailing list e messaggistica. Funzionalità di ricerca dei contenuti che vanno oltre la disposizione in categorie. Tecnicamente un CMS è un’applicazione lato server che, appoggiandosi su un database preesistente per lo stoccaggio (sistemazione e conservazione) dei contenuti, permette una generazione dinamica di questi, prelevandoli proprio dal database. L'applicazione è composta da una serie di script che permettono a chiunque, anche digiuno di linguaggi di programmazione, di aggiornare il sito “retto” dal CMS, inserendo i contenuti direttamente online, fondamentalmente tramite una serie di form (più o meno "avanzate"). Gli script, lavorando sul server, memorizzano le informazioni immesse dall'utente (amministratore o redattore) traducendole in dati “digeribili” dal server (record del database, altri script) e le restituiscono all'utente finale in modo completamente trasparente (all'utente finale arriva comunque una pagina HTML “standard”, come avrebbe potuto scriverla in codice il web master). Proprio per questo, in un CMS si distingue di solito la parte front-end (che viene presentata ai navigatori) da quella back-end, che contiene la parte gestionale (amministrazione, opzioni, editing): • la sezione applicativa (front end), che l'utente web usa per beneficiare dei contenuti e delle applicazioni del sito; • la sezione di amministrazione (back end), che serve all’amministratore del sito per organizzare e supervisionare la produzione dei contenuti. La parte back-end: • non richiede (almeno per tutte le operazioni quotidiane più comuni) conoscenze maggiori di quelle necessarie a navigare su internet e di saper scrivere con un word-processor; • non richiede software proprietari, reti private virtuali, plugins esotici, ma un semplice Browser Internet. In pratica l'amministratore del CMS effettua tutte le operazioni di gestione connettendosi al sito dal proprio terminale, immettendo username e password (per ricevere gli eventuali permessi di gestione) e sfogliando delle pagine composte da form (spuntando, scrivendo, scegliendo opzioni da menù a discesa), che sarebbero i pannelli di interfaccia grafica che permettono il controllo e la gestione del sito (inserire, modificare, eliminare i vari contenuti). I CMS possono essere realizzati tramite programmazione in vari linguaggi web tra cui più comunemente in ASP, PHP, .NET; il tipo di linguaggio adoperato è indifferente a livello di funzionalità. Alcuni linguaggi rendono il CMS multipiattaforma, mentre altri lo rendono utilizzabile solo su piattaforme proprietarie. Tipi di CMS Esistono CMS specializzati, cioè appositamente progettati per un tipo preciso di contenuti (un'enciclopedia on-line, un blog, un forum, una rivista ecc.) e CMS generici, che tendono a essere più flessibili per consentire la pubblicazione di diversi tipi di contenuti. Anche se la tendenza attuale è quella della convergenza (i CMS specializzati fanno sempre più cose e quelli generalisti implementano sempre più servizi specializzati), è possibile ancora fare qualche distinzione. Senza voler fare una tassonomia esaustiva, fra i sistemi specializzati vi sono: • LMS (Learning Management Systems): sistemi per la formazione online, orientati alla diffusione di materiale didattico e alla sua erogazione; hanno classi, corsi, calendari, test di apprendimento, fino a esami online. 6 • • • piattaforme per i Blog: sistemi dedicati alla creazione di blog e forum; permettono la pubblicazione di un "diario online", con coordinate ormai consolidate: commenti dei lettori, pubblicazione di un flusso "puro" xml (feed) per l'utilizzo degli articoli fuori dal contesto del sito, anche con strumenti diversi dai browser internet, strumenti per la creazione di una rete esterna al blog. sistemi di e-commerce: sistemi dedicati al commercio online di beni e servizi; includono il carrello, l'interfaccia con i più diffusi sistemi di pagamento online, la gestione di articoli, categorie, fornitori, diversi strumenti di marketing, calcolo dei costi. piattaforme per i WIKI: i wiki sono siti la cui peculiarità è la contribuzione (più o meno) orizzontale di tutti gli utenti alla creazione e modifica dei contenuti. L'esempio più conosciuto è senz'altro wikipedia. I web content management system (WCMS) Nonostante i CMS non siano stati concepiti per il Web, oggi il loro utilizzo più diffuso è rivolto alla gestione di siti web, soprattutto se sono di grandi dimensioni e richiedono un frequente aggiornamento. I CMS utilizzati per il Web sono chiamati con l’acronimo di WCMS. Una delle applicazioni più utili dei sistemi di WCMS, infatti, è nella gestione dei portali (intranet, extranet, community, siti di e-commerce...), dove vengono impiegati come strumento di pubblicazione, flessibile e multiutente. Ad esempio, gestione di contenuti testuali (notizie, articoli ecc.), link, immagini, liste di discussione, blog, forum, materiale scaricabile. Può essere modificata anche la struttura stessa delle pagine in numero ed organizzazione. I WCMS consentono di definire utenti, gruppi e diritti in modo da poter permettere una distribuzione del lavoro tra più persone e da poter assegnare ad un preciso “tipo” di utente, identificato da username e password, determinati privilegi. Per esempio, è possibile definire una classe di utenti abilitati esclusivamente all'inserimento delle notizie, mentre si può riservare la scrittura di articoli ad un altro gruppo, e limitare tutti gli altri alla sola consultazione; tutto questo grazie ad un log in tramite username e password che identifica la classe di appartenenza dell’utente e in base a quest’ultima ne conferisce i vari permessi. La scelta di un software di WCMS è strategica per le aziende che generano la maggior parte di volume d'affari su Internet, ma (in proporzione diversa) è molto importante anche per il libero professionista che vuole utilizzare il medium Internet per farsi conoscere. Vantaggi dei CMS Un CMS permette di costruire e aggiornare un sito dinamico, anche molto grande, senza necessità di scrivere una riga di HTML e senza conoscere linguaggi di programmazione lato server (es. PHP) o progettare un apposito database. L'aspetto esteriore delle pagine può essere personalizzato scegliendo un foglio di stile CSS (il CSS si occupa della presentazione grafica del sito: colori, caratteri, posizione testo e immagini, ecc.) appositamente progettato per un determinato CMS. 7 Limiti dei CMS • • Un CMS è tanto più efficiente quanto più è specializzato. Molti piccoli portali fanno ricorso a CMS (scritti da altri e messi a disposizione gratuitamente o a pagamento) di tipo generico; per quanto un CMS possa essere flessibile, un sito basato su questa struttura in genere presenta un aspetto poco personalizzato se non è possibile intervenire direttamente sul codice sorgente del prodotto per modificarlo. Analogamente i contenuti saranno sempre ancorati a quanto previsto da chi ha progettato il CMS e non alle esigenze di chi pubblica il sito. Quindi i CMS generici hanno sì come vantaggio il fatto di essere molto flessibili e possono essere utilizzati per gestire diversi tipi di contenuti, ma hanno come limite una modesta efficienza, nel senso che non sono improntati su nessun tipo di contenuto specifico, cioè offrono una gestione generica e non specifica a qualsiasi tipo di contenuto (tutti i contenuti vengono gestiti allo stesso modo, non c’è distinzione ad esempio tra contenuti testuali e contenuti immagini). Problemi di gestione: derivano dal fatto che chi pubblica o gestisce il sito può usare il CMS per intervenire sui contenuti e sull'aspetto, ma generalmente (caso di chi acquista il software) non è in grado di intervenire direttamente (o far intervenire) sulla struttura del CMS stesso; questo è un limite strettamente connesso al vantaggio primario dei CMS: pubblicare un portale senza doverne progettare la struttura o senza possedere le conoscenze tecniche (o le risorse finanziarie) per uno sviluppo personalizzato. Questi problemi sono risolvibili utilizzando software open source: la possibilità di accedere al codice sorgente del prodotto permette di personalizzare il software sulla base delle proprie esigenze a patto di non avere necessità di apportare modifiche al prodotto adottato. Anche in questo caso, vanno messi in conto i costi per lo sviluppo di moduli personalizzati o funzioni particolari a meno di non possedere in proprio o nella propria struttura aziendale le conoscenze tecniche per intervenire nel codice sorgente (essere un webmaster o avere un webmaster in azienda). I portali di una certa importanza generalmente non fanno mai ricorso a CMS distribuiti, bensì usano programmi e database progettati su misura; ovvero "CMS personalizzati" e dunque necessariamente specializzati. In questo modo la struttura e la presentazione vengono realizzate tenendo presente i contenuti che il sito dovrà ospitare e potranno essere modificati in seguito a nuove esigenze. Non mancano però i casi in cui grandi aziende o società si siano affidate a CMS open source liberi. Perché usare un CMS Con un CMS l'editore del sito (colui che lo amministra e ne gestisce i contenuti) è potenzialmente in grado di affrancarsi, dopo il primo setup, da agenzie e programmatori. Ne guadagna l'editore e ne guadagnano i navigatori: al posto del vecchio sito-depliant, fatto e lasciato lì a ingiallire, i visitatori possono avere notizie fresche, modi di interazione (più o meno spinti) con l'editore o altri utenti, servizi utili come la ricerca o l'e-commerce; insomma quello che, oggi, il navigatore medio si aspetta da qualsiasi sito. 8 Funzioni “standard” di un CMS È molto difficile trovare un sottoinsieme comune (fermo restando che qui si parla dei CMS generalisti), ma si evidenziano di seguito le funzioni standard principali che accomunano i vari CMS: • gestione dei contenuti: creazione di sezioni, sottosezioni e pagine; editing della maggior parte dei contenuti con tools WYSIWYG (scrittura trasparente di testo formattato -html- con immagini, link, ecc.); • tools: news, faq, calendario; • motore di ricerca interno; • gestione degli utenti: possibilità di autoiscrizione, permessi su pagine/contenuti, gestione di gruppi; • gestione del flusso di pubblicazione: gerarchia di permessi per scrittura, approvazione e messa online agli utenti finali; • community: forum, sondaggi, possibilità per gli utenti di gestire i propri dati (sino a pubblicare, su CMS particolarmente orientati al Web 2.0, contenuti propri orizzontalmente rispetto all'editore); • sistemi di pubblicazione alternativa: feed RSS, versioni accessibili, eventualmente invio per email agli utenti registrati (in realtà queste caratteristiche sono implicite nella struttura fondamentale di ogni CMS, che separa la nuda interfaccia, detta template, dai contenuti veri e propri). Inoltre quasi tutti i CMS, in ogni caso, hanno dei sistemi di plugin (moduli nel caso di Drupal) con cui è possibile estendere le funzionalità base in ogni direzione. Ovviamente, quanto più un CMS è diffuso (o quanto è più ampia la comunità di sviluppatori interessati) tanto è più facile trovare senza fatica la “pappa pronta” di un plug-in che fa al caso nostro. A titolo di esempio verrà mostrato di seguito un backend di una piattaforma CMS: 9 Formato RSS e feed web RSS (acronimo della sua ultima versione 2.0 Really Simple Syndication) è uno dei più popolari formati per la distribuzione di contenuti Web; è basato su linguaggio XML, da cui ha ereditato la semplicità, l'estensibilità e la flessibilità. L'applicazione principale per cui è noto sono i feed RSS. RSS definisce una struttura adatta a contenere un insieme di notizie, ciascuna delle quali sarà composta da vari campi (nome autore, titolo, testo, riassunto, ...). Quando si pubblicano delle notizie in formato RSS, la struttura viene aggiornata con i nuovi dati; visto che il formato è predefinito, un qualunque lettore RSS potrà presentare in una maniera omogenea notizie provenienti dalle fonti più diverse. Il feed web è un’unità di informazioni formattata secondo specifiche (di genesi XML) stabilite precedentemente. Ciò per rendere interoperabile ed interscambiabile il contenuto fra le diverse applicazioni o piattaforme. Un feed è usato per fornire agli utilizzatori una serie di contenuti aggiornati di frequente. I distributori del contenuto rendono disponibile il feed e consentono agli utenti di iscriversi. L’aggregazione consiste in un insieme di feeds accessibili simultaneamente, ed è eseguita da un aggregatore Internet. L’uso principale dei feed RSS (detti anche flussi RSS) attualmente è legato alla possibilità di creare informazioni di qualunque tipo che un utente potrà vedere molto comodamente, con l'aiuto di un lettore apposito, nella stessa pagina, nella stessa finestra, senza dover andare ogni volta nel sito principale. Quindi permettono agli utenti di essere aggiornati su nuovi articoli o commenti pubblicati nei siti di interesse senza doverli visitare manualmente uno a uno. Questo è dovuto al fatto che il formato XML è un formato dinamico. Il web feed presenta alcuni vantaggi, se paragonato al ricevere contenuti postati frequentemente tramite e-mail: • Nell'iscrizione ad un feed, gli utenti non rivelano il loro indirizzo di posta elettronica; in questo modo non si espongono alle minacce tipiche dell'email: lo spam, i virus, il phishing, ed il furto di identità. • Se gli utenti vogliono interrompere la ricezione di notizie, non devono inviare richieste del tipo "annulla la sottoscrizione"; basta che rimuovano il feed dal loro aggregatore. Gli RSS sono particolari formati dei dati che permettono di pubblicare in modo standard risorse che sono (o possono essere) aggiornate in modo frequente, quali notizie, blog, audio e video. Un documento RSS (chiamato in gergo feed ) consiste di un elenco delle risorse cui il documento si riferisce, insieme ad un loro sommario e ad altri dati importanti quali la loro data di pubblicazione, quella di eventuale scadenza, l’autore ecc. Un feed, dunque, raccoglie documenti simili e li rende disponibili in modo sintetico, automatico e in formato standard. Questo formato può essere letto da molti software diversi (chiamati "lettori RSS", "feed reader" o anche "aggregatori"). Le modalità di utilizzo di un documento RSS più diffuse sono due: - come applicazione a se stante: attraverso appositi software che interpretano un feed (detto anche flusso RSS) permettendo agli utenti di visualizzarne i contenuti; - attraverso un’altra applicazione per l’accesso a Internet (come un browser, eventualmente mediante un plug-in, o come un’applicazione per l’accesso e la riproduzione di media digitali, quale ad esempio Apple iTunes): integrando i contenuti del feed all'interno di un sito Web. 10 Per utilizzare i contenuti in formato RSS è necessario utilizzare un programma ad hoc, chiamato aggregatore RSS.. Copiando con il tasto destro del mouse il collegamento (il link, l'indirizzo URL)) della pagina RSS in questione question e quindi incollandolo nel programma aggregatore si riesce finalmente a vederne i contenuti in modo chiaro e coerente: una lista di notizie. notizie Cliccando su ciascuna notizia si ha generalmente la possibilità di averne un breve riassunto e un link alla pagina web che la contiene per intero. Dunque il feed RSS offre la possibilità di avere una specie di indice aggiornato dei contenuti, contenuti delle novità di un sito. Piuttosto che andare ogni volta su quel sito per vedere se ci sono novità, basterà aprire l'aggregatore RSS e sarà lui stesso a collegarsi al al sito ed eventualmente mettere in evidenza che c'è un articolo, una notizia, in generale un contenuto nuovo nuovo su quel sito che si può andare a consultare. In pratica l’utente può avere sul proprio aggregatore ore notifica delle novità e degli aggiornamenti avvenuti sui vari siti di suo interesse (attraverso i loro rispettivi feed RSS) e andare effettivamente effettivamen sul sito solo se la novità ovità è di suo interesse e visionarla per intero. Abituandosi Abituandos all'uso dei feed RSS, si potrebbe anche non aprire mai il browser web o farlo solo su particolari e selezionate novità. I feed RSS sono facilmente riconoscibili in un sito da icone quali: e . Quando si vuole essere sempre aggiornati dei contenuti di un sito, sito basta cliccare con il tasto destro del mouse su tali icone ne (se il sito le offre), copiare copiar il relativo collegamento (si dovrebbe ottenere l'URL di un pagina che finisce con .xml) e infine incollarlo nell’aggregatore nell’ preferito. Alcuni dei principali aggregatori RSS RS gratuiti sono: Feedreader e Sharpreader (Windows), ( Sage plug-in (FireFox/ThunderBird), Urss plug-in (Mozilla), Straw Linux (Gnome), (Gnome) Netnewswire Lite (Mac OS X). Un esempio di feed RSS è stato preso della home page del sottostante sito: http://www.espertoweb.it/espertoweb_rss2.xml http://www.espertoweb.it/espertoweb_rss2.xml. 11 Drupal Drupal è uno strumento software che permette di realizzare una grande varietà di siti WEB. Il nome è lo spelling inglese per la parola tedesca druppel che significa goccia. goccia Più precisamente è una piattaforma di CMS (Content Management System) scritto in PHP che poggia su un database per memorizzare i contenuti; necessita dunque di un software DBMS (Data Base Menagement System) ed il più utilizzato è MySQL.. Questo consente all'amministratore del sito ito ed eventualmente ad altri utenti di inserire ed aggiornare i contenuti senza dover conoscere particolari linguaggi di programmazione (come HTML, PHP, ecc) o far uso di programmi come Dreamweaver, Frontpage, Nvu. Drupal è multipiattaforma piattaforma, cioè funziona su diversi sistemi operativi, operativi tra cui Windows, Mac OS X, Linux e qualsiasi piattaforma che supporti i web server Apache o IIS. IIS Drupal, come molti CMS contemporanei, offre offr un'interfaccia di amministrazione con cui l'amministratore può gestire ogni aspetto del sito web, senza il bisogno di possedere conoscenze conoscen tecniche particolari di programmazione Web. Web È possibile cambiare la grafica del sito semplicemente scegliendo un tema grafico tra quelli disponibili o personalizzando il foglio di stile CSS associato. Drupal è un framework con sviluppo modulare, modulare, che consente di partire dal sito base e aggiungere solo le funzionalità necessarie al progetto sistema modulare; modulare cioè è espandibile, scalabile, alabile, sicuro, robusto, flessibile e permette anche la possibilità di manutenzione. manutenzione Le varie caratteristiche e funzionalità del sito possono essere estese installando i gli opportuni moduli (nome dato ai plugin di Drupal) aggiuntivi realizzati e distribuiti dalle comunity (drupal.org, ( druplaitalia.org, drupal.it) o da utenti generici. generici Risulta così semplicee realizzare un sito multilingue con gallerie fotografiche, calendario degli eventi, forum, blog, e-commerce e ommerce e molto altro. È possibile anche creare personalmente dei propri moduli; consente quindi di aggiungere numerose funzionalità al sistema base. Drupal è un software tware open source con licenza GPL (licenza di software libero, essa concede ai licenziatari il permesso di modificare il programma, di copiarlo e di ridistribuirlo con o senza modifiche, gratuitamente o a pagamento), pagamento), gestito e sviluppato da una comunità molto vasta. Esso quindi può essere scaricato,, installato e rilasciato liberamente e gratuitamente, utilizzato sia a scopo personale che commerciale e modificato sulla base delle diverse esigenze. Il sito ufficiale è www.drupal.org mentre i riferimenti italiani sono www.drupalitalia.org e www.drupal.it. www.drupal.it Piattaforma dinamica per la realizzazione di siti web, Drupal consente ente a un individuo o a una comunità unità di utenti di pubblicare, gestire e organizzare una varietà vari tà di contenuti, integrando in un unico pacchetto pronto all'uso molte apprezzate funzionalità di gestione contenuti, weblog, 12 strumenti di collaborazione e software per comunità di discussione. Consente quindi la realizzazione di siti Web basati sulla logica contenuti/commenti. Attivando e configurando i singoli moduli, un amministratore può disegnare un sito unico, che può essere utilizzato per una combinazione di gestione della conoscenza, per funzioni di pubblicazione sul web e interazione di una community. Le principali funzionalità di Drupal, organizzate per caratteristiche comuni alle piattaforme web, sono: Gestione dei Contenuti: attraverso un'interfaccia semplice e utilizzabile via browser, i membri possono pubblicare contenuti nei diversi moduli disponibili: storie, blog, sondaggi, immagini, forum, scaricamenti, ecc. Gli amministratori possono scegliere tra diversi template di temi (modelli di temi già preesistenti e contenuti nel database) o crearne uno essi stessi per dare al sito un look e uno stile unico. Il sistema di classificazione flessibile consente una classificazione gerarchica, indici incrociati di messaggi e diversi insiemi di categorie per la maggior parte dei tipi di contenuti. L'accesso al contenuto da parte dei vari utenti è controllato attraverso ruoli e rispettivi permessi per ciascun utente definiti dall'amministratore. Le pagine del sito possono mostrare messaggi per tipo di modulo o per categorizzazione del contenuto, con fonti RSS separate, disponibili per ogni tipo pubblicato. Gli utenti possono anche cercare per parola chiave all'interno dell'intero sito. Weblog: una singola installazione può essere configurata come sito weblog personale individuale o per diversi individui. Drupal supporta le API Blogger, fornisce fonti RSS per ogni blog individuale e può essere impostato per lanciare un ping (programma che misura il tempo, impiegato da uno o più pacchetti ICMP a raggiungere un altro computer o server in rete ed a ritornare indietro all’origine; è utilizzato soprattutto a verificare la presenza e la raggiungibilità di un altro computer connesso in rete e per misurare le latenze -intervallo di tempo che intercorre tra uno stimolo e la reazione da esso provocata- di trasmissione di rete) alle directory di weblog come blo.gs e weblog.com quando un nuovo contenuto è inserito nella home page. Comunità basate sulla discussione: un sito Drupal può comportarsi come un sito di notizie e/o fare uso di un forum di discussione tradizionale. Degli spazi per i commenti, collegati alla maggior parte dei tipi di contenuto, rendono semplice per i membri discutere nuovi messaggi. Gli amministratori possono determinare se il contenuto e i commenti possono essere inseriti senza approvazione, con l'approvazione di un amministratore o per moderazione della comunità. Grazie all'aggregatore di notizie interno, le comunità possono registrarsi su altri siti e discutere contenuti provenienti da essi. Collaborazione: utilizzato per gestire la realizzazione di Drupal, il modulo progetto si presta per supportare altri progetti di software open source. Il modulo tipo wiki (libro in collaborazione) include il controllo di versione, rendendo semplice per un gruppo creare, revisionare e aggiornare documentazione o altri tipi di testo. Drupal, opera dello sviluppatore Dries Buytaert, è stato concepito con l'intenzione di mettere a disposizione degli utilizzatori un potente e flessibile CMS con cui gestire e catalogare svariate tipologie di contenuti: testi, immagini, documenti, archivi e molto altro. I contenuti potranno essere inseriti e manipolati da più utenti dotati di diversi privilegi, questo CMS integra infatti un avanzato sistema di iscrizione e di autenticazione che permette ad un 13 amministratore, vero e proprio "utente di root" della piattaforma, di concedere ai diversi utilizzatori differenti permessi che vanno dalla semplice lettura alla possibilità di editare i contenuti. Quindi Drupal consente agli utenti di registrarsi e autenticarsi e permette agli amministratori di creare livelli d’accesso differenziati secondo i ruoli (utente non iscritto, utente iscritto, amministratore del sito, amministratore root, ecc). Drupal è anche uno strumento per la condivisione di contenuti e l'interazione tra autori e utenti e tra utenti fra loro; l'amministratore ha infatti la possibilità di concedere permessi di moderazione/amministrazione a singoli utenti o gruppi di utenti, abilitare moduli ed installare estensioni grazie a cui sarà possibile postare commenti, partecipare alle discussioni di un forum, mettere a disposizione e condividere materiali in formato digitale, ecc. I contenuti possono essere organizzati in base alla tipologia (Story, Page, Image…): questo consente di dividere i contenuti in modo flessibile, rendendone semplice l’inserimento e la visualizzazione, e consentendo di realizzare uno schema di navigazione del sito estremamente funzionale. Drupal è un'applicazione completamente Web based, potrà quindi essere utilizzata attraverso un semplice browser. Drupal, come detto precedentemente, è un CMS realizzato in PHP e, pertanto, per funzionare necessita unicamente di: • un'interprete per PHP; • un Web server (è raccomandato l'utilizzo di Apache ma il CMS funziona egregiamente anche con IIS di Microsoft, Drupal potrà quindi essere installato sia in ambiente Linux che Windows); • un DBMS (Database Manager System) MySQL , per la memorizzazione dei dati (è possibile utilizzare anche PostgreSQL mentre MSSQL e Oracle per il momento non sono supportati). Con Drupal possono essere realizzati siti Web pubblici, locali, dinamici e statici. Alcuni esempi di siti realizzabili con Drupal sono blog, forum, gallerie d’immagini, e-commerce, multisiti e multilingua. La struttura di Drupal Drupal ha ricevuto elogi da web master, progettisti e programmatori, in parte grazie al suo design modulare che permette al suo livello base, o «core» di fornire solo le funzioni essenziali nella sua configurazione predefinita. Funzioni e capacità di visualizzazione aggiuntive possono estendere il «core» tramite l'installazione di moduli e temi. Moduli Il «core» di Drupal contiene i propri moduli, cioè le funzionalità base del sito. I moduli sviluppati dalla comunità possono comunque essere scaricati e installati in un'area dell'installazione di Drupal non destinata al «core». Il «core» di Drupal è stato progettato con un sistema di «hook» (ganci) o callback (chiamata ad una funzione), che permettono ai moduli sviluppati dalla comunità o da un utente di inserire funzioni nel processo di esecuzione di Drupal. I moduli inclusi nel «core» forniscono le seguenti funzionalità: Inserire, modificare e catalogare i contenuti Eseguire ricerche 14 Inserire commenti Partecipare a forum Rispondere a sondaggi Lavorare a progetti di scrittura collaborativa Inserire e visualizzare i profili degli utenti Comunicare tra gli utenti e con gli amministratori del sito Modificare l'aspetto del sito utilizzando temi grafici già sviluppati e pronti all'uso Creare menu su più livelli della struttura di navigazione Localizzare l'interfaccia in diverse lingue, permettendo all'utente di scegliere Fornire feed RSS Aggregare e presentare i contenuti RSS di altri siti Registrare nuovi utenti e gestirne gli account Gestire in modo granulare permessi e ruoli per i singoli utenti o per interi gruppi Usare regole per impedire l'accesso al sito a determinati utenti in base a nome, indirizzo email, indirizzo IP Collezionare e presentare statistiche dettagliate del sito Gestire il caching (Il Web caching è la caching di documenti web (pagine HTML, immagini, ecc.) che permette di ridurre l'uso della banda e il tempo di accesso ad un Sito web. Una web cache memorizza copie di documenti richiesti dagli utenti, successive richieste possono essere soddisfatte dalla cache se si presentano certe condizioni) delle pagine ed il throttling (funzionalità che permette di disabilitare alcuni moduli/blocchi in caso di alto traffico del sito) Creare e specificare vari filtri sull'input degli utenti e diversi modelli di contenuto Generare indirizzi semplici da ricordare, permettendo una migliore catalogazione ai motori di ricerca. Inoltre, il sito di Drupal contiene centinaia di moduli gratuiti sviluppati dalla comunità. Alcune delle funzionalità fornite o migliorate dai moduli sviluppati dagli utenti: • Sistemi e-commerce • Flusso di lavoro redazionale • Gallerie fotografiche • Gruppi autogestiti • Sitemap di Google • Gestione di mailing list (liste di posta) • Integrazione con CVS (Concurrent Versions System, implementa un sistema di controllo versione: mantiene al corrente di tutto il lavoro e di tutti i cambiamenti in un insieme di file, tipicamente è l'implementazione di un software in via di sviluppo, in progetto, e permette a molti sviluppatori -potenzialmente distanti- di collaborare) • Gestire immagini e video • Gestire servizi di terze parti Critiche L'installazione di Drupal (e dei suoi moduli) richiede l'accesso a un database ed alcuni permessi avanzati, ad esempio la possibilità di usare comandi SQL (come select, insert, update, delete, create, drop, index, alter e lock tables). Alcuni fornitori di hosting web non offrono queste funzioni. Chi voglia usare Drupal deve accertarsi che il suo server offra queste funzioni prima di installare. 15 Come per altri sistemi di gestione dei contenuti, è possibile configurare il database di partenza da riga di comando oppure con phpMyAdmin. A partire dalla versione 5.0, è possibile installare Drupal e configurare il database quasi interamente tramite un'interfaccia web. Alcuni considerano più difficile imparare a usare Drupal e installarlo, rispetto ad altri CMS o a semplici piattaforme per blog come WordPress. Drupal 6.0, pubblicato il 13 febbraio 2008 ha una metodologia d’installazione più semplice rispetto alle versioni precedenti. Alcuni programmatori criticano Drupal perché non lo considerano sviluppato con la Programmazione Orientata agli Oggetti (Object Oriented Programming), ma programmare Drupal da una prospettiva orientata agli oggetti spiega come i principi della OOP e della programmazione orientata agli aspetti (AOP) siano applicati anche a Drupal. Algoritmo Intuitivamente, un algoritmo si può definire come un procedimento che consente di ottenere un risultato atteso eseguendo, in un determinato ordine, un insieme di passi semplici corrispondenti ad azioni scelte solitamente da un insieme finito. Quindi con il termine algoritmo si intende, in genere, un metodo per ottenere un certo risultato (risolvere un certo tipo di problema) attraverso un numero finito di passi. Nel senso più ampio della parola, "algoritmo" è anche una ricetta di cucina, o la sezione del libretto delle istruzioni di una lavatrice che spiega come programmare un lavaggio. Di norma, comunque, la parola viene usata in contesti matematici (fin dalle origini) e soprattutto informatici (più recentemente). Un esempio più appropriato di algoritmo potrebbe essere, quindi, il procedimento per il calcolo del massimo comune divisore o del minimo comune multiplo. Da questa definizione si evincono le quattro proprietà fondamentali dell'algoritmo: o la sequenza di istruzioni deve essere finita (finitezza); o la sequenza di istruzioni deve portare ad un risultato (effettività); o le istruzioni devono essere eseguibili materialmente (realizzabilità); o le istruzioni devono essere espresse in modo non ambiguo (non ambiguità). Affermando che i passi costituenti di un algoritmo debbano essere "semplici", si intende soprattutto che essi siano specificati in modo non ambiguo, ovvero immediatamente evidenti a chi sarà chiamato ad applicare l'algoritmo, cioè il suo esecutore. Il concetto di algoritmo Il calcolatore elettronico per risolvere un problema utilizza un algoritmo, cioè un insieme di azioni (o istruzioni) che, eseguite secondo un ordine prestabilito, permettono di trovare il risultato cercato sulla base dei dati in ingresso. Un computer è un rapidissimo esecutore di sequenze di istruzioni (gli algoritmi). Algoritmo: procedura di trasformazione di un insieme di dati iniziali in un insieme di risultati finali mediante una sequenza di istruzioni. Linguaggio di programmazione: linguaggio (insieme di simboli e regole) per rappresentare le istruzioni di un algoritmo e la loro concatenazione. 16 Programma: algoritmo scritto in un linguaggio di programmazione al fine di comunicare al calcolatore elettronico le azioni da eseguire, cioè esso è memorizzato su disco e attende di essere caricato in RAM per poi essere eseguito (entità passiva, statica). Processo: programma in esecuzione sul computer, cioè è caricato in RAM e la CPU esegue una ad una l’insieme di istruzioni del programma (entità attiva, dinamica). In informatica, con il termine algoritmo si intende un metodo per la soluzione di un problema adatto a essere implementato sotto forma di programma. In fase di progettazione di un software l’algoritmo consiste nella soluzione di un problema, che si è analizzato, seguendo una serie di operazioni sequenziali rappresentate sotto forma di diagramma a blocchi o di pseudocodifica. Esistono cinque tipi di blocchi elementari: La combinazione di questi blocchi elementari serve per rappresentare graficamente un algoritmo, e questo può essere fatto solo se: - viene usato un numero finito di blocchi; - lo schema inizia con un blocco iniziale e termina con un blocco finale; - ogni blocco soddisfa delle condizioni di validità. Tale sequenza di operazioni deve essere applicabile ad ogni linguaggio di programmazione. Un programma può essere visto come un insieme di algoritmi diversi che interagiscono per risolvere problemi complessi. 17 Processo Concetti generali di un processo Ecco alcuni concetti e definizioni di base che caratterizzano lo studio della teoria e degli algoritmi della schedulazione real-time. Processo: Per processo o task si intende una sequenza di istruzioni che, in assenza di altre attività, viene eseguita dal processore in modo continuativo fino al suo completamento. La situazione di riferimento per un ambiente real-time è quella in cui sono presenti numerosi processi, ciascuno con particolari esigenze di schedulazione. In questo caso è necessario individuare una strategia di assegnazione della CPU che sequenzializzi l’uso della risorsa fisica secondo un criterio stabilito a priori. Un processo da quando viene caricato in RAM e durante tutto il tempo dell’esecuzione può trovarsi in uno dei 5 stati seguenti: 1) new (nuovo o inizio): il processo è stato appena creato, più precisamente è stato creato sulla RAM il suo descrittore di processo (struttura dati contenente le informazioni sul processo); 2) ready (pronto): il processo è pronto per essere eseguito dalla CPU, cioè tutte le sue istruzioni sono state caricate in RAM ed ha tutto ciò che gli serve per l’esecuzione tranne la disponibilità della CPU; 3) running (in esecuzione): il processo è sulla CPU, ovvero la CPU sta eseguendo le istruzioni di quel processo; 4) waiting (bloccato o in attesa): il processo è in attesa di un evento, cioè non può essere pronto perché sta aspettando che si verifichi una certa condizione (di solito legata ai dispositivi I/O, ma può dipendere anche da altri motivi); 5) terminated (in terminazione): il processo sta per terminare, cioè il processo rimane in questo stato da quando viene dato il comando di terminazione fino a quando il processo non scompare dalla memoria del Sistema Operativo. La seguente illustrazione mostra il ciclo di vita di un processo: 18 Definiamo attivo un processo potenzialmente in grado di eseguire su un processore (CPU). Un processo attivo in attesa di un processore (occupato nell’esecuzione di un altro processo) viene definito pronto e accodato in una coda di attesa detta ready queue (coda pronti). Un processo che correntemente utilizza il processore viene indicato come processo in esecuzione. L’insieme delle regole che determinano l’esecuzione di un processo pronto presente nella ready queue costituisce l’algoritmo di schedulazione. Solitamente le regole di schedulazione sono applicate all’ingresso della coda di attesa in modo da ottenere una ready queue già ordinata secondo la precedenza di esecuzione, in altre parole, l’ordinamento della coda viene realizzato in modo che il processo da eseguire sia nella prima posizione di uscita. In alcuni sistemi è prevista la possibilità di interrompere in qualsiasi istante il processo in esecuzione per riportarlo nella coda pronti, e dare la possibilità ad un processo a priorità superiore di disporre del processore. L’operazione di sospensione dell’esecuzione e reinserimento nella ready queue di un processo è nota con il termine preemption (prelazione o “revoca della CPU”). Si osservi che l’assegnazione effettiva del processore al processo pronto che viene schedulato è indicata con il termine dispatching, e viene effettuata dal sistema operativo alla terminazione di un task o dopo un’operazione di preemption. Quindi in definitiva possiamo dire che il cambio di processo, che utilizza la CPU, avviene quando: o il processo corrente viene prelazionato da un altro di maggiore priorità, ritornando nella ready queue, passa cioè dallo stato running a quello ready; o il processo corrente si blocca su qualche condizione, passa dallo stato running allo stato waiting (deve verificarsi prima una certa condizione affinché il processo ritorni allo stato ready); o il processo corrente termina il suo “round”, cioè termina il tempo di utilizzo (continuo) della CPU che gli è stato riservato, passa dallo stato running allo stato ready; o il processo corrente termina tutte le istruzioni di cui è composto, cioè passa dallo stato running allo stato terminated. La figura seguente illustra sinteticamente l’architettura di uno schedulatore: Schedulazione fattibile: Una schedulazione è detta fattibile se esiste un’assegnazione di task al processore tale che tutti i task siano completati rispettando un insieme di vincoli prefissati. Insieme di task schedulabile: Un insieme di task (task-set) è detto schedulabile se per esso esiste una schedulazione fattibile. 19 Vincoli sui processi La definizione di schedulazione fattibile richiede che l’esecuzione dei task avvenga rispettando i vincoli a cui questi sono sottoposti. In generale esistono tre categorie di vincoli sui processi: vincoli temporali, vincoli di precedenza, e vincoli su risorse condivise. 1) Vincoli temporali Nei sistemi real time i task sono soggetti a vincoli temporali che devono essere rispettati perché l’elaborazione sia valida. Un tipico esempio di vincolo temporale è la deadline di un processo, che rappresenta l’istante temporale entro cui il processo deve terminare la propria esecuzione e produrre un risultato. Il concetto di deadline è centrale nella teoria dello scheduling real time, tanto da determinare una classificazione di base dei processi real time relativamente alle conseguenze di una mancata deadline. In generale, i task di un sistema real-time possono essere di 2 tipi: Processo hard real time: Un processo real-time è di tipo hard se il mancato rispetto della propria deadline (cioè il task non finisce di computare totalmente entro la sua deadline) comporta un danno irreparabile sul sistema, impedendone il corretto funzionamento. - Processo soft real time: Un processo real-time è di tipo soft se il mancato rispetto della propria deadline comporta un danno non irreparabile al sistema, quindi non compromette il corretto funzionamento del sistema. Il superamento della deadline produce un degrado delle prestazioni proporzionale al tempo di superamento della deadline. Si osservi che un processo soft real-time non è vincolato da una deadline “rigida” e può essere completato anche oltre il termine da questa indicato. Sostanzialmente questa distinzione si traduce nella diversa quantificazione dei costi di una possibile inesattezza temporale del sistema. Un esempio di task soft real-time può essere un riproduttore DVD, in cui il mancato rispetto dei vincoli si traduce in un degrado della qualità del filmato, ma non pregiudica il proseguimento della riproduzione; mentre un task hard real-time può essere il controllore della temperatura del nocciolo di una centrale nucleare, dove il mancato rispetto dei vincoli temporali può provocare un evidente disastro. - I parametri principali che caratterizzano il comportamento temporale di un processo sono i seguenti: • tempo di arrivo (a): istante di tempo in cui il task arriva nella ready queue e quindi diventa pronto per l’esecuzione; • tempo di computazione (C): tempo necessario al processore per eseguire completamente un task senza interruzioni, oppure tempo che il task deve usufruire della CPU affinché esso termini tutte le sue istruzioni; • deadline (d): istante di tempo entro cui il task deve essere completato; • istante di inizio (s): istante di tempo in cui il task va in esecuzione per la prima volta; • istante di fine (f): istante di tempo in cui il task termina la sua esecuzione; • latenza (L): rappresenta il ritardo di completamento del task rispetto alla deadline ed è così definita: L = f − d (si osservi che un task che completa la propria esecuzione prima della deadline è caratterizzato da latenza negativa). Il significato e le relazioni tra i parametri sopra definiti sono chiariti nella seguente figura, la quale illustra i parametri temporali caratteristici di un processo: 20 Un’altra possibile caratterizzazione temporale dei processi è quella che riguarda la regolarità delle attivazioni dei processi stessi. Da questo punto di vista si può fare la distinzione fra processi periodici e processi aperiodici. • Processo periodico: Un processo è detto periodico se è caratterizzato da una sequenza infinita di attività identiche, dette istanze, che si presentano con cadenza regolare. Il parametro T rappresenta il periodo del processo (cioè il lasso di tempo che intercorre tra due esecuzioni di un task periodico), ma un processo periodico è caratterizzato anche da un tempo di computazione C e da una deadline D (relativa al periodo); si suppone che i parametri T, C, e D siano costanti per ogni istanza. Quindi un task periodico si ripresenta nella ready queue ad intervalli di tempo regolari e presenta sempre lo stesso tempo di computazione, la stessa deadline e lo stesso periodo. La seguente figura mostra un esempio di processo periodico: • Processo aperiodico: Un processo è detto aperiodico se è costituito da una sequenza di attività identiche, ciascuna delle quali caratterizzata da un tempo di arrivo, un tempo di calcolo, e una deadline ma che si presentano ad intervalli irregolari, quindi non prevedibili a priori. Nei sistemi operativi nei quali non esiste un meccanismo di gestione esplicita dei vincoli temporali è possibile comunque gestire tali vincoli trasformandoli in priorità. Intuitivamente, un task con caratteristiche temporali molto stringenti si vedrà assegnata una priorità maggiore di quella assegnata a un task con vincoli temporali meno critici. Con questo approccio è possibile utilizzare uno schedulatore su base prioritaria per gestire i vincoli temporali dei processi. La seguente illustrazione mostra uno schema concettuale di uno scheduler che gestisce vincoli temporali: 21 2) Vincoli di precedenza Esistono applicazioni nelle quali i processi non possono essere eseguiti in ordine arbitrario. In questi casi i processi presenti nel sistema sono legati tra loro da relazioni di precedenza che sono rappresentabili attraverso un grafo orientato e aciclico, detto grafo di precedenza. - Si dice che il task J1 precede il task J2 (J1<J2) se esiste un cammino sul grafo di precedenza che parte dal nodo J1 e arriva al nodo J2. - Si dice che J1 è immediato predecessore di J2 (J1→J2) se esiste nel grafo di precedenza un arco diretto uscente dal nodo J1 ed entrante nel nodo J2. Nella Figura 1.3.4 è illustrato un grafo di precedenza. Si osservi che J1 è l’unico processo che può essere eseguito immediatamente e i processi J2, J3 e J4 sono vincolati ad essere eseguiti solo dopo la sua terminazione; J5 può essere eseguito solo dopo il completamento dei task J2 e J3. Di seguito viene mostrato un esempio di grafo di precedenza tra processi: 22 3) Vincoli su risorse Per risorsa intendiamo una qualunque entità software che può essere utilizzata da uno o più processi durante l’esecuzione. Tipicamente una risorsa può essere una struttura dati, una zona di memoria, o una porzione di codice. Una risorsa che può essere utilizzata da più processi è detta condivisa (es. RAM, periferiche di I/O), mentre se essa è riservata solo ad un particolare processo viene detta dedicata. Le risorse condivise che non ammettono accessi contemporanei da parte di più di un processo, allo scopo ad esempio di mantenere la coerenza delle strutture dati interne, sono dette risorse mutuamente esclusive (es. CPU); gli accessi a queste risorse devono essere serializzati per garantire il corretto funzionamento del sistema (gestiscono un solo processo alla volta). Per garantire un accesso mutuamente esclusivo alle risorse, il sistema operativo necessita di un meccanismo di sincronizzazione degli accessi. Quando si parla di vincoli su risorse, si intende riferirsi a vincoli di mutua esclusione su risorse condivise. Il rispetto di un vincolo su risorsa è ottenuto sincronizzando i processi che utilizzano tale risorsa facendo in modo che l’accesso ad essa sia mutuamente esclusivo. I vincoli su risorse hanno importanti conseguenze sulla schedulazione dei processi perché la CPU, essendo una risorsa mutuamente esclusiva, può gestire un solo task alla volta e quindi se la CPU è già occupata da un task gli altri task, eventualmente presenti nella ready queue, per essere eseguiti devono aspettare che la CPU si liberi (a meno di eventuali priorità se l’algoritmo di scheduling è con prelazione). Processo bloccato: Un processo è detto bloccato (waiting) se è in attesa di una risorsa condivisa occupata da un altro processo. I processi bloccati sono accodati in una coda associata al meccanismo di protezione della risorsa condivisa. Il sistema operativo prevede uno stato apposito per la sospensione dei processi bloccati su risorse. Un processo in esecuzione entra nello stato waiting se prova ad accedere ad una risorsa occupata, e resta in questo stato fino a quando la risorsa non viene liberata dal processo che la stava utilizzando precedentemente. Un processo bloccato esce dallo stato waiting quando la risorsa occupata viene liberata e quindi può e va ad utilizzarla esso stesso. Scheduler Lo scheduler (da to schedule letteralmente "mettere in lista", ovvero "pianificare") è un componente fondamentale dei sistemi operativi multitasking (più processi in RAM contemporanemente) in grado di far eseguire, al processore di un computer, attraverso l'omonima operazione di scheduling, più processi (task) concorrentemente attraverso varie politiche di scheduling. Esso rappresenta dunque il gestore del multitasking attraverso criteri di assegnazione delle risorse di elaborazione ai vari processi e implementati attraverso vari tipi di algoritmi di scheduling. Generalmente, infatti, computer con un processore sono in grado di eseguire un processo per volta; quindi per poter far convivere più task è necessario usare uno scheduler. Nel dettaglio lo scheduler si occupa di fare avanzare un processo interrompendone temporaneamente un altro, realizzando così quello che è chiamato cambio di contesto (context switch) all'interno del ciclo del 23 processore. Quindi lo scheduler decide i tempi e i modi con cui i processi possono usare la CPU in base a vari algoritmi di scheduling che permettono di scegliere nella maniera più efficiente possibile a quale task assegnare la CPU. Scheduling della CPU Lo scheduling è un'operazione molto importante per il corretto ed efficiente funzionamento del calcolatore. Infatti, non solo consente di eseguire più processi contemporaneamente, ma consente anche di migliorare l'utilizzo del processore. Ad esempio, quando è necessario eseguire un'operazione di I/O, il processore non può proseguire l'elaborazione del processo attualmente in esecuzione fino al completamento della stessa. Dato che le operazioni di I/O sono molto più lente del processore sarebbe un inutile spreco di risorse se il processore rimanesse bloccato fino al completamento delle stesse. Per evitare questo, le operazioni di I/O vengono gestite unicamente dal Sistema Operativo che, nel frattempo, assegna l'uso del processore ad un altro processo. In questo modo si massimizza l'uso delle risorse del sistema. È importante la distinzione tra scheduling con diritto di prelazione (scheduling preemptive) e scheduling senza diritto di prelazione (scheduling non-preemptive o scheduling cooperative). Nel primo caso lo scheduler può sottrarre il possesso del processore al processo anche quando questo potrebbe proseguire nella propria esecuzione. Nel secondo caso, invece, lo scheduler deve attendere che il processo termini o che cambi il suo stato da quello di esecuzione a quello di attesa o di pronto, a seguito, ad esempio, di una richiesta di I/O oppure a causa di un segnale di interruzione (interrupt). Esistono vari algoritmi di scheduling che tengono conto di varie esigenze e che possono essere più indicati in alcuni contesti piuttosto che in altri. La scelta dell'algoritmo da usare dipende da cinque principali criteri: • Utilizzo del processore: percentuale di tempo in cui la CPU esegue le istruzioni dei processi rispetto al tempo in cui essa svolge le operazioni del sistema operativo o rimane inutilizzata; la CPU deve essere attiva il più possibile, ovvero devono essere ridotti al minimo i possibili tempi morti, quindi questo parametro è da massimizzare; • Produttività: il numero di processi completati in una determinata quantità di tempo, quindi questo parametro è da massimizzare; • Tempo di completamento: il tempo che intercorre da quando il processo viene creato (new) a quando viene completata la sua esecuzione (terminated), quindi è da minimizzare; • Tempo d'attesa: il tempo in cui un processo pronto per l'esecuzione (ready) rimane in attesa, nella ready queue, della CPU, quindi è da minimizzare; • Tempo di risposta: il tempo che trascorre tra la sottomissione del processo e l'ottenimento della prima risposta, quindi è da minimizzare. Gli algoritmi di scheduling incidono fortemente sul tempo di attesa e solo parzialmente sugli altri parametri. Quindi essi vengono valutati in base al tempo medio di attesa. 24 Obiettivi dello scheduling Un algoritmo di scheduling si pone i seguenti obiettivi: • Equità: processi dello stesso tipo devono avere trattamenti simili; • Bilanciamento: tutte le parti del sistema devono essere sfruttate. Inoltre bisogna effettuare un distinguo tra i sistemi batch e i sistemi interattivi. Nei primi la produttività, ossia la massimizzazione del numero di job completati deve essere massimizzata e il tempo di completamento deve essere minimizzato. Nei secondi il tempo di risposta deve essere il minimo possibile per dare l'idea di continuità all'utente e la proporzionalità deve essere rispettata, ossia il tempo di risposta deve essere proporzionale alla complessità dell'azione. Problema generale dello scheduling Dato un task-set, ovvero un insieme di n task J = {J1, J2, …, Jn}, di cui siano noti i vincoli temporali, il grafo dei vincoli di precedenza e le modalità di utilizzo delle risorse condivise, il problema generale dello scheduling consiste nella determinazione di una schedulazione fattibile dell’insieme J. In altre parole, la risoluzione del problema generale dello scheduling deve individuare una sequenza di assegnazione dei task al processore tale che: o tutti i task terminino la propria elaborazione entro la deadline; o i vincoli di precedenza espressi dal grafo di precedenza siano rispettati; o l’accesso alle risorse mutuamente esclusive sia correttamente sequenzializzato. Il problema dello scheduling nella sua forma più generale sopra enunciata è computazionalmente intrattabile in tempo polinomiale rispetto al numero di processi; cioè non esiste alcun algoritmo con complessità polinomiale di grado n capace di individuare una schedulazione fattibile. Al fine di individuare algoritmi con complessità polinomiale si introducono delle ipotesi semplificative rispetto al problema generale dello scheduling. Se si considerano, ad esempio, casi nei quali i vincoli di precedenza sono assenti, oppure è ammissibile avere una preemptive, oppure non sono presenti vincoli sulle risorse, il problema della schedulazione viene notevolmente semplificato e può essere risolto con algoritmi noti. Classificazione degli algoritmi di scheduling Di seguito viene riportata una sintetica classificazione dei possibili algoritmi di scheduling. Si noti che le tipologie di algoritmi considerate sono tra loro ortogonali. • • Uniprocessore: se il calcolatore presenta un’unica CPU; Multiprocessore: se il calcolatore presenta due o più CPU; • Preemptive: lo scheduler ha la possibilità di sospendere l’esecuzione di un task che utilizza la CPU e di assegnare quest’ultima ad un altro task con priorità maggiore; Non preemptive: lo scheduler non può togliere la CPU ad un task per assegnarla ad un altro task (anche se quest’ultimo ha priorità maggiore), la CPU può essere assegnata solo quando il task che la stava utilizzando ha terminato (o tutte le sue istruzioni o il “round” di tempo previsto). • 25 • • • • • • Statici: le decisioni dello scheduler sono basate su parametri “fissi” assegnati ai task prima della loro attivazione; Dinamici: le decisioni dello scheduler sono basate su parametri assegnati ai task che “possono variare” durante l’evoluzione del sistema; Off–line: lo scheduling è effettuato prima dell’attivazione dei task (all’attivazione del taskset); On–line: lo scheduling è effettuato ad ogni attivazione o termine di un task; Best–effort: questi algoritmi massimizzano o minimizzano (a seconda dei casi) gli indici di prestazioni ma non garantiscono la fattibilità, usati per il soft real-time; Guaranteed: questi algoritmi garantiscono la fattibilità ponendosi nell’ottica dello scenario worst case (caso peggiore, cioè tutti i task hanno come tempo di arrivo 0 -arrivano tutti contemporaneamente-) ma non si curano degli indici di prestazione, usati per l’hard realtime; • Clairvoyant: lo scheduling prevede l’arrivo di un nuovo task; • Optimal: lo scheduling minimizza una qualche funzione di costo; La prima classificazione riguarda semplicemente il tipo di sistema di elaborazione considerato e in particolare la presenza di uno o più processori. Gli algoritmi di schedulazione preemptive prevedono la possibilità di sospendere in qualsiasi istante l’esecuzione di un processo a favore di un altro; nel caso in cui non è prevista la preemption ogni decisione riguardante la schedulazione viene presa a seguito della autosospensione di un task o del suo completamento. Gli algoritmi statici utilizzano una regola di decisione che si basa su parametri fissi, assegnati ai processi una volta per tutte prima della loro attivazione; nel caso dinamico al contrario, i parametri utilizzati dalla regola di decisione sono soggetti a variazioni durante la vita del processo. La classificazione off–line/on–line distingue tra algoritmi che realizzano la schedulazione prima dell’attivazione dei processi, sulla base di informazioni note a priori (off–line), e algoritmi che operano durante il run–time ricalcolando l’ordinamento dei task a ogni nuova attivazione. Infine per algoritmo best–effort si intende un algoritmo che massimizza (o minimizza) un indice di prestazione definito globalmente sull’insieme dei task. Adottando una strategia di questo tipo si privilegiano le prestazioni medie del sistema e sono possibili violazioni di vincoli temporali relativi a qualche task. Gli algoritmi guaranteed assicurano il rispetto dei vincoli temporali di ogni singolo task utilizzando un test di garanzia eseguito sull’intero insieme dei task ogni volta che un nuovo processo entra nel sistema. 26 Sistema operativo real-time Un sistema operativo real-time o in tempo reale (abbreviato in RTOS) è un sistema operativo specializzato per il supporto di applicazioni software real-time. Questi sistemi vengono utilizzati tipicamente in ambito industriale (controllo di processo, pilotaggio di robot, trasferimento dati nelle telecomunicazioni) o comunque dove sia necessario ottenere una risposta dal sistema entro un tempo prefissato. Un sistema operativo real-time non deve essere necessariamente veloce: non è importante l'intervallo di tempo in cui il sistema operativo/applicativo deve reagire; l'importante è che risponda entro un tempo massimo pre-determinato. In altre parole il sistema deve essere prevedibile. In pratica un sistema real-time deve garantire che una elaborazione (o task) termini entro un dato vincolo temporale o scadenza (detta in gergo deadline). Per garantire questo è richiesto che la schedulazione delle operazioni sia fattibile. Il concetto di fattibilità di schedulazione è alla base della teoria dei sistemi real-time ed è quello che ci permette di dire se un insieme di task sia eseguibile o meno in funzione dei vincoli temporali dati (periodo del task -se il task è periodico-, deadline, tempo di computazione del task sulla CPU, tempo di arrivo del task nella ready queue). Sistemi Hard real-time e soft real-time I sistemi real-time si possono dividere quindi in due categorie: • I sistemi "hard" sono quelli che possono garantire la fattibilità di schedulazione di un insieme di task hard e soft real-time. • I sistemi "soft" sono quelli che possono garantire la fattibilità di schedulazione di un insieme di soli task soft real-time (meno rigidi dei sistemi hard real-time). Caratteristiche di un sistema real-time Un sistema real-time dovrebbe possedere le seguenti caratteristiche: • Schedulazione ottima: tutti i task sono noti a priori così come i vincoli temporali; dovrebbe essere possibile, dunque, avere uno schedulatore che implementi una schedulazione che minimizzi al massimo il costo delle operazioni,cioè tutti i task, appartenenti al task-set, dovrebbero terminare regolarmente senza andare i overflow e il tempo di occupazione della CPU necessario affinché tutto il task-set termini dovrebbe essere il minore possibile. • Condivisione delle risorse: i task sono entità separate ma che concorrono ad uno stesso scopo, pertanto non è necessario avere spazi di indirizzamento separati. • Garanzia di esecuzione: tutti i task di tipo hard real-time devono terminare entro le proprie deadline quindi, nel caso in cui arrivi un nuovo task o un task non possa completare entro la deadline, una notifica anticipata del sistema può essere utilizzata per impedire l'esecuzione del nuovo task o di recuperare l'esecuzione del task che sta per sfondare. • Prevedibilità delle chiamate di sistema: il sistema deve essere in grado di valutare i tempi di calcolo di ogni task per determinare la schedulazione fattibile; quindi ogni chiamata di sistema deve avere un tempo di esecuzione massimo ben definito in modo da non introdurre ritardi indefiniti. 27 I fattori che minano la prevedibilità I prodotti delle famiglie Windows e Unix non soddisfano le caratteristiche tipiche di un sistema real-time: ad esempio, pur gestendo l'esecuzione di più processi con pre-rilascio, non è possibile prevedere in alcun modo quale sarà il tempo di esecuzione di un singolo processo. Inoltre, l'utilizzo di hard disk per la conservazione dei dati, dispositivi USB o altri dispositivi che introducono forti latenze di esecuzione da parte della CPU, rende impossibile stabilire con certezza quanto tempo sarà necessario per reperire l'informazione utile alla corretta esecuzione del codice. Ci sono diversi fattori che causano la non prevedibilità nella risposta del sistema operativo. Tra di essi, i principali sono i seguenti: • Il DMA: può rubare il bus (il bus è unico) alla CPU ritardando l'esecuzione di un task critico. In un sistema real-time si preferisce quindi disattivarlo o usarlo in modalità timeslice (fetta di tempo) dove si assegna in maniera costante e fissa il bus al DMA anche se non ci sono operazioni da fare (un intervallo di tempo il bus è utilizzabile dal DMA un altro intervallo no). DMA (Direct Memory Access, «accesso diretto alla memoria») è un meccanismo che permette ad alcuni sottosistemi hardware di un computer (periferiche) di accedere direttamente alla memoria di sistema per scambiarsi dati, oppure leggere o scrivere, senza chiamare in causa la CPU per ogni byte trasferito tramite il meccanismo usuale dell'interrupt (con l’interrupt ogni operazione prima di essere eseguita deve essere comunicata alla CPU, la quale, interrompendo l’operazione che già stava eseguendo, governa poi l’esecuzione della nuova operazione se questa ha priorità maggiore di quella già in esecuzione) e la successiva richiesta di operazione desiderata, ma generando un singolo interrupt per blocco trasferito (la CPU viene interrotta solo a termine dell’operazione eseguita direttamente dalla periferica senza l’ausilio della CPU). Il DMA, tramite il rispettivo elemento hardware, ha quindi il compito di gestire i dati passanti nel bus permettendo a periferiche che lavorano a velocità diverse di comunicare senza assoggettare la CPU a un enorme carico di interrupt che ne interromperebbero continuamente il rispettivo ciclo di elaborazione. • La cache: può causare non prevedibilità poiché esistono casi in cui essa fallisce e può causare ritardi nell’accesso alla memoria da parte della CPU. Dovendo considerare quindi il caso peggiore si preferisce non usarla affatto. • Meccanismi di gestione della memoria (tecniche di allocazione dei dati nella RAM): queste tecniche non devono introdurre ritardi non prevedibili durante l'esecuzione di task critici, ad esempio la paginazione può causare dei page fault (non viene trovata nella RAM la pagina di memoria contenente l’istruzione da eseguire in quel preciso momento) intollerabili per un sistema hard real-time. Tipicamente si usa la segmentazione o la partizione statica della memoria. • Le interruzioni: sono generate da dispositivi periferici quando hanno qualche informazione da scambiare con la CPU. Queste interruzioni durante l'esecuzione di un task critico generano ritardi non prevedibili e quindi si preferisce disattivarle. • I sistemi di power management: sono meccanismi hardware che possono rallentare la CPU o far eseguire ad essa del codice utile a dissipare minor energia. È chiaro che in un sistema real-time è importante non sfondare una deadline piuttosto che consumare poca energia, quindi questi meccanismi vengono disattivati. 28 Scheduling Real-time dei task periodici I task periodici vengono indicati con il simbolo τ ed hanno i seguenti parametri: • Tempo di computazione C: tempo necessario affinché il task termini tutte le sue istruzioni; • Deadline relativa D: vincolo temporale entro il quale il task dovrebbe finire la computazione; questo è un valore fisso del task che si ripete sempre ad ogni periodo; • Deadline assoluta d: tempo rimanente al task affinché esso finisca la sua computazione; questo è un valore che varia da periodo a periodo in base al passare del tempo (più passa il tempo, più mi avvicino alla deadline relativa D e più la deadline assoluta d diminuisce); • Periodo T: tempo che intercorre tra due esecuzioni di un task periodico, una volta terminato un periodo il task si ripresenta con gli stessi valori dei parametri del periodo precedente. • Tempo di arrivo a: istante di tempo in cui il task arriva nella ready queue e quindi diventa pronto per l’esecuzione; • Istante iniziale s: istante di tempo in cui il task va in esecuzione per la prima volta; • Istante finale f: istante di tempo in cui il task termina la sua esecuzione; • Latenza L: rappresenta il ritardo di completamento del task rispetto alla deadline ed è così definita: L = f − d L’immagine seguente mostra i parametri principali di un task periodico: 29 Tra i vari algoritmi di scheduling applicati ai task periodici τ vi sono: 1) EDF (Earliest Deadline First) Questo algoritmo ordina i task in base alla deadline d, nel senso che ad ogni istante la CPU è assegnata al task con la deadline “assoluta” d più vicina. In pratica, privilegia quei task aventi la deadline d più breve in quell’istante di tempo. In sintesi la priorità è inversamente proporzionale alla deadline assoluta d. Attenzione: per deadline assoluta d non si intende il valore “statico” che ogni preciso task ha in ciascun periodo (cioè il valore proprio della deadline D di quel task che ha in ogni periodo T), ma il valore “dinamico” che assume la deadline d del task in quell’istante di tempo (più aumenta il tempo più il valore della deadline d diminuisce, questo riferito ad un solo periodo T); cioè all’inizio di un periodo T il task ha una precisa deadline D “relativa” (questo valore è fisso per il task i-esimo ed è uguale anche nei periodi successivi di questo) ma durante il periodo questa deadline d “diminuisce” al passare del tempo (all’interno del periodo il valore della deadline d diminuisce). Dato quindi un task-set, il task a cui verrà assegnata per prima la CPU sarà quello avente la deadline assoluta d minore rispetto agli altri, e così via per i task successivi; in sintesi: il task con la deadline assoluta d minore ha maggiore priorità. Questo algoritmo è inoltre: Con prelazione: se so ha un task che in un preciso istante di tempo ha una deadline assoluta d minore rispetto al task che sta usando la CPU; a quest’ultimo gli viene tolta la CPU per assegnarla al task con maggiore priorità. Dinamico: le decisioni dello scheduler sono basate sul valore dinamico che le varie deadline assumono in ogni istante di tempo, quindi la priorità di ogni task varia col variare del tempo (la deadline assoluta d non è un parametro fisso). 2)RM (Rate Monotonic) Questo algoritmo ordina i task in base al periodo, nel senso che ad ogni istante la CPU è assegnata al task con il periodo T più breve. Ad ogni task è associata una priorità “fissa” proporzionale alla sua frequenza (rate), ovvero più è elevata la frequenza del task e più esso avrà una priorità elevata. Siccome la frequenza è l’inverso del periodo (f=T^-1) quanto detto si può riscrivere in un altro modo: più è piccolo il periodo del task è più esso avrà una priorità elevata. In pratica privilegia quei task aventi il periodo più breve in quel preciso istante di tempo. In sintesi la priorità è inversamente proporzionale al periodo T. Attenzione: per periodo T si intende il valore “statico” T di ogni preciso task (essendo il task periodico ha un preciso periodo T). Dato quindi un task-set, il task a cui verrà assegnata per prima la CPU sarà quello avente il periodo T minore rispetto agli altri, e così via per i task successivi; in sintesi: il task con il periodo T minore ha maggiore priorità. 30 Questo algoritmo è inoltre: Con prelazione: se si ha un task che in un preciso istante di tempo ha il periodo T minore rispetto al task che sta usando la CPU; a quest’ultimo gli viene tolta la CPU per assegnarla al task con maggiore priorità. Statico: le decisioni dello scheduler sono basate sul valore fisso dei vari periodi T di ogni task che hanno in ogni istante di tempo, quindi la priorità di ogni task è definita a priori e non varia col variare del tempo per tutta la durata dell’applicazione (il periodo T è un parametro fisso). 31 SPECIFICHE DI PROGETTO 32 Il mio progetto ha come obiettivo la creazione di un modulo per Drupal che, implementando degli algoritmi di scheduling real-time, mostri agli utenti della piattaforma, dopo che essi hanno inserito i vari parametri richiesti per i task, il risultato dell’elaborazione di questi algoritmi sotto una forma grafica rappresentata dal diagramma di Gantt. Quindi il mio ruolo in questo progetto è quello del web master perché vado io stesso a creare un modulo utilizzando i linguaggi di programmazione e successivamente mettendo lo stesso modulo a disposizione degli utenti che accedono alla piattaforma. Il modulo che vado a creare può essere tranquillamente aggiunto come ogni altro modulo di Drupal e quindi ogni utente, una volta che lo ha scaricato ed installato correttamente, può farne uso. Inoltre, esso ha un’interfaccia grafica semplice quindi è rivolto ad ogni tipologia di utenza. WERTHER è l'acronimo di WEb Real Time HElper Resolver, tradotto in italiano web realtime aiutante risolutore. Scopo del modulo è quello di fornire un supporto per la didattica degli algoritmi di scheduling in tempo reale. Tale supporto si manifesterà in modo chiaro ed evidente nella produzione a schermo di un diagramma di Gantt che illustri la schedulazione di un task-set definito in maniera interattiva dall'utente. L'interfaccia principale del modulo sarà costituita da una pagina dinamica. Inizialmente in tale pagina sarà visualizzata, in alto a sinistra, un riquadro con riferimento al primo task in cui sarà possibile inserire i parametri fondamentali dei task da schedulare. Tali parametri sono WCET o tempo di computazione (C), deadline relativa (D) e periodo (T). I parametri di ciascun task saranno inseriti dall’utente in dei riquadri, con i nomi di essi riferiti al relativo task; ogni riquadro dovrà contenere i parametri relativi ad un solo task, quindi in pratica saranno visualizzati tanti riquadri quanti sono il numero dei task che l’utente sceglierà di elaborare. Inizialmente, il form sarà costituito da un solo riquadro, riferito al primo task; l'utente potrà aumentare il numero di task da schedulare (e dunque i riquadri) cliccando su un apposito pulsante situato immediatamente a destra del riquadro. Si dovrà prevedere anche un pulsante per ridurre il numero di task da schedulare; ovvero i riquadri, da situare immediatamente a destra del pulsante precedente. Il form dovrà contenere da un minimo di uno ad un massimo di dieci riquadri. Per questi due pulsanti ho apportato delle modifiche grafiche, spostandoli in basso sotto tutti i riquadri dei task; in questo modo, ho dedicato tutto lo spazio superiore ai riquadri contenenti i task e i loro parametri, ottenendo quindi una migliore visualizzazione grafica di ciò che l’utente inserisce. Inoltre, avendo stabilito a priori che il numero minimo di task da schedulare deve essere uno e il numero massimo di task deve essere dieci, ho fatto in modo che: • se il form presenta un solo riquadro (quindi non si può eliminare) allora verrà visualizzato solo il pulsante che permette di aggiungere un altro riquadro, cioè un altro task; • se il form presenta dieci riquadri (quindi non se ne possono aggiungere altri) allora verrà visualizzato solo il pulsante che permette di eliminare l’ultimo riquadro, cioè l’ultimo task. Per migliorare la visualizzazione del form all’utente, ho fatto in modo che ogni riquadro, chiamato con il nome del relativo task, può essere ridotto al solo nome del task i-esimo semplicemente cliccando su di esso, e ri-cliccando su di esso il riquadro viene nuovamente esteso. In questo modo l’utente può avere una visione parziale e specifica del form (ad esempio solo sul task i-esimo che deve inserire i parametri), senza che sia costretto a scorrere in giù la pagina per visualizzare tutti i riquadri. Quindi, viene data all’utente anche la possibilità di mostrare e nascondere i contenuti dei riquadri semplicemente cliccando sul nome del riquadro. Nella sezione in alto a destra della pagina, saranno situati un menù di riepilogo a discesa che consentirà di scegliere l'algoritmo da applicare sul task-set, ed un pulsante che, cliccato, provocherà la visualizzazione del diagramma di Gantt nella parte bassa della pagina. Anche per questi due pulsanti ho apportato delle modifiche grafiche, cioè li ho spostati in 33 basso sotto tutti i riquadri dei task e sotto i tasti di aggiungi ed elimina task; in questo modo ho dedicato tutto lo spazio superiore ai riquadri contenenti i task e i loro parametri ottenendo quindi una migliore visualizzazione grafica di ciò che l’utente inserisce. Tutti questi spostamenti, oltre che per una questione grafica, sono stati fatti per evidenziare all’utente la sequenzializzazione ed i passi da compiere per utilizzare il modulo; cioè prima inserire i vari parametri dei task ed eventualmente scegliere il numero, successivamente selezionare l’algoritmo di scheduling da applicare ed infine cliccare sul tasto “Schedula” che attiva la submit del modulo e procede con l’elaborazione dei dati inseriti dall’utente. Il flusso di lavoro dell'utente potrà essere così schematizzato: 1. L'utente accede al modulo cliccando su un link sul menù del portale. 2. L'utente inserisce i parametri del primo task; 3. Eventualmente clicca sul pulsante per aggiungere riquadri al form ed inserisce ulteriori parametri; 4. Oppure se ha inserito un task in più, clicca sul pulsante per eliminare riquadri al form; 5. L'utente sceglie l'algoritmo di scheduling da utilizzare tra quelli disponibili nella SELECT; 6. L'utente clicca sul bottone “Schedula” per far partire l’elaborazione dei dati inseriti; 7. Il modulo visualizza il risultato a schermo tramite diagramma di Gantt. Inizialmente si prevede l'implementazione degli algoritmi di scheduling EDF e RM. Il modulo dovrà prevedere possibili estensioni ad altri algoritmi di scheduling sia altri per la gestione di task periodici sia algoritmi per la gestione di task aperiodici. Non si prevedono restrizioni di alcun tipo riguardo l'accesso alle funzionalità del modulo; cioè qualsiasi utente che ha scaricato il modulo e lo ha installato può usufruirne senza il bisogno di particolari permessi; oppure ogni utente che accede ad una piattaforma contenente questo modulo può utilizzarlo anche essendo un utente non registrato alla piattaforma. 34 REALIZZAZIONE PROGETTO 35 Drupal operazioni iniziali Dato che dovevo realizzare un modulo per Drupal, per prima cosa ho cercato di capire il modo di installazione di questo software, quindi l’ho installato e successivamente sono andato a documentarmi sulle sue principali funzionalità. Siccome dovevo lavorare in “locale” mi serviva un programma che mi emulava un web server vero e proprio e, grazie alle conoscenze derivate dall’esame di “Linguaggi e programmazione web”, ho deciso di utilizzare per questo XAMPP. XAMPP è un pacchetto software gratuito contenente Apache HTTP Server, il database MySQL e tutti gli strumenti necessari per utilizzare i linguaggi di programmazione PHP e Perl. Il programma è rilasciato sotto la GNU General Public License ed è un utile web server, gratuito e caratterizzato da un approccio user friendly. Mediante XAMPP è possibile avere un application server capace di interpretare pagine web dinamiche PHP. La versione XAMPP Lite contiene i seguenti componenti di base: • Il Web server: Apache HTTP Server; • Il database management system (o database server): MySQL e SQLite; • Il server FTP: ProFTPD; • Il Mail server: Mercury Mail Transport System (solo per Windows); • I linguaggi di scripting: Perl, PHP e/o Python. Poi su indicazione del professore sono andato sul sito www.drupal.org per trovare il setup di Drupal e tutta la documentazione ad esso inerente (guida sull’installazione, sull’utilizzo, sulle funzionalità, sui moduli, sui temi, ecc.). Installare Xampp e Drupal Di seguito andrò a descrivere passo passo il procedimento da utilizzare per installare e configurare Xampp e Drupal 6. Inizialmente bisogna scaricare il setup di Xampp Lite dal sito: http://www.apachefriends.org/it/xampp-windows.html#4532. 36 Installare XAMPP Lite nella directory C: e rispondere alle domande poste dal prompt dei comandi che appariranno successivamente in sequenza nel modo seguente: 1. y (per creare un icona sul desktop) 2. y (per creare i giusti percorsi) 3. n (per non rendere XAMPP Portable) 4. x (per uscire) Dopo aver installato Xampp sarà la volta di Drupal. Dal sito http://drupal.org/ scaricare l’ultima versione disponibile di Drupal 6. 37 Decomprimere il formato .rar scaricato (nel mio caso drupal-6.16.rar), ottenendo una cartella decompressa denominata “drupal-6.16”, al cui interno saranno contenuti i file di Drupal. Copiare l’intera cartella drupal-6.16 nella directory C/xampplite/htdocs e rinominarla con il nome “drupal” Avremo quindi in C/xampplite/htdocs la cartella “drupal” con dentro i vari file di Drupal. Entrare in C/xampplite/htdocs/drupal/sites/default, copiare (non rinominare!) il file default.settings.php ed incollarlo nella stessa cartella (C/xampplite/htdocs/drupal/sites/default) Fatto questo apparirà nella cartella corrente un file chiamato “Copia di default.settings.php”, 38 rinominare quest’ultimo con il nuovo nome “settings.php”. Avremo quindi in C/xampplite/htdocs/drupal/sites/default due file: default.settings.php e settings.php (settings.php serve per il corretto funzionamento del DB). Aprire XAMPP Control Panel. Mettere la spunta solo su "svc" di Apache (lasciare "svc" di MySql deselezionato) Cliccare su "Start" sia per Apache sia per MySql per avviare il server locale. Quando entrambi sono diventati running cliccare su "Admin" di MySql. Aprire con un browser il database, scrivendo nell’URL http://localhost/phpmyadmin/, e sulla colonna a sinistra di questa pagina cliccare sul tasto Home (icona a forma di casa) Nel campo di tipo textfield del form di nome "Crea un nuovo database" scrivere "drupal" e cliccare poi sul tasto "Crea". Così facendo abbiamo creato un database vuoto su cui poi far appoggiare Drupal. 39 Accedere a Drupal, scrivendo nell’URL http://localhost/drupal, e, una volta entrati nella pagina, cliccare "Install Drupal in English" per installare Drupal. 40 Nel campo "Database name" scrivere: drupal (cioè il nome del database, creato in precedenza, su cui si appoggerà Drupal) Nel campo "Database username" scrivere: root Lasciare in bianco il campo "Database password" Infine cliccare su “Save and continue” per procedere con l’installazione. 41 Apparirà una nuova schermata con altri campi dove inserire del testo, questi campi sono: "Site name": dove bisogna scrivere “localhost” "Username": dove bisogna scrivere “admin” "Password": dove bisogna scrivere “admin” Vi sono altri due campi dove si deve immettere un indirizzo email per il sito (io ho scritto [email protected]) e per l'admin (io ho scritto [email protected]). 42 Infine spuntare il checkbox (quadratino selezionabile) chiamato “Check for updates automatically” e cliccare sul bottone “Save and continue” per terminare l’installazione di Drupal. Non appena l’installazione sarà terminata apparirà una pagina contenente un testo, il quale informa l’utente che si è verificato un errore sull’email (perché si sono messe delle email inesistenti), ma a noi non interessa, quindi possiamo tranquillamente ignorarlo. 43 Una volta ignorato quest’errore verremo rediretti immediatamente nella pagina home di Drupal, alla quale possiamo accedere d’ora in poi immettendo nel campo URL del browser http://localhost/drupal. Da questa pagina ora, dopo aver fatto ovviamente il log-in ed essere entrato come “admin” della piattaforma, possiamo andare a personalizzare la nostra piattaforma attraverso l’aggiunta di nuovi moduli e temi, andare a creare dei blog, delle pagine, dei forum, ecc. Quindi gli URL principali per l’utilizzo di Drupal sono: http://localhost ---> consente l’accesso a XAMPP Lite http://localhost/phpmyadmin/ ---> consente l’accesso al database MySQL http://localhost/drupal ---> consente l’accesso a Drupal 44 Operazioni da svolgere subito dopo aver installato Drupal • Impostare cron. • Controllare che l'opzione Clean URLs sia attivata (Administer >> Site configuration >> Clean urls). Installare e abilitare il modulo "Pathauto" (Richiede l'abilitazione del modulo "Path" e l'installazione ed abilitazione del modulo "Token"). Il modulo Pathauto permette di sostituire le path assegnate da Drupal con delle path alias più user-friendly. Operazione da fare prima di pubblicare il sito o appena pubblichiamo una nuova pagina, in modo che il motore di ricerca indicizzi le path alias e non quelle originali è la seguente: Administer >> Site building >> URL aliases ("Automated alias settings" tab); ciò permetterà la sostituzione delle path assegnate da Drupal con delle path alias amichevoli. Creare vocabolari e termini, abilitando il modulo Taxonomy. • • 45 Da ricordare • Dopo aver abilitato un modulo: o abilitarne anche il relativo block; o dare agli utenti (ruoli) la possibilità di usarlo (permessi); o modificare le caratteristiche del content type. • Dopo aver creato un nuovo content type (o aver installato un modulo che crea un suo content type) dobbiamo abilitare il relativo block e dare i permessi agli utenti. • Dopo aver cambiato un tema riabilitare i vari blocks. Installare un modulo aggiuntivo Di seguito andrò ad elencare le operazioni da eseguire per installare su Drupal un modulo. 1. Il modulo da installare deve essere compatibile con la versione di Drupal installata 2. Scaricare il modulo (file.tar.gz) (ad esempio da http://drupal.org/project/modules) 3. Decomprimerlo con WinRar 4. Mettere il modulo (cartella contenente vari files e cartelle riguardanti il modulo stesso) in C/xampplite/htdocs/drupal/sites/all/modules 46 Se è il primo modulo ad essere aggiunto dobbiamo creare una cartella “modules” in C/xampplite/htdocs/drupal/ C/xampplite/htdocs/drupal/sites/all, , la quale andrà a contenere tutti i vari moduli eventualmente aggiunti dall’utente Ad esempio se abbiamo scaricato ed estratto il file “pathauto-6.x 6.x-1.5tar.gz” (cartella pathauto contenente i vari file del modulo pathauto) metteremo la cartella "pathauto" "pathauto in C/xampplite/htdocs/drupal/sites/all/mo C/xampplite/htdocs/drupal/sites/all/modules Attenzione a non mettere i moduli aggiuntivi in C/xampplite/htdocs/drupal/modules (qui ci sono i moduli del core di Drupal) 5. Leggere il file INSTALL.txt o README.txt (contenuto nel modulo) per sapere se il modulo necessita di particolari procedure/files/altri moduli/... per funzionare correttamente 47 6. Andare su Drupal in Administer >> Site building >> Modules (indirizzo http://localhost/drupal/admin/build/modules) e mettere la spunta sul checkbox del modulo per abilitarlo 7. Infine cliccare sul bottone “Save configuration” per salvare la configurazione e abilitare definitivamente il modulo. 48 Note: Alcuni moduli, per funzionare, richiedono che vengano cambiati i permessi (Administer >> User management >> Permissions). Se il modulo ha delle impostazioni, possono essere raggiunte andando in Administer >> Site building o Administer >> Site configuration. Disinstallare un modulo aggiuntivo Per disinstallare un modulo procedere nel modo seguente. 1. Disabilitare il modulo (andando in Administer >> Site building >> Modules) 2. In Administer >> Site building >> Modules cliccare sul tab "Uninstall" e disinstallare il modulo scegliendolo fra quelli in lista 3. Se il modulo non è nella lista, dopo averlo disabilitato, cancellarlo manualmente (eliminare la cartella contenente i files del modulo da .../sites/all/modules) 4. Se si disinstalla manualmente il modulo eliminandone la cartella, eseguire il modulo aggiuntivo "System Table Cleaner" il quale eliminerà tutti i riferimenti che il modulo ha creato nel database (infatti cancellando la cartella del modulo tutti i riferimenti che il modulo aveva creato nel database non vengono rimossi) Aggiornare un modulo esistente Per aggiornare un modulo già esistente ed installato si va inizialmente a controllare se per il preciso modulo è presente un nuovo aggiornamento. Per far questo si va su: Administer >> Reports >> Available updates. Se è presente un nuovo aggiornamento lo si scarica in formato .rar e lo si decomprime. Successivamente bisogna disabilitare su Drupal il modulo vecchio da aggiornare e dopo eliminare la cartella contenente i file del modulo da .../sites/all/modules. Mettere in .../sites/all/modules la cartella contenente i file del modulo aggiornato, in modo tale che questo riappaia nell’elenco di Drupal dei moduli da abilitare. Entrare in Drupal ed andare ad abilitare il nuovo modulo (contenente gli aggiornamenti). Eseguire lo script update.php per aggiornare il database (il nuovo modulo può infatti apportare modifiche al database): • Andare all'indirizzo "http://www.example.com/update.php" (oppure "http://www.example.com/test_site/update.php") • Se abbiamo installato Drupal seguendo questa guida l'indirizzo esatto è "http://localhost/drupal/update.php" • Fare un backup del database • Fare un backup del vecchio modulo (sorgente) • Mettere il sito offline (Administer >> Site configuration >> Site maintenance) • Seguire le istruzioni a schermo • Rimettere il sito online • Controllare in Administer >> Reports >> Available updates se il modulo è stato correttamente aggiornato 49 Installare un tema aggiuntivo Per installare un tema aggiuntivo su Drupal procedere in tal modo. 1. Il tema da installare deve essere compatibile con la versione di Drupal installata 2. Scaricare il tema (file.tar.gz) (ad esempio da http://drupal.org/project/themes) 50 3. Decomprimerlo con WinRar 4. Mettere il tema (cartella contenente vari files e cartelle che compongono il tema) tema in C/xampplite/htdocs/drupal/sites/all/themes ocs/drupal/sites/all/themes Se è il primo tema ad essere aggiunto dobbiamo creare una cartella “themes” in C/xampplite/htdocs/drupal/sites/all htdocs/drupal/sites/all,, la quale andrà a contenere tutti i vari temi eventualmente aggiunti dall’utente Ad esempio Marinelli se abbiamo scaricato ed estratto il file marinelli-6.x-2.96.tar.gz marinelli (cartella marinelli contenente i vari file del tema marinelli) metteremo la cartella "marinelli" "marinelli in C/xampplite/htdocs/drupal/sites/all/themes Attenzione a non mettere i temi aggiuntivi in C/xampplite/htdocs/drupal/themes (qui ci sono i temi del core di Drupal) 5. Leggere il file INSTALL.txt o README.txt (contenuto nel tema) per sapere se il tema necessita di particolari procedure/files/... per funzionare correttamente 6. Andare in Administer > Site building > Themes e mettere la spunta sul tema per abilitarlo e sul radio button per impostarlo come tema di default e infine disabilitare il tema precedente 7. Infine cliccare sul bottone “Save configuration” per salvare la configurazione e abilitare definitivamente il tema. 51 Dopo aver abilitato un tema si può notare subito il cambiamento dello sfondo, del font, ecc. Ad esempio il tema Marinelli è il seguente: 52 Organizzazione del lavoro Dopo aver visto a grandi linee come funziona la piattaforma Drupal, non ho iniziato direttamente a sviluppare il progetto, cioè non ho implementato subito il modulo task-scheduling. Infatti per prima cosa mi sono fatto uno schema mentale su come organizzarmi il lavoro, una strategia di elaborazione dell’informazione e delle conoscenze per poter realizzare il modulo software. Ho deciso che la strategia che più si addiceva alla realizzazione per questo tipo di progetto potesse essere un approccio bottom-up; cioè prima ho creato ed elaborato nel dettaglio ogni singola funzione che avrebbe composto il mio modulo, successivamente ho connesso le funzioni che si susseguivano logicamente (cioè quelle funzioni che messe insieme avevano uno scopo comune, ad esempio più funzioni assieme mi davano come risultato il form composto da tanti riquadri quant’è il numero di task che compongono il task-set) al fine di formare dei componenti (insiemi di funzioni) più grandi; infine ho interconnesso tutte queste macro componenti per formare l’intero programma. Ho optato per questa strategia di progettazione per le seguenti motivazioni: • Siccome il progetto da realizzare era abbastanza lungo, non volevo verificare l’esito del mio lavoro solo a modulo completo ma volevo controllarlo e testarlo in intervalli di tempo brevi; • Componendo una funzione alla volta avevo anche la possibilità di correggere, ove presenti, gli errori passo per passo, infatti in questo modo essi erano riconoscibili più facilmente grazie al fatto che il codice di cui era formata ogni funzione non era per niente lungo rispetto al codice che è andato a formare l’intero modulo; • È stata la prima idea di strategia adottabile che mi è venuta in mente dopo aver letto le specifiche, perché sono subito riuscito a separare i vari concetti e quindi ad individuare subito i macro componenti che dovevano andare a formare il mio modulo. L’utilizzo di questa strategia mi ha permesso di sviluppare il modulo pezzo per pezzo, senza essere per forza vincolato ad un ordine preciso da seguire nelle operazioni da svolgere e nello scrivere il codice delle funzioni, alla fine poi avrei messo insieme il tutto nell’ordine corretto. Dopo aver scelto la strategia di progettazione, ho individuato precisamente quali dovevano essere i miei macro componenti e da quante e quali funzioni dovevano essere composti. Il modulo finale era il risultato dell’interconnesione di queste tre macro componenti di funzioni: • Form composto da riquadri che si mostrano e nascondono all’utente quando esso clicca sull’etichetta di ciascuno: insieme di funzioni che andavano a costituire la parte iniziale del modulo, cioè quella dove l’utente andava ad inserire i dati; • Algoritmi EDF e RM: insieme di funzioni che lavorano in background (operazioni non visibili all’utente) ed elaborano i dati inseriti dall’utente; • Diagramma di Gantt: insieme di funzioni che visualizzano graficamente all’utente il risultato dell’elaborazione dei dati, da lui inseriti, tramite diagramma di Gantt. Di seguito andrò a descrivere i vari passi che ho eseguito per creare il modulo in ordine temporale di come li ho svolti. 53 Implementazione algoritmi nel linguaggio C Stimolato principalmente dalla curiosità, ho preso la decisione di dedicarmi inizialmente all’implementazione dei due algoritmi di scheduling EDF e RM. Siccome volevo aver subito la conferma della funzionalità corretta degli algoritmi creati, grazie alla conoscenza del linguaggio C, derivata dall’esame di “Fondamenti di Informatica”, e inoltre essendo il PHP un linguaggio avente le istruzioni e la struttura delle variabili molto simile al C, ho sviluppato gli algoritmi nel linguaggio C. Questo mi avrebbe dato modo di ottenere un feedback diretto e rapido di come lavorano gli algoritmi da me creati. Ora vado a descrivere in dettaglio i ragionamenti eseguiti e il codice scritto per implementare i due algoritmi. Algoritmo EDF Inizialmente ho incluso nel mio file C la libreria di input/output del C, grazie al comando #include, e ho definito due costanti N e M che contengono rispettivamente il numero massimo dei task e il numero massimo dei parametri di ogni task, grazie al comando #define. La costante N=10 perché dalle specifiche abbiamo detto che il numero massimo di task che l’utente può inserire è 10; la costante M=5 perché i parametri che prendo in considerazione per ogni task sono il tempo di computazione C, la dealine relativa D, il periodo T, il numero di computazioni fatte dal task e un flag che verifica se il task è presente o no nella ready queue. Dopo questi passaggi necessari ho iniziato l’algoritmo vero e proprio grazie all’istruzione main. In tutti i programmi C prima di utilizzare una variabile bisogna definirla ed infatti è questa la prima cosa che sono andato a fare. Ho definito dieci variabili intere (int), numero minimo necessario per implementare l’algoritmo; queste sono: • num_task: contiene il numero dei task da elaborare, inserito dall’utente; • task[N][M]: è un array bidimensionale (detto anche matrice) dove in ogni riga verranno memorizzati i parametri di un task inseriti dall’utente (riga i-esima contiene tutti e solo i paramentri del task i-esimo), una volta inseriti dall’utente essi non variano più fino al termine dell’algoritmo; • ready_queue[N][M]: è un array bidimensionale dove in ogni riga si andranno a memorrizzare i parametri di un task durante l’elaborazione dell’algoritmo, essi variano ad ogni iterazione dell’algoritmo; • secondi: variabile contatore dei secondi che passano dall’inizio dell’algoritmo fino al termine di esso, ad ogni secondo ho un iterazione; • i, j: contatori che utilizzo per passare da un task all’altro e/o da un parametro all’altro nei vari cicli for; • min_task: conterrà il numero del task avente la deadline assoluta minore; 54 • • • min_dead: conterrà il valore della deadline assoluta minore; overflow: la utilizzo in modalità booleana per decidere se fermare o no le iterazioni fatte secondo per secondo; invio: mi serve solo a bloccare la visualizzazione dei risultati prodotti fino a quando non premo un tasto, una volta premuto il tasto procede con l’iterazione successiva. Prima di iniziare con l’algoritmo vero e proprio devo richiedere all’utente il valore del numero dei task e i valori dei parametri dei task grazie a due cicli for e al comando scanf; come detto prima il numero dei task verrà memorizzato nella variabile num_task, mentre l’array task[i][j] memorizzerà il parametro j-esimo appartenente al task i-esimo, per j=0 il parametro è il tempo di computazione C, per j=1 il parametro è la deadline relativa D e per j=2 il parametro è il periodo T. Gli ultimi due parametri j=3 e j=4, rispettivamente il numero di computazioni di un task e un flag ready (può assumere valore 0 o 1) per vedere se un determinato task è presente nella ready queue, inizialmente sono fissati a priori ed hanno un valore ben preciso; il numero di computazioni è 0, dato che nessuno di loro ha mai computato inizialmente (ipotesi: nessuna computazione precedente da parte di un task), mentre il flag ready è invece 1, dato che con 0 vado ad indicare i task che non sono presenti nella ready queue ed invece con 1 i task che sono presenti (per ipotesi: inizialmente tutti i task sono presenti nella ready queue, caso critico). In questi passaggi vado a costruire l’intero array task con i dati inseriti dall’utente e i valori iniziali fissi dati dalle ipotesi fatte. Inizializzo i valori delle variabili secondi e overlflow uguali a 0, per la prima variabile perché l’algoritmo parte dall’istante 0 secondi, la seconda variabile invece è un flag (può assumere valore 0 o 1) e quando è uguale a 0 indica che nessun task è in overflow mentre quando è 1 indica che uno o più task sono in overflow. Questo codice visualizzerà ad esempio: 55 Siccome c’è anche la possibilità che tutti i task inseriti dall’utente finiscano le loro computazioni prima dell’inizio del loro prossimo periodo, quindi la CPU resta inutilizzata per alcuni secondi; bisogna tenere conto anche di questo caso. Quindi nell’array task inserisco anche un indice i-esimo aggiuntivo a quelle dei task che mi tiene conto dei tempi morti della CPU (è come un task invisibile); questo indice per non essere mai selezionato dall’algoritmo deve avere un tempo di computazione, una deadline relativa e un periodo molto maggiore rispetto ai task e, inoltre, per ipotesi deve avere il numero di computazioni=0 ed essere sempre presente nella ready queue quindi flag ready=1. Siccome gli elementi di un array partono dall’indice 0 (i=0 corrisponde al task1, i=1 al task2,…) l’ultimo task inserito dall’utente corrisponde all’indice num_task-1, quindi l’indice che avrà il “task per i tempi morti” sarà num_task. L’ultima cosa di cui ho bisogno prima di partire con l’elaborazione dell’algoritmo è la creazione della ready queue. Questa viene fatta grazie all’array ready_queue, il quale ha la stessa struttura dell’array task. Inizialmente ha anche gli stessi valori dell’array task, quindi attraverso due cicli for copio tutti i valori che contiene task in ready_queue. La differenza tra questi due array si vede durante l’elaborazione dell’algoritmo: task: i valori dei parametri inseriti dall’utente non variano mai durante l’elaborazione e restano sempre gli stessi fino alla fine, array statico; ready_queue: i valori dei parametri, inizialmente uguali a quelli di task, variano ad ogni iterazione, array dinamico. Per verificare lo stato iniziale della ready queue prima di iniziare l’algoritmo stampo a schermo i parametri di ready_queue grazie a due cicli for e al comando printf, questo lo faccio per avere un riepilogo dei dati inseriti dall’utente e per verificare che la procedura dell’inserimento di tutti i dati si è svolta correttamente. 56 Questo codice visualizzerà ad esempio: Adesso posso iniziare a sviluppare l’algoritmo. L’algoritmo di scheduling ha come compito quello di selezionare il task con priorità maggiore ed assegnargli la CPU. Questo controllo ed assegnazione deve essere fatto per ogni secondo, quindi deve essere ripetuto (iterato) continuamente; esso termina solo quando uno o più task vanno in overflow. Quindi ho deciso di iniziare l’algoritmo con un ciclo while, esso itera l’algoritmo (tutte le istruzioni contenute dal ciclo while) secondo per secondo (secondi++) e termina quando il flag overflow è diverso da 0. Si vedrà dopo che questo flag viene messo a 1 quando uno o più task vanno in overflow. Sapendo che l’algoritmo si ripete ciclicamente, la prima operazione che faccio è quella di resettare le variabili min_dead e min_task dai valori assunti dal ciclo precedente. Questo perché il controllo sulla deadline assoluta d viene fatto secondo per secondo e, se ad esempio, min_dead conteneva al secondo precedente un valore di deadline piccolo appartenente ad un task che ha finito di computare e non resetto questo valore, al prossimo controllo anche se il task non sarà più presente nella ready queue i valori di min_dead e min_task resteranno quelli del ciclo precedente (essendo appartenuti ad un task con priorità maggiore). Ho deciso di resettare queste due variabili inizializzandole in modo tale che assumano i valori della deadline e della posizione nell’array ready_queue del “task dei tempi morti”, sia perché la sua deadline ha un valore elevato e quindi ai controlli qualsiasi task presente nella ready queue avrà una priorità maggiore di esso (ecco perché ho posto min_dead=ready_queue[num_task][1]) e sia perché se eventualmente non sono presenti task nella ready queue automaticamente il task selezionato dall’algoritmo è il nostro “task invisibile” (ecco perché ho posto min_task=num_task). 57 Ora tocca alla parte di controllo, selezione e assegnazione del task avente priorità maggiore tra tutti i task presenti nella ready queue, cioè quella che riguarda direttamente lo scheduling di un task-set. Ricordiamo a questo punto che l’algoritmo EDF dà priorità maggiore, cioè seleziona, il task avente deadline assoluta d minore (deadline dinamica, varia da secondo a secondo quindi ad ogni iterazione). Per selezione di un task intendo, nel mio algoritmo, la memorizzazione del valore della deadline assoluta minore nella variabile min_dead=ready_queue[i][1] e la memorizzazione della posizione occupata dal task, avente deadline minore, nella ready queue nella variabile min_task=i. La prima scansione mi seleziona il primo task presente nella ready queue avente un qualsiasi valore della deadline assoluta (mi interessa solo se è presente nella ready queue, ready_queue[i][4]==1), infatti appena ne trova uno il ciclo for termina grazie all’istruzione break. La seconda scansione parte dal task selezionato in precedenza e continua a controllare tutti i task presenti nella ready queue fino alla fine. In questa scansione viene selezionato il task avente deadline assoluta minore rispetto a tutti gli altri task presenti nella ready queue. Grazie a delle printf stampo a schermo il risultato delle scansioni precedenti; esse visualizzano il secondo di tempo in cui ci troviamo, la posizione dell’array in cui si trova il task selezionato e il valore della deadline assoluta di questo task. Questo codice visualizzerà ad esempio: Dopo aver scelto il task a cui deve essere assegnata la CPU devo passare alla fase di computazione, cioè, oltre a fare in modo che il task selezionato computi per quel secondo (il tempo di computazione diminuisce), devo aggiornare lo stato temporale di tutti i task presenti nella ready queue (la deadline assoluta diminuisce, il tempo per arrivare a fine periodo diminuisce). Quindi per ogni task diminuisco di 1 (perché itero secondo per secondo) il periodo assoluto e la deadline assoluta, mentre solo per il task selezionato diminuisco di 1 il tempo di computazione 58 assoluto. E infine aumento di 1 la variabile secondi (per indicare il passare del tempo ad ogni iterazione). Ora grazie ancora a delle printf e a due cicli for (ne servono due per scorrere un array bidimensionale) stampo a schermo la situazione di tutti i task (presenti e non nella ready queue) con i valori dinamici assunti dai parametri in quel preciso secondo. Ogni riga è attribuita ad un singolo task e i parametri sono, in ordine di visualizzazione: tempo di computazione assoluto, deadline assoluta, periodo assoluto, numero di computazioni, presenza o no nella ready queue. Questo codice visualizzerà ad esempio: L’ultima cosa da fare prima di poter terminare l’algoritmo e quindi chiudere il ciclo while è la gestione della ready queue. Infatti non dobbiamo solo aggiornare i parametri dei task secondo per secondo ma anche la situazione della ready queue e più precisamente col passare dei secondi possono verificarsi tre casi: la fine del tempo di computazione di un task: sta ad indicare che dobbiamo togliere il task dalla ready queue; l’inizio di un nuovo periodo di un task: sta ad indicare che dobbiamo re-inserire il task nella ready queue; l’andata in overflow di uno o più task: sta ad indicare che dobbiamo terminare l’algoritmo. Con un controllo if verifico se il task che ha computato in questo secondo ha finito totalmente il suo tempo di computazione assoluto (cioè se è uguale a 0). Se si verifica questo fatto allora devo incrementare di 1 il parametro che conteggia il numero di computazioni di quel task e devo togliere il task dalla ready queue (ready_queue[min_task][4] da 1 a 0) a cui non serve più la CPU. Infine con una printf visualizzo il task che ha finito la computazione in quel secondo, ovviamente se in quell’istante di tempo vi è un task che ha terminato. 59 Questo codice visualizzerà ad esempio: Con un ciclo for scansiono tutta la ready queue e mi concentro (grazie all’istruzione if) sui task che hanno finito la computazione, che hanno terminato il loro periodo assoluto e che non sono presenti nella ready queue e sui task che non hanno finito la computazione e sono andati in overflow, cioè il loro periodo assoluto è scaduto senza che hanno finito di computare. Nel primo caso reinserisco il task i-esimo nella ready queue, cambiando il parametro ready_queue[i][4] da 0 ad 1 e, siccome tratto i task periodici, riprendendo i valori iniziali del tempo di computazione C, deadline relativa D e periodo T. Poi con una printf visualizzo il task che eventualmente rientra nella ready queue in quell’istante di tempo. Questo codice visualizzerà ad esempio: Nel secondo caso devo bloccare le iterazioni, cioè devo terminare il ciclo while. Questo lo faccio semplicemente cambiando il flag overflow da 0 a 1, così che alla prossima iterazione la condizione del while non sarà vera e tutto il contenuto del ciclo si interrompe, fermando a sua volta l’elaborazione dell’algoritmo. Poi con una printf visualizzo il task che è andato in overflow. Questo codice visualizzerà ad esempio: A titolo di esempio, di seguito verrà mostrato l’intero output prodotto dal file EDF.exe (eseguibile del file EDF.c) con un task-set scelto a caso. 60 61 62 Algoritmo RM Quasi tutti i ragionamenti fatti (e quindi il codice scritto) per l’algoritmo EDF sono validi anche per l’algoritmo RM, quindi non andrò a ripetere e spiegare tutto l’algoritmo RM ma esporrò solo le parti ed i ragionamenti in cui questo si differenzia dal precedente riassumendo brevemente però i concetti generali di come funziona l’algoritmo. Inizialmente vi sono le inclusioni delle librerie e le definizioni delle costanti. Successivamente, con l’inizio del programma (main), vado a dichiarare tutte le variabili che utilizzo nel codice e a richiedere all’utente l’inserimento dei dati (numero dei task e parametri di essi). Qui l’unica differenza dal precedente sta nella dichiarazione di una variabile: mentre prima avevo la variabile min_dead, che conteneva il valore della deadline assoluta minore, ora ho la variabile min_perio, che contiene il valore del periodo relativo minore. Una volta creato totalmente l’array task vado a generare anche l’array ready_queue tramite copia di tutti i parametri dell’array task e infine stampo a schermo l’array ready_queue con tutti i suoi parametri. 63 Questo codice visualizzerà ad esempio: Anche qui l’algoritmo vero e proprio inizia con il ciclo while e il reset delle variabili min_task e min_perio inizializzandole ai valori del “task dei tempi morti”. Ora tocca alla parte di controllo, selezione e assegnazione del task avente priorità maggiore tra tutti i task presenti nella ready queue, cioè quella che riguarda direttamente lo scheduling di un task. Ed è qui che l’algoritmo RM presenta le differenze maggiori rispetto all’algoritmo EDF. Ricordiamo a questo punto che l’algoritmo RM dà priorità maggiore, cioè seleziona, il task avente periodo relativo T minore (periodo statico, non varia da secondo a secondo quindi ad ogni 64 iterazione). Per selezione di un task intendo, nel mio algoritmo, la memorizzazione del valore del periodo relativo minore nella variabile min_perio=ready_queue[i][2] e la memorizzazione della posizione occupata dal task, avente periodo minore, nella ready queue nella variabile min_task=i. La prima scansione mi seleziona il primo task presente nella ready queue avente un qualsiasi valore del periodo relativo (mi interessa solo se è presente nella ready queue, ready_queue[i][4]==1), infatti appena ne trova uno il ciclo for termina grazie all’istruzione break. La seconda scansione parte dal task selezionato in precedenza e continua a controllare tutti i task presenti nella ready queue fino alla fine. In questa scansione viene selezionato il task avente periodo relativo minore rispetto a tutti gli altri task presenti nella ready queue. Grazie a delle printf stampo a schermo il risultato delle scansioni precedenti; esse visualizzano il secondo di tempo in cui ci troviamo, la posizione dell’array in cui si trova il task selezionato e il valore del periodo relativo di questo task. Questo codice visualizzerà ad esempio: La fase di computazione è identica all’algoritmo precedente, cioè il task selezionato computa per quel secondo (il tempo di computazione diminuisce) e devo aggiornare lo stato temporale di tutti i task presenti nella ready queue (la deadline assoluta diminuisce, il tempo per arrivare a fine periodo diminuisce). Quindi per ogni task diminuisco di 1 (perché itero secondo per secondo) il periodo assoluto e la deadline assoluta, mentre solo per il task selezionato diminuisco di 1 il tempo di computazione assoluto. E infine aumento di 1 la variabile secondi (per indicare il passare del tempo ad ogni iterazione). 65 Ora con l’uso dell’istruzione printf e di due cicli for (ne servono due per scorrere un array bidimensionale) stampo a schermo la situazione di tutti i task (presenti e non nella ready queue) con i valori dinamici assunti dai parametri in quel preciso secondo. Ogni riga è attribuita ad un singolo task e i parametri sono, in ordine di visualizzazione: tempo di computazione assoluto, deadline assoluta, periodo assoluto, numero di computazioni, presenza o no nella ready queue. Questo visualizzerà ad esempio: L’ultima cosa da fare prima di poter terminare l’algoritmo e quindi chiudere il ciclo while è la gestione della ready queue. Infatti non dobbiamo solo aggiornare i parametri dei task secondo per secondo ma anche la situazione della ready queue e più precisamente col passare dei secondi possono verificarsi tre casi: la fine del tempo di computazione di un task: sta ad indicare che dobbiamo togliere il task dalla ready queue; l’inizio di un nuovo periodo di un task: sta ad indicare che dobbiamo re-inserire il task nella ready queue; l’andata in overflow di uno o più task: sta ad indicare che dobbiamo terminare l’algoritmo. Questa procedura è identica a quella spiegata per l’algoritmo EDF e quindi non descrivo per non essere ripetitivo. Anche qui l’algoritmo termina (la condizione del while non è soddisfatta) non appena un task va in overflow. Alla fine comunque con l’uso delle printf vado a visualizzare, se eventualmente presente: il task che in quel secondo ha finito di computare; il task che in quel secondo va in overflow. 66 Questo codice visualizzerà ad esempio: A titolo di esempio, di seguito verrà mostrato l’intero output prodotto dal file RM.exe (eseguibile del file RM.c) con un task-set scelto a caso. 67 Testing degli algoritmi Una volta terminati gli algoritmi, sono andato a testare il loro funzionamento utilizzando il metodo seguente: mi sono inventato dei task-set: numero task di cui era composto ciascun task-set e valori dei parametri per ciascun task; ho schedulato a mano ciascun task-set in base al comportamento dei due algoritmi implementati; ho inserito i dati nei programmi C da me creati; ho confrontato i risultati per vedere se gli algoritmi codificati da me erano esatti. Di seguito vado a mostrare un esempio di un task-set schedulato a mano: 68 Grazie a questo metodo ed al fatto che, per come avevo scelto di implementare gli algoritmi (con tante printf che mi visualizzavano come procedevano passo passo gli algoritmi), ottenevo per ogni secondo un feedback di come questi si comportavano sono riuscito in poche prove a capire e correggere alcuni piccoli errori che si erano presentati inizialmente ed a produrre un codice comprensibile (facilitato anche dal nome che ho dato alle variabili e dai tanti commenti messi in ogni riga), corretto e comunque non troppo complesso. Creazione della directory del modulo “algoritmi” Il primo passo da fare per creare un modulo di Drupal è quello di creare una nuova cartella dentro la directory di Drupal che contiene i moduli (percorso: C:\xampplite\htdocs\drupal\sites\all\modules). Questa cartella andrà a contenere tutti i file riguardanti il modulo. Per coerenza, con il mio progetto e quindi con lo scopo del modulo che andavo a creare, ho scelto “algorimi” come nome della cartella, quindi il percorso del mio modulo sarà C:\xampplite\htdocs\drupal\sites\all\modules\algoritmi. 69 I file riguardanti il modulo possono essere di vari tipi: file .info: informa Drupal dell’esistenza del modulo file .module: contiene il codice principale del modulo file .php: contiene altre funzioni che vengono utilizzate dal modulo file .install: viene eseguito la prima volta che un modulo è attivato, e viene utilizzato per eseguire procedure di configurazione, come richiesto dal modulo. Il compito più comune è la creazione di tabelle del database e campi. Il file .install non ha alcuna sintassi speciale, si tratta semplicemente di un file PHP con una diversa estensione. I file .install sono utilizzati anche per effettuare gli aggiornamenti quando è presnte una nuova versione del modulo. file .js (javascript): contiene altre funzioni che vengono utilizzate dal modulo ecc. I principali, quelli che permettono l’esistenza di un modulo Drupal, sono il file .info ed il file .module. Il mio modulo è formato dal file algoritmi.module, algoritmi.info e crea_gantt.php; oltre a questi sono andato a creare una cartella file_immagine che andrà a contenere l’immagine creata e salvata dalla dal file crea_gantt. Creazione del file algoritmi.info La sintassi del file .info è simile al file INI (INItialization, formato standard per i file di configurazione, file di testo con una struttura di base). Il file .info è sostanzialmente un file di testo statico che contiene meta informazioni sul modulo, quindi serve per la dichiarazione dell’esistenza del modulo e per la configurazione del tema. Senza questo file il modulo non si mostrerà nell’elenco dei moduli. L’elemento di base contenuto in un file .info è la proprietà. Ogni proprietà del file di testo è una coppia di due elementi separati da un “segno di uguale = ”, quello a sinistra dell’uguale è il nome della proprietà mentre quello a destra è il valore della proprietà (esempio: nome = valore ). Il nome deve iniziare con un carattere alfabetico, può contenere numeri e caratteri di sottolineatura, ma non trattini, spazi o segni di punteggiatura. Esso verrà utilizzato da Drupal nella formazione di varie funzioni in PHP e quindi ha le stesse limitazioni. Attenzione: non scegliere nomi già utilizzati per altri moduli; come tutti i componenti installati ogni modulo deve avere un nome univoco. 70 Le proprietà possono essere raggruppate in sezioni denominate arbitrariamente. Il nome della sezione appare su una riga a sé, tra due parentesi quadre [ ]. Tutte le proprietà dopo la dichiarazione della sezione sono associate a quella sezione. Non c'è un "esplicito" delimitatore di fine per una sezione; essa termina appena viene dichiarato l’inizio di un’altra o alla fine del file. Le sezioni non possono essere nidificate (esempio: [sezione]). La sezione sarebbe quindi un elenco di valori associati, simile ad un array. Il punto e virgola “ ; ” indica l’inizio di un commento. Esso si prolunga fino alla fine della riga, quindi tutto quello scritto alla destra del punto e virgola fino alla fine della stessa riga è considerato commento. Poiché il file .info è memorizzato nella cache, è necessario cancellare la cache prima che eventuali modifiche vengono visualizzate nel vostro sito. Nel file .info è possibile inoltre specificare quali impostazioni del tema dovrebbero essere accessibili dall'interfaccia di amministrazione di Drupal. Drupal comprende i nomi delle proprietà elencati di seguito ed userà i valori di default per i tasti opzionali non presenti nel file .info: • • • • • • • • • • • • Nome (richiesto) Descrizione (consigliato) Screenshot versione (sconsigliato) core (richiesto) motore (richiesto nella maggior parte dei casi) base tema regioni Dipendenze (opzionale) fogli di stile CSS script php Nome (richiesto) Il nome pubblicato del modulo. Deve seguire lo standard di capitalizzazione di Drupal 6, quindi solamente la prima lettera del testo deve essere scritta in maiuscolo. name = Nome del modulo Descrizione (consigliato) Una descrizione corta del modulo, preferibilmente di una linea, che informa l’amministratore del sito cosa fa il modulo una volta abilitato. description = Descrizione dello scopo del modulo. Screenshot (opzionale, solo per i temi e non per i moduli) L’attributo di screenshot dice a Drupal dove trovare un'immagine di anteprima del tema, utilizzato sulla pagina di selezione del tema (admin/build/themes). Se quest’attributo viene omesso dal file .info, Drupal utilizza il file "screenshot.png" nella directory del tema. Utilizzare questa chiave solo se il file di anteprima non si chiama "screenshot.png" o se si desidera inserire in una directory al di fuori della directory dei temi di base la tua (per esempio, screenshot = immagini/screenshot.png ). screenshot = immagine.png 71 Versione (sconsigliato) La stringa di versione verrà automaticamente aggiunta da drupal.org quando il modulo o tema creato viene immesso in rete. Così si può omettere questo valore. Tuttavia, se il modulo o il tema non viene ospitato sulle infrastrutture drupal.org, si può dare al modulo e tema qualunque versione stringa abbia un senso. version = 1.0 Core (richiesto) Da 6.x in poi, per tutti i file .info per moduli e temi bisogna indicare versione corretta del core. Il nucleo di Drupal si rifiuterà di abilitare o avviare dei moduli o temi dove non vi è scritta esplicitamente la versione corretta del core. Il valore dato all’attributo “core” è confrontato direttamente con la costante DRUPAL_CORE_COMPATIBILITY, se non corrisponde, il modulo o il tema sarà subito disabilitato o non abilitato proprio. core = 6.x Motore (richiesto nella maggior parte dei casi, per i temi) Il motore di tema, che viene utilizzato dal tema. Se non viene fornito, il tema viene considerato stand alone, cioè, implementato con un file ".theme". La maggior parte dei temi dovrebbero usare "phptemplate" come motore predefinito. Il lavoro di PHPTemplate è quello di scoprire tema funzioni e modelli per il comportamento del tema. Si può omettere questa voce solo se si sa cosa si sta facendo. engine = phptemplate Tema base (solo per i temi) Dichiarando un tema di base è possibile avere dei sotto-temi. Questo permette l’ereditarietà di un tema, nel senso che le risorse del "tema base" possono essere riutilizzate all'interno del sotto-tema. Posso dichiarare anche dei sotto-sotto-temi che hanno come tema base un sottotema, questo permette più livelli di ereditarietà. base theme = nome tema base Regioni (solo per i temi) Le regioni blocco a disposizione del tema sono definite specificando la chiave “regions” seguito, tra parentesi quadre, dal nome della posizione dove si andrà a trovare la regione e, a destra dell’uguale, dal nome umano della posizione come valore (esempio, regions[posizione_regione] = Nome della regione). Drupal 6 default regioni: o regions [left] = barra laterale sinistra o regions [right] = barra laterale destra o regions [content] = contenuto o regions [header] = intestazione o regions [footer] = piè di pagina Dipendenze (per i moduli) La dipendenza dei moduli è un opzione extra che può essere inserita nel file .info. Per dipendenza si intende quando un modulo, per essere abilitato, richiede l’abilitazione precedente di uno o più moduli. Questa dipendenza di un modulo nei confronti di altri moduli viene rappresentata con la seguente sintassi: o dependences[] = Nome modulo1 da cui dipendere 72 o dependences[] = Nome moduloN da cui dipendere Se un modulo ha delle dipendenze nei confronti di altri moduli, Drupal non ne permette l’attivazione fino a quando tutti i moduli da cui questo dipende non sono stati abilitati. Fogli di stile Tradizionalmente i temi di default utilizzano style.css automaticamente e si possono aggiungere ulteriori fogli di stile chiamando drupal_add_css( ) nei loro file template.php. A partire da Drupal 6, si può aggiungere ai temi i fogli di stile anche attraverso i loro file .info. stylesheets [all] [] = theStyle.css Script Tradizionalmente si poteva aggiungere ai temi il Javascripts chiamando drupal_add_js( ) nelle loro file template.php. A partire dal 6.x, se un file denominato script.js è presente nella directory dei temi allora è automaticamente inclusa. Tuttavia, in Drupal 7, questo comportamento è stato cambiato nuovamente in modo che script.js è incluso solo se è stato specificato nel file .info: scripts [] = myscript.js Php Questo definisce la minima versione PHP che il modulo o il tema sosterrà. Il valore di default è derivato dalla costante DRUPAL_MINIMUM_PHP, che è la versione minima richiesta per il resto del nucleo. Questo può essere ridefinito per una versione più recente, se necessario. php = 4.3.3 Dopo questa parentesi generale su come può essere costituito il file .info, vado a spiegare come ho creato il mio file algoritmi.info. La prima riga è costituita da un commento (c’è il ; ) dove ho scritto il nome del modulo con la relativa estensione, la versione del modulo e la data di creazione del file .info. Le altre tre righe sono gli attributi obbligatori e principali di un file .info ovvero il nome del modulo, la descrizione del modulo e la versione del core per cui è stato scritto il modulo. Il nome e la descrizione verranno visualizzati nella pagina di abilitazione moduli di Drupal (http://localhost/drupal/admin/build/modules). 73 Creazione del file algoritmi lgoritmi.module Dopo aver letto la documentazione, presente sul sito di Drupal, sono riuscito a comprendere le nozioni base ed i passi standard su come creare un modulo. Gancio hook_help Il primo passo da fare è l’implementazione l del gancio hook_help( _help( ). ) Grazie a questo vengono offerti aiuto ed informazioni addizionali circa il nostro modulo. A causa del file .info l’utilizzo di questo gancio è opzionale, comunque è buona norma renderlo effettivo. Per rendere effettivo qualsiasi gancio in Drupal bisogna sostituire “hook” sul nome del gancio con il nome del nostro modulo e creare la funzione avente come nome nome modulo_nomegancio( ) sul file .module. Nel mio caso ho chiamato to la funzione algoritmi_help( ). Il parametro $path contiene il percorso del menù del router, come definito in hook_menu, per l’aiuto che viene richiesto (ad esempio: admin/nodo); se il percorso router include un carattere jolly%, questo verrà visualizzato in $path (ad esempio: le pagine nodo sarebbero sarebbe pari a $path 'node/%'). L'implementazione gancio può anche essere chiamato con i descrittori speciali dopo un # 74 "segno" e in questo caso il modo consigliato per processare la variabile $path è attraverso un’istruzione per la scelta multipla chiamata switch-case. Il parametro $arg è un array che corrisponde al valore di ritorno della funzione arg( ), per i moduli che vogliono fornire un aiuto che è specifico a determinati valori di caratteri jolly in $path. Ad esempio, è possibile fornire un aiuto per il percorso 'user /1', cercando per il percorso di 'user /%' e $arg [1] == '1 '. Questo array deve essere sempre utilizzato, piuttosto che richiamando direttamente arg( ), perché l'implementazione del suo gancio può essere chiamata per altri scopi oltre a quello di costruire questa pagina di aiuto. Si noti che a seconda di quale modulo si invoca hook_help, $arg può contenere solo stringhe vuote; indipendentemente da ciò, da $arg[0] a $ arg[11] sarà sempre impostato. Inizialmente dichiaro una variabile con il nome $output che andrà a contenere il mio output, questa variabile grazie ad = ‘ ’ la dichiaro in modo tale che sia di tipo stringa. Poi gestisco la variabile $path con lo switch-case; in generale prevedo solo il caso admin/help#algoritmi (in generale: admin/help#nomemodulo) che è usato dal core di Drupal per collegarsi dalla pagina principale di aiuto (/admin/help), dove sono visualizzati tutti gli aiuti generali (soprattutto quelli del core), alla pagina specifica di aiuto del mio modulo, dove sono visualizzati solo gli aiuti riguardanti il mio modulo. Successivamente memorizzo nella variabile stringa $output il testo che contiene il messaggio d’aiuto rivolto all’utente (<p> e </p> sono le tag che indicano l’inizio e la fine del paragrafo, mentre in t( ) è contenuto il testo che sarà mostrato all’utente). Con l’istruzione break termino lo switch-case. Il valore di ritorno della funzione (return $output) è la variabile $output, cioè una stringa localizzata contenente il testo d’aiuto visualizzabile dall’utente. 75 Gancio hook_menu Dopo aver agganciato l’hook_help al mio modulo ho implementato l’hook_menu. Esso permette di definire le voci di menù e di callback (chiamate di ritorno) di pagina; cioè di creare il link per il mio modulo e il conseguente collegamento URL. Questo con lo scopo di agganciare i moduli per registrare i percorsi, che determinano quali richieste sono da trattare. A seconda del tipo di iscrizione richiesta da ciascun percorso, un link si trova nel blocco di navigazione e/o un elemento appare nella pagina di amministrazione di menù (admin/menu). Ogni item (indice dell’array $items) definisce un URL (o percorso, path) per il sito di Drupal. Drupal chiamerà questo gancio due volte: una con $may_cache impostato su TRUE, e una volta con esso impostato su FALSE. Pertanto, ogni voce di menù dovrebbe essere registrata quando $may_cache è VERO o FALSO, non in entrambe le volte. Impostazione di una voce di menù che si verifica per entrambe le due volte si tradurrà in un comportamento non specificato. Questo gancio è anche un buon posto per inserire il codice che dovrebbe essere eseguito una sola volta per ogni pagina visualizzata; mettetela in un if(!may_cache) blocco. $may_cache è un valore booleano che indica se le voci di menù cacheable devono essere restituite. La cache menù è per il singolo utente, per cui gli elementi possono essere memorizzati nella cache fintanto che non dipendono dall'attuale posizione dell'utente. Il valore di ritorno di questa funzione è una serie di voci (array con i vari indici) di menù. Ogni elemento (o voce, item) di menu è un array $items[ ] associativo che può contenere le seguenti coppie nome-valore: • path (percorso): obbligatorio. Il percorso per il collegamento quando l'utente seleziona l'elemento. • title (titolo): obbligatorio. La traduzione del titolo della voce di menù. • callback (chiamata di ritorno): La funzione da chiamare per visualizzare una pagina web quando l'utente visita il percorso. Se omesso, sarà richiamato ed utilizzato l’elemento parent (padre) del menù. • callback arguments (argomenti di callback): una serie di argomenti da passare alla funzione di callback. • access (accesso): un valore booleano che determina se l'utente dispone di diritti di accesso a questa voce di menù. Di solito determinata da una chiamata a user_access( ). Se la ometto e anche callback è assente, verranno utilizzati i diritti di accesso degli elementi parent (padre) di menù. • weight (peso): un numero intero che determina la posizione relativa degli elementi del menù; di default è 0. Nel dubbio, si lasci stare, quella predefinita ordine alfabetico di solito è meglio. • type (tipo): una maschera di bit di flag che descrivono le proprietà degli elementi di menù; maschere di bit di scelta rapida sono forniti come costanti nel menu.inc e le principali sono: MENU_NORMAL_ITEM: voci di menù normale mostrate nella struttura dei menù e può essere spostato o nascosto da parte dell'amministratore. MENU_ITEM_GROUPING: i raggruppamenti delle voci sono usati per le pagine come "node/add" che semplicemente è un elenco delle sottopagine da visitare. MENU_CALLBACK: nelle callback viene semplicemente registrato un percorso in modo che il corretto funzionamento viene attivato quando si accede all’URL. MENU_DYNAMIC_ITEM: voci di menù dinamiche che cambiano di frequente, e quindi non devono essere conservate nella banca dati per la personalizzazione amministrativa. MENU_SUGGESTED_ITEM: i moduli possono "suggerire" le voci di menù che l'amministratore può abilitare. MENU_LOCAL_TASK: task locali vengono visualizzati come schede di default. 76 MENU_DEFAULT_LOCAL_TASK: ogni task-set locale dovrebbe fornire un task di default, che collega allo stesso percorso del suo task padre quando esso viene cliccato. Se il tasto type è omesso, si assume MENU_NORMAL_ITEM per default. Dopo aver spiegato anche qui i concetti generali del gancio hook_menu ora vado a descrivere in che modo ho sviluppato la mia funzione hook_menu. Innanzitutto ho reso effettivo il mio gancio dandogli il nome algoritmi_menu( ); questa funzione permette di mostrare (nel frame a sinistra di Drupal) all’utente il link del modulo Algoritmi e, una volta cliccato su di esso, apre la pagina vera e propria che dovrà contenere il contenuto del mio modulo. Fatto questo vado a definire una variabile $items (elementi o voci di menù) come tipo array; ogni indice dell’array corrisponde ad una pagina del modulo, quindi per aggiungere più pagine al modulo basta aggiungere più item (indici dell’array) all’array $items. Definisco per l’array $items l’indice ‘algoritmi’, cioè vado a definire l’URL del modulo algoritmi quindi vado a creare il link “Algoritmi Scheduling”. Le coppie nome-valore di questo array sono: Il titolo: cioè il nome del link; La descrizione: cioè la frase che verrà visualizzata all’utente quando esso posiziona il puntatore sul link; Il nome (quindi il tipo) della funzione di callback da richiamare una volta cliccato sul link: in questo caso chiamo una funzione che mi genera una form (drupal_get_form); I parametri che devono essere passati alla funzione di callback, i campi callback e callback arguments assieme sono quelli che mi generano la pagina che sarà visualizzata all’utente una volta cliccato sul link: in questo caso chiamo la funzione algoritmi_form la quale conterrà le istruzioni che mi andranno a generare la pagina; I permessi di accesso alla pagina che sarà visualizzata dopo aver cliccato sul link: in questo caso, rispettando le specifiche, ho permesso l’accesso alla pagina a tutti i tipi di utenti (dall’amministratore agli utenti non registrati); La posizione e il percorso da compiere per trovare il link appena creato: in questo caso ho fatto in modo che, una volta abilitato il modulo, il link sia visibile nella pagina del menù principale di navigazione di Drupal visibile quindi nel frame a sinistra. Questa funzione ritorna l’array $items[ ] con l’unico indice che ho stabilito per esso, cioè ‘algoritmi’. Quindi verrà visualizzato un unico link con le caratteristiche descritte in precedenza. Se qualche utente visita questo URL (http://localhost/drupal/algoritmi) la funzione drupal_get_form, con i parametri passati dalla ‘algoritmi_form’, viene usata per generare il contenuto della pagina. 77 La funzione drupal_get_form è la funzione “chiave” nel form API che si occupa di recuperare, processare e mostrare automaticamente un form elaborata HTML per i moduli. moduli Nel suo uso di base ci vuole solo un argomento, uno string che sia l’ID del modulo e anche il nome della funzione che costruisce l’array $form. $form Poiché il modulo di identificazione è generalmente anche il nome di una funzione, seguita da un numero qualsiasi di lettere, numeri o caratteri di sottolineatura, spazi e trattini non sono ammessi. La funzione drupal_get_form può prendere argomenti opzionali aggiuntivi, untivi, che saranno semplicemente trasferiti alla funzione costruttrice $form. La funzione drupal_get_form fa principalmente quanto segue: Comincia il processo intero di costruzione del form ottenendo l’array $form dalla funzione costruttrice; Traduce i nomi degli item di $form[‘nome’] in elementi attuali del form; Esegue qualsiasi validazione, cioè chiama le funzioni di validazione personalizzate, se esse sono state dichiarate; Invia il form, se una funzione di invio (submit) è stata dichiarata e se l’utente ha cliccato sul tasto di submit; Chiama qualsiasi funzione personalizzata di temi che sono stati dichiarati; Restituisce uno string HTML che contiene il form attuale. Gancio hook_form Dopo aver agganciato al file .module le funzioni di help e di menù ho iniziato a creare le funzioni che generano il form di inserimento dati, che verrà visualizzato all’utente una volta cliccato sul link Algoritmi. La prima funzione da analizzare è quella che ha come gancio hook_form. hook_form Questo gancio permette di visualizzare un modulo nodo di modifica, cioè un form dove l’utente può inserire dei 78 dati. Esso è realizzato con moduli nodo ed è chiamato a recuperare il form che viene visualizzato per creare o modificare un nodo. Questo modulo viene visualizzato nel nodo di percorso /add /[tipo di nodo] o nodo/[nodo ID]/modificare. La presentazione e pulsanti di anteprima, i controlli amministrativi e display, e le sezioni aggiunte da altri moduli (ad esempio, le impostazioni del percorso, le impostazioni del menù, impostazioni commenti, ecc) vengono visualizzati automaticamente dal modulo di nodo. Questo gancio ha solo bisogno di tornare il titolo del nodo e la modifica dei campi specifici di form per il tipo di nodo. I parametri da immettere nell’hook_form sono: &$node: il nodo che viene aggiunto o modificato. $form_state: l’array di stato form. Il valore di ritorno dell’hook_form è un array $form che contiene il titolo e le informazioni degli eventuali elementi che costituiscono il form. I vari elementi del form sono dichiarati in modo array (hanno una struttura gerarchica e possono essere anche nidificati), cioè ogni indice dell’array $form[ ] si riferisce ad un singolo elemento del form e per ognuno di questi ne sono memorizzate le caratteristiche (tipo form, nome form, ecc.). Le proprietà e gli attributi di ciascun elemento del form sono indicate con coppie di chiavi-valori, dove la chiave è il nome della proprietà e/o attributo mentre il valore è il valore della proprietà e/o attributo. La proprietà del nome dell’elemento del form viene dichiarato nell’array $form, alla fine dell’albero dell’array. Ad esempio se un elemento dell’albero è stato strutturato in questo modo: $form[‘account_setting’][‘username’]. Quindi la proprietà del nome di un elemento è ‘username’ e questa è la chiave che sarà disponibile in $form_state[‘values’], utilizzabile nelle funzioni successive come ad esempio quelle di validazione e di elaborazione. Le chiavi di proprietà e/o attributi sono dichiarate con quotazioni circostanti, cominciando con un segno # ed i valori di esse sono ‘stringhe’. Il tipo di elemento del form è dichiarato come un attributo con la proprietà ‘#type’. L’ordine in cui vengono dichiarate le proprietà e attributi di ogni elemento dell’array non importa, e tutti gli attributi di cui esso non ha bisogno non devono essere dichiarati. Molte proprietà e attributi hanno anche un valore predefinito (di default), che viene preso in considerazione quando esse non sono dichiarate esplicitamente. Non utilizzare l’attributo ‘#value’ per nessun elemento dell’array che possa essere modificato dall’utente, utilizzare invece l’attributo ‘#default_value’. Inoltre non collocare dei valori da $form_state[‘values’] (o da $_POST), perché di questo se ne occupa automaticamente FormsAPI, collocare solo il valore originale del campo. Uno dei grandi vantaggi di questo sistema è che le chiavi nominate esplicitamente rendono molto più semplice la decifrazione dell’elemento del form. Le versioni precedenti di FormAPI usavano una combinazione di $form_values, variabili globali, e flag personalizzate nella definizione del modulo stesso per acquisire informazioni sul flusso di lavoro del modulo ed il suo stato attuale durante la lavorazione. In Drupal 6, un solo array ($form_state) è passato per riferimento (grazie al simbolo &) in ogni fase di lavorazione del form. Diversi indici standard di $form_state sono utilizzati in FormAPI come valori di default: • $form_state ['values'] L’array $_POST è utilizzato per raccogliere i valori dei dati, inseriti dagli utenti nei form, inviati con method=‘post’; le informazioni inviati da un form con il metodo POST è 79 invisibile agli altri e non ha limiti sulla quantità di informazioni da inviare. La chiave 'values' viene utilizzata per memorizzare questa raccolta di dati; inoltre essa sostituisce la vecchia distinta variabile $form_values che è stata passata ai gestori (handler) di validazione e di sottomissione. Le seguenti tre variabili con indici standard possono essere utilizzate per controllare l’interpretazione e l’elaborazione del flusso di lavoro del form. I gestori di validazione e di sottomissione possono modificare i dati in queste tre chiavi per alterare il flusso di lavoro del form basato su input dell'utente. • $form_state ['redirect'] La chiave 'redirect' (rifare) controlla che cosa succede dopo che l'elaborazione di un form è completa. Per impostazione predefinita ‘redirect’, quando viene ricaricata la pagina contenente il form al termine di una sottomissione, i campi di questo, riempiti in precedenza dall’utente, vengono cancellati (clear, cioè tornano vuoti). Se 'redirect' è impostato su un percorso di Drupal (come user/edit), l'utente sarà reindirizzato a questa strada, invece. Se 'redirect' è impostata su FALSE, l'utente non verrà reindirizzato dopo che il form viene elaborato, i valori sono entrati nel form e rimarranno nei campi. • $form_state ['rebuild'] La chiave 'rebuild' (ricostruzione) sovrascrive la chiave 'redirect': quando è impostata su TRUE, il form sarà ricostruito da zero e visualizzato sullo schermo. Questo dà alla costruzione del codice del form la possibilità di aggiungere altri campi o modificare la struttura del form in base all’input dell’utente (ad esempio, ri-costruire il form con i campi aggiuntivi se l'utente fa clic su 'Aggiungi task'). Se questo flag è impostato da un gestore di validazione, ogni gestore di 'submit' sarà saltato. Se è impostato da un gestore di 'submit', il form sarà ricostruito e visualizzato dopo che tutti i gestori di submit hanno terminato l'elaborazione. • $form_state ['storage'] Quando si costruiscono form complessi, che richiedono più passaggi per la realizzazione (per esempio, un sondaggio di tre pagine), è necessario preservare i dati da tutti i passaggi in modo che possano essere trattati insieme alla fine. Tutti i dati immessi nel ‘bidone della conservazione’ della collezione $form_state verranno automaticamente memorizzati nella cache e ri-caricati quando il form viene presentato la prossima volta, permettendo al vostro codice di accumulare dati di gradino in gradino fino alla fase finale del processo senza alcun codice aggiuntivo . Gli sviluppatori che desiderano un maggiore controllo possono utilizzare i loro meccanismi di caching per memorizzare i propri dati temporanei del form (la sessione utente - session - e campi form nascosti – hidding - sono due alternative a questo), ma il bidone di 'conservazione' è gestito automaticamente da FormAPI. Si noti che se $form_state ['storage'] è popolata, $form_state ['rebuild'] viene automaticamente impostato su TRUE. • • • $form_state ['submitted'] $form_state ['submit_handlers'] (gestore di sottomissione) $form_state ['validate_handlers'] (gestore di validazione) Queste tre chiavi memorizzano le informazioni sullo stato attuale di trasformazione del form. Se 'submitted' è TRUE, l'input dell'utente è attualmente in fase di elaborazione. Le chiavi 'submit_handlers' e 'validate_handlers' permettono di personalizzare i gestori di validazione e di sottomissione che sono stati allegati al pulsante specifico cliccato dall'utente. 80 • $form_state ['clicked_button'] Una copia completa dell'elemento bottone su cui è stato fatto clic per inviare il form. Questo è più affidabile rispetto al vecchio nome $form_values['op'], e svolge anche ogni ulteriore informazione che è stata posta nella definizione del form che costituisce l'elemento bottone. Gestori di validazione e di sottomissione possono inserire dati aggiuntivi nei raccoglitori personalizzati in $form_state. Il gancio hook_form mi è servito per generare il form contenente i vari riquadri dei task dove l’utente può inserire i dati (tempo di computazione, deadline e periodo), aggiungere e/o rimuovere un riquadro, scegliere l’algoritmo da eseguire e ovviamente inviare il tutto al server perché possa essere elaborato. Come detto in precedenza, anche qui ho reso effettivo il gancio hook_form chiamandolo algoritmi_form( ). Dalle specifiche appare scontata ed essenziale (inizialmente bisogna visualizzare un solo task e deve esserci almeno un task per poter sottomettere il form) la presenza iniziale del primo riquadro, riferito al task1, sulla pagina contenente il form. Questo mi ha portato a dover trattare il primo task, o task1, in maniera separata dagli eventuali task successivi. E, sempre dalle specifiche, per poter realizzare i riquadri con i nomi dei rispettivi task, che al click su di essi si chiudono e si aprono a tendina, ho avuto la necessità prima di definire il riquadro con il nome del task i-esimo e poi ad ogni riquadro di definire i vari campi di inserimento dati relativi al task i-esimo. In parole povere il nome del task fungerà da etichetta per il riquadro, contenente i campi per l’inserimento dei dati, che potrà mostrarsi e nascondersi all’utente grazie ad un click sull’etichetta. 81 Per il task1: Creo nella variabile $form, dandogli come indice ‘task1’ cioè il nome del task $form[‘task1’], un array il quale avrà come attributi: o Tipo: il tipo dell’array sarà campo di testo; o Titolo: il testo, posto all’interno di t( ), che sarà visualizzato come nome del riquadro; o Collapsible (pieghevole): impostato su TRUE renderà il riquadro pieghevole al click sulla sua etichetta; o Collapsed (collassato): impostato su FALSE inizialmente renderà il riquadro non collassato e lo mostrerà per intero, cioè non compresso alla sola etichetta; Creo nella variabile $form, dandogli come indici ‘task1’ il nome del task e ‘comp1’ tempo di computazione del task o ‘dead1’ deadline del task o ‘perio1’ periodo del task, degli array bidimensionali $form[‘task1’][‘comp1’], $form[‘task1’][‘dead1’] e $form[‘task1’][‘perio1’] i quali definiscono i campi del form dove l’utente andrà ad inserire i parametri del task1. Gli attributi di questi tre array sono identici, tranne ovviamente il testo scritto nel titolo e il secondo indice della variabile $form_state[‘values’][‘comp1’ o ‘dead1’ o ‘perio1’] che sarà relativo al campo che si prende in considerazione, e sono: o Tipo: il tipo dell’array sarà campo di testo; o Titolo: il testo, posto all’interno di t( ), sarà visualizzato come nome del campo; o Descrizione: il testo, posto all’interno di t( ), sarà visualizzato come descrizione sotto al campo; o Taglia: la dimensione dei caratteri di cui sarà composto il campo di testo dove l’utente può inserire i dati; o Lunghezza massima: il numero dei caratteri che potrà inserire l’utente nel campo di testo. o Valore di default: il valore di default, modificabile dall’utente, che verrà visualizzato nel campo ogni volta che verrà ricaricato il form (e non la pagina!!!); il ricaricamento del form verrà fatto ogni volta si cliccherà sui bottoni “Aggiungi task” e “Rimuovi task”, che spiegherò successivamente. Il valore assegnato al valore di default è frutto di alcune operazioni precise, le quali sono molto vantaggiose perchè fanno risparmiare all’utente reinserimenti ripetitivi quindi tempo. In pratica vengono impostati come valori di default i dati, eventualmente inseriti in precedenza (prima che il form viene ricaricato) dall’utente; quindi l’utente non dovrà reinserirli al ricaricamento del form perché essi sono già presenti. Di seguito analizziamo le operazioni che vengono fatte per determinare il valore di default: 82 Per prima cosa viene controllata, grazie all’istruzione !empty, che la variabile $form_state[‘values’][‘comp1’ o ‘dead1’ o ‘perio1’], nella quale eventualmente è stato inviato il relativo dato inserito dall’utente nel rispettivo campo prima di aver cliccato su un bottone di sottomissione (nel mio caso sono Aggiungi task, Rimuovi task e Schedula) NON sia vuota (il non è perché davanti ad empty c’è il “ ! ”); Grazie poi all’operatore “ ? ”: • se questa variabile non è vuota, cioè contiene un valore inserito in precedenza dall’utente in quel preciso campo, viene assunto come valore di default il valore precedente (primo valore dopo i “ : ”), contenuto nella variabile $form_state[‘values’][‘…’]; • se questa variabile è vuota, cioè l’utente in precedenza non ha inserito nessun valore in quel preciso campo, viene assunto come valore di default il valore di stringa vuota, cioè “ ” (secondo valore dopo i “:”). Nota: come nome degli indici dell’array $form ho utilizzato dei nomi semplici e significativi che si rifanno agli eventuali valori che andranno a contenere, in modo tale che anche leggendo il codice si riesce a capire facilmente cosa essi trattano e cosa andranno a contenere. Cioè l’indice ‘task1’ vuol dire che i valori contenuti riguardano il task1, l’indice ‘comp1’ (abbreviativo di computazione) indica che il valore contenuto riguarda il tempo di computazione del task1, l’indice ‘dead1’ (abbreviativo di deadline) indica che il valore contenuto riguarda la deadline del task1 e l’indice ‘perio1’ (abbreviativo di periodo) indica che il valore contenuto riguarda il periodo del task1. 83 Per i task successivi al primo (vanno dal task2 al task10): Prima di stampare a schermo i riquadri riguardanti i task dal secondo al decimo vado a fare un controllo sulla variabile $form_state[‘storage’][‘num_task’], nella quale viene memorizzato il numero dei task che l’utente sceglie. $form_state[‘storage’] è una variabile standard di Drupal 6 che mantiene memorizzati nella cache i dati inseriti in essa, quindi ad ogni ricaricamento del form le informazioni che contiene vengono conservate e non vengono perse. Il controllo fatto, con l’istruzione if, su di essa verifica che non sia vuota (!empty): Se è vuota restituisce FALSE: vuol dire che il form è stato caricato per la prima volta, cioè alla variabile $form_state[‘storage’][‘num_task’] non gli è stato assegnato ancora nessun valore e perciò è vuota; quindi il blocco di istruzioni situato dentro all’if non viene eseguito e viene mostrato solo il primo task (il quale è stato definito prima dell’if). Se non è vuota restituisce TRUE: vuol dire che il form è già stato ricaricato una o più volte, cioè alla variabile $form_state[‘storage’][‘num_task’] gli è stato assegnato già un valore; quindi il blocco di istruzioni situato dentro all’if viene eseguito e vengono mostrati tanti task quant’è il valore presente nella variabile. Nota: il valore vuoto può essere espresso in diversi modi, tramite: o Stringa vuota: “ ” o Zero come stringa: “ 0 ” o Zero come numero: 0 o NULL o FALSE o Un array vuoto: array( ) o Una variabile dichiarata ma senza valore: $var Per creare gli altri riquadri successivi al primo, ho utilizzato una procedura semplificata composta da un ciclo for, il quale crea tanti riquadri (ripete in cascata gli elementi all’interno del ciclo) quant’è il valore numerico contenuto nella variabile $form_state[‘storage’][‘num_task’]. Utilizzo come variabile contatore la $i che viene incrementata di 1 ad ogni iterazione, essa viene inizializzata a 2 perché se devo creare altri riquadri parto dal task2 dato che il task1 è stato creato in precedenza. Per iterare tante volte quanti sono i task scelti dall’utente ho previsto il controllo $i<=$form_state[‘storage’][‘num_task’], il quale fa in modo che fino a quando questa condizione risulta verificata allora itera tutte le istruzioni contenute all’interno del ciclo for. Gli elementi da ripetere in cascata sono il nome dell’etichetta del riquadro relativo al task i-esimo e i rispettivi campi per l’inserimento del tempo di computazione, per l’inserimento della deadline e per l’inserimento del periodo. Cioè saranno gli stessi elementi aventi le stesse caratteristiche (type, title, collapside, collapsed, size, maxlenght, description, default_value) di quelli scritti ed esaminati per il riquadro relativo al primo task. L’unica differenza che si trova in questo caso sono gli indici degli elementi, in cui si evidenzia la presenza della variabile contatore $i; questo perché ad ogni iterazione vado a ricreare un nuovo riquadro relativo ad uno specifico task, il quale dovrà avere una variabile con indice differente dagli altri task: • $form[‘task’ . $i] ad esempio per il task2 sarà $form[‘task2’], per il task3 sarà $form[‘task3’], ecc ad esempio per il task2 sarà $form[‘task2’][‘comp2’], per • $form[‘task’ . $i][‘comp’ . $i] il task3 sarà $form[‘task3’][comp3’], ecc 84 • • $form[‘task’ . $i][‘dead’ . $i] ad esempio per il task2 sarà $form[‘task2’][‘dead2’], per il task3 sarà $form[‘task3’][dead3’], ecc $form[‘task’ . $i][‘perio’ . $i] ad esempio per il task2 sarà $form[‘task2’][‘perio2’], per il task3 sarà $form[‘task3’][perio3’], ecc Una volta creati gli elementi del form che andranno a costituire i riquadri per l’inserimento dei parametri dei vari task restano da creare gli elementi rimanenti. Per quanto riguarda i due tasti per aggiungere e rimuovere un riquadro sono andato a mettere un controllo if sul valore della variabile $form_state[‘storage’][‘num_task’]; questo permette la visualizzazione dei tasti solo se il risultato dell’if dà come esito TRUE. Siccome da specifiche si possono avere al massimo dieci task, il controllo fa in modo che il bottone “Aggiungi task” compaia fino a quando i task siano in numero inferiore a dieci; infatti quando si hanno dieci riquadri l’esito dell’if è FALSE e questo fa in modo che le istruzioni contenute al suo interno, le quali generano il bottone “Aggiungi task”, non vengano eseguite e di conseguenza non venga visualizzato il relativo bottone. Inizialmente, quando il form viene caricato per la prima volta, la variabile $form_state[‘storage’][‘num_task’] ha come valore di default il valore vuoto, quindi risulta TRUE al controllo if, perché il valore vuoto può essere anche 0 e 0 risulta minore di 10. Siccome da specifiche si può avere minimo un task, il controllo fa in modo che il bottone “Rimuovi task” non compaia quando i task siano in numero inferiore a due; infatti quando si ha un solo riquadro l’esito dell’if è FALSE e questo fa in modo che le istruzioni contenute al suo interno, le quali generano il bottone “Rimuovi task”, non vengano eseguite e di conseguenza non venga visualizzato il relativo bottone. Inizialmente, quando il form viene caricato per la prima volta, la variabile $form_state[‘storage’][‘num_task’] ha come valore di default il valore vuoto, quindi risulta FALSE al controllo if, perché il valore vuoto può essere anche 0 e 0 non risulta maggiore di 1. Nel caso di questi due bottoni gli indici sono ‘aggiungi_task’ e ‘rimuovi_task’ e gli attributi sono: Tipo di form: submit, bottone di sottomissione; Valore: testo che verrà visualizzato sul bottone (nel mio caso sarà “Aggiungi task” e “Rimuovi task”; Validazione: mostra al rispettivo bottone il nome del suo gestore di convalidazione. Quando l’utente andrà a cliccare su uno di questi due bottoni, verrà aggiunto o rimosso un task, cioè un riquadro, dal form. 85 Ora devo creare un form che permetta all’utente di selezionare un algoritmo tra quelli disponibili come valori del form (visualizzati nel menù a tendina che si apre una volta cliccatoci sopra). L’indice di questo form è ‘tipo_alg’ e gli attributi sono: Tipo di form: select, selezione di una voce presente nel menù a tendina; Titolo: testo, visualizzato all’utente, presente vicino al form select; Opzioni: è un array che contiene le varie voci (value) che saranno visualizzate nel menù a tendina e quindi selezionabili dall’utente; Valore: testo di una voce del menù a finestra, mostrato all’utente. Infine l’ultimo form da generare è il bottone di sottomissione dell’intero form (quindi dell’intera pagina, dato che questa pagina è composta totalmente da form). Il form che rappresenta il bottone di submit ha come indice ‘submit’ ed i seguenti attributi: Tipo di form: submit, bottone di sottomissione del form; Valore: testo che verrà visualizzato sul bottone, nel mio caso “Schedula”. La funzione algoritmi_form( ) ritorna come valore l’intero array $form[ ] dove sono contenuti tutti gli elementi (individuati dai vari indici) che compongono il form. Funzione algoritmi_aggiungi_task La funzione algoritmi_aggiungi_task( ), avente come ID il nome del form stesso (algoritmi) e il nome dell’indice del bottone di submit (aggiungi_task), contiene varie istruzioni che vengono eseguite non appena l’utente clicca sul bottone di submit “Aggiungi task”. Cliccando su questo bottone le istruzioni contenute nella funzione algoritmi_aggiungi_task( ) permettono di rivisualizzare il form con un riquadro in più, ovvero danno la possibilità all’utente di poter inserire altri tre parametri relativi ad un altro task. Gli argomenti in ingresso a questa funzione sono l’array $form, che contiene tutti i dati dei form da visualizzare, e l’indirizzo (grazie al simbolo &, che indica il passaggio per riferimento) dell’array $form_state. In pratica questa funzione viene attivata ogni volta che l’utente clicca sul bottone “Aggiungi task” ed essa: Fa un controllo sul valore contenuto nella variabile $form_state[‘storage’][‘num_task’], cioè controlla il numero di task che erano presenti prima che l’utente cliccasse sul bottone; Aggiorna il valore della variabile $form_state[‘storage’][‘num_task’], inizializzandola al valore numerico successivo rispetto a quello contenuto da essa; 86 Richiama nuovamente la funzione che costruisce il form, questo viene fatto impostando a TRUE un'altra variabile di default di Drupal 6 cioè $form_state[‘rebuild’]; quindi il form verrà ri-caricato solo che questa volta si avrà il valore contenuto nella variabile $form_state[‘storage’][‘num_task’] incrementato di una unità. Avendo trattato nella funzione algoritmi_form( ) il primo task in maniera separata da quelli successivi, anche in questa funzione bisogna fare la stessa cosa. Questo perché nella variabile $form_state[‘storage’][‘num_task’] l’esistenza del task1 è indicata con il valore di default vuoto (che può essere anche lo 0), mentre l’esistenza dei task successivi è indicata con il valore numerico proprio dei task presenti, cioè dai riquadri visualizzati nel form (cioè se vi sono due task il valore sarà 2, se vi sono 3 task il valore sarà 3, ecc.). Il controllo sul numero dei task presenti viene fatto con un istruzione if che verifica se la variabile $form_state[‘storage’][‘num_task’] è vuota (grazie all’istruzione empty): Se questa condizione risulta TRUE (quindi siamo nel caso che solo il task1 è presente) viene eseguito il blocco di istruzioni contenuto nell’if, cioè viene aggiornata la variabile $form_state[‘storage’][‘num_task’] inizializzandola a 2 (il numero dei riquadri è stato aggiornato in modo tale che venga aggiunto il secondo task) e viene richiamata la funzione che genera il form. Il risultato di queste operazioni produrrà un form che ora visualizzerà due riquadri. Se questa condizione risulta FALSE (quindi siamo nel caso che sono presenti altri task oltre al task1) viene eseguito il blocco di istruzioni contenuto nell’else; questo blocco è costituito da un costrutto switch-case, il quale in base al numero contenuto nella variabile $form_state[‘storage’][‘num_task’] individua il valore di questa (che rappresenta il numero di task presenti), aggiorna questo valore aumentandolo di una unità rispetto a quello presente e richiama la funzione che genera il form. Il risultato di queste operazioni produrrà un form che ora visualizzerà da tre fino a dieci riquadri. Nota: la presenza del break alla fine di ogni case fa in modo che non vengano eseguiti in cascata i case successivi a quello che verifica la condizione del valore contenuto nella variabile $form_state[‘storage’][‘num_task’]. 87 Funzione algoritmi_rimuovi_task La funzione algoritmi_rimuovi_task( ), avente come ID il nome del form stesso (algoritmi) e il nome dell’indice del bottone di submit (rimuovi_task), contiene varie istruzioni che vengono eseguite non appena l’utente clicca sul bottone di submit “Rimuovi task”. Cliccando su questo bottone le istruzioni contenute nella funzione algoritmi_rimuovi_task( ) permettono di rivisualizzare il form con un riquadro in meno. Gli argomenti in ingresso a questa funzione sono l’array $form, che contiene tutti i dati dei form da visualizzare, e l’indirizzo (grazie al simbolo &, che indica il passaggio per riferimento) dell’array $form_state. In pratica questa funzione viene attivata ogni volta che l’utente clicca sul bottone “Rimuovi task” ed essa: Fa un controllo sul valore contenuto nella variabile $form_state[‘storage’][‘num_task’], cioè controlla il numero di task che erano presenti prima che l’utente cliccasse sul bottone; Aggiorna il valore della variabile $form_state[‘storage’][‘num_task’], inizializzandola al valore numerico precedente rispetto a quello contenuto da essa; Richiama nuovamente la funzione che costruisce il form, questo viene fatto impostando a TRUE un'altra variabile di default di Drupal 6 cioè $form_state[‘rebuild’]; quindi il form verrà ri-caricato solo che questa volta si avrà il valore contenuto nella variabile $form_state[‘storage’][‘num_task’] decrementato di una unità. In questa funzione non è necessario trattare il primo task in maniera separata dagli altri perché il bottone “Rimuovi task” non viene visualizzato quando si ha solo il task1, ma viene visualizzato quando ci sono due o più task, quindi di sicuro avremo a che fare con più di un task. Anche in questo caso però nella variabile $form_state[‘storage’][‘num_task’] l’esistenza del task1 è indicata con il valore di default vuoto (che può essere anche lo 0), mentre l’esistenza dei task successivi è indicata con il valore numerico proprio dei task presenti, cioè dai riquadri visualizzati nel form (cioè se vi sono due task il valore sarà 2, se vi sono 3 task il valore sarà 3, ecc.). Il controllo sul numero dei task presenti viene fatto direttamente con il costrutto switchcase, il quale verifica il valore numerico presente nella variabile $form_state[‘storage’][‘num_task’] e, in base a questo numero (che rappresenta il numero di task presenti), individua il relativo case; quest’ultimo contiene le istruzioni per aggiornare il valore numerico diminuendolo di una unità rispetto a quello presente e richiama la funzione che genera il form. Il risultato di queste operazioni produrrà un form che ora visualizzerà da uno fino a nove riquadri. Nota: la presenza del break alla fine di ogni case fa in modo che non vengano eseguiti in cascata i case successivi a quello che verifica la condizione del valore contenuto nella variabile $form_state[‘storage’][‘num_task’]. 88 Gancio hook_validate Il gancio hook_validate permette di validare i form, o meglio verificare la correttezza dei valori inseriti nei form dall’utente (se i valori rispettano determinate specifiche stabilite a priori). Il nome della funzione di validazione è l’ID della form stessa con _validate allegato ad essa, nel mio caso algoritmi_validate( ). Poiché la funzione di convalida ha lo stesso nome della funzione che genera i form (algoritmi), Drupal userà questa funzione di convalida automaticamente non appena il form sarà sottomesso (submit). La funzione ha due argomenti in ingresso: $form: l’array del modulo del form eseguito, cioè il form appartenente ad ‘algoritmi’ che voglio validare; $form_state[‘values’]: l’array che contiene i valori del modulo in cui si può eseguire la convalidazione. Mentre non prevede nessun valore di ritorno. Venendo alla mia funzione di validazione, per prima cosa prelevo dalla variabile standard, prevista da Drupal 6 per il deposito dei dati, $form_state[‘storage’][‘num_task’] il numero dei task inseriti dall’utente e memorizzo questo valore in una variabile chiamata $num_task. Anche per questa funzione bisogna trattare il primo task in maniera separata da quelli successivi. In questo caso la motivazione è la differenza del metodo di indicizzazione dato all’array $form_state[‘values’] per il task1 rispetto a quelli successivi; ad esempio l’indice della variabile contenente il tempo di computazione per il task1 è $form_state[‘values’][‘comp1’], mentre per i task successivi al primo è $form_state[‘values’][‘comp’ . $i] dove $i è la variabile contatore che va da 2 a 10. Per il primo task memorizzo direttamente i tre parametri, inseriti dall’utente e inviati dopo la sottomissione del form alla variabile di default di Drupal 6 $form_state[‘values’], nelle rispettive variabili $c, dove memorizzo il tempo di computazione, $d, dove memorizzo la deadline relativa, e 89 $t, dove memorizzo il periodo. E su queste tre variabili che contengono i parametri del task1 verranno fatti diversi controlli per verificarne l’esistenza e la correttezza. Per i task successivi al primo adopero un ciclo for che va da $i=2 fino al valore contenuto nella variabile $num_task, quindi itera il blocco di istruzioni contenuto al suo interno tante volte quant’è il numero dei task meno uno (perché il task1 è stato considerato a parte). In questo blocco de istruzioni viene fatta la scansione di tutti i task e ad ogni iterazione memorizzo i parametri (tempo di computazione, deadline e periodo) del task i-esimo nelle rispettive variabili $c, $d e $t; questi valori i-esimi sono contenuti nell’array $form_state[‘values’] rispettivamente agli indici [‘comp’ . $i], [‘dead’ . $i] e [‘perio’ . $i], quindi utilizzo il ciclo for per scorrere i valori dei parametri dei task attraverso la variabile contatore $i. E ad ogni iterazione su queste tre variabili, che contengono i parametri del task i-esimo, verranno fatti diversi controlli per verificarne l’esistenza e la correttezza. 90 Vi sono diversi controlli da fare sui parametri di ciascun task. • Sulla variabile $c che riguarda il tempo di computazione del task i-esimo: o Controlla che la variabile $c contenga un numero intero e compreso tra 0 e 99. Il tempo di computazione deve essere espresso in millisecondi ed essi devono appartenere all’insieme dei numeri interi, inoltre il loro valore deve essere compreso tra 0 e 99. Per prima cosa, controllo che il valore di $c non sia un numero intero (if(!is_numeric($c)) ), e se la condizione non è verificata, con l’istruzione form_set_error, visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. Successivamente, appurato quindi che il valore inserito è un numero intero, controllo se il numero in questione non è compreso tra 0 e 99 ( if($c<0 && $c>99) ) e se la condizione non è verificata, quindi con l’istruzione form_set_error visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. o Controlla che la variabile $c sia minore della variabile $t. Il tempo di computazione del task i-esimo deve essere per forza minore del periodo del task i-esimo; se così non fosse il task andrebbe in overflow già in partenza, perché finirebbe sempre il periodo e mai la computazione. In questo caso verifico che il tempo di computazione del task i-esimo, contenuto in $c, sia maggiore del periodo del task i-esimo, contenuto in $t, se ciò accade il task va in overflow a priori e quindi devo segnalare all’utente un errore di inserimento nei dati. Quest’errore come sempre viene segnalato grazie all’istruzione form_set_error, che inoltre blocca anche la sottomissione del form. o Controlla che la variabile $c sia minore della variabile $d. Il tempo di computazione del task i-esimo deve essere per forza minore della deadline del task i-esimo; se così non fosse il task andrebbe in overflow già in partenza, perché finirebbe sempre prima la deadline della computazione. In questo caso verifico che il tempo di computazione del task i-esimo, contenuto in $c, sia maggiore rispetto alla deadline del task i-esimo, contenuta in $d, se ciò accade il task va in overflow a priori e quindi devo segnalare all’utente un errore di 91 inserimento nei dati. Quest’errore come sempre viene segnalato grazie all’istruzione form_set_error, che inoltre blocca anche la sottomissione del form. • Sulla variabile $d che riguarda la deadline del task i-esimo: o Controlla che la variabile $d contenga un numero intero e compreso tra 0 e 99. La deadline deve essere espresso in millisecondi ed essi devono appartenere all’insieme dei numeri interi, inoltre il loro valore deve essere compreso tra 0 e 99. Per prima cosa, controllo che il valore di $d non sia un numero intero (if(!is_numeric($d)) ) e se la condizione non è verificata, con l’istruzione form_set_error, visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. Successivamente, appurato quindi che il valore inserito è un numero intero, controllo se il numero in questione non è compreso tra 0 e 99 ( if($d<0 && $d>99) ) e se la condizione non è verificata, con l’istruzione form_set_error visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. • Sulla variabile $t che riguarda il periodo del task i-esimo: o Controlla che la variabile $t contenga un numero intero e compreso tra 0 e 99. Il periodo deve essere espresso in millisecondi ed essi devono appartenere all’insieme dei numeri interi, inoltre il loro valore deve essere compreso tra 0 e 99. Per prima cosa, controllo che il valore di $t non sia un numero intero (if(!is_numeric($t)) ) e se la condizione non è verificata, con l’istruzione form_set_error, visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. Successivamente, appurato quindi che il valore inserito è un numero intero, controllo se il numero in questione non è compreso tra 0 e 99 ( if($t<0 && $t>99) ) e se la condizione non è verificata allora, con l’istruzione form_set_error, visualizzo all’utente il tipo di errore che ha commesso nell’inserimento dei dati e a quale particolare valore faccio riferimento; blocco poi il submit del form. Funzione algoritmi_submit La funzione algoritmi_submit( ) contiene le istruzioni ed il codice principale di tutto il modulo. Infatti, questa funzione, in base all’algoritmo selezionato ed ai parametri inseriti dall’utente, compie le operazioni di scheduling dei task. Come prima cosa, grazie all’istruzione drupal_set_message, visualizzo all’utente un messaggio per informarlo che il form è stato sottomesso correttamente, cioè che i dati inseriti e selezionati dall’utente sono corretti e sono stati inviati al server per essere elaborati. Sapendo che i valori dei dati inseriti dall’utente nei form vengono raccolti nell’array $form_state[‘values’], per ricavare un determinato valore di un elemento del form basta riferirsi al 92 nome che si è dato a quel preciso elemento, quindi basta scrivere esplicitamente il secondo indice (l’indice deve essere il nome di un elemento dell’array $form) nell’array $form_state. Dopo questo messaggio iniziale, bisogna andare a vedere quale algoritmo di scheduling è stato selezionato dall’utente, in modo tale da sapere a quali parametri dare la priorità e quali istruzioni compiere per l’elaborazione dei task. Il valore della SELECT “Tipo algoritmo” selezionato dall’utente viene raccolto nell’array $form_state[‘values’][‘tipo_alg’], dove ‘tipo_alg’ è l’indice dell’array $form che costruisce la SELECT “Tipo algoritmo”. Per sapere quindi quale algorirmo l’utente ha selezionato tra quelli disponibili nella SELECT basta fare un controllo con l’istruzione if, cioè se il valore contenuto nella variabile $form_state[‘values’][‘tipo_alg’] è uguale a ‘EDF’ allora l’utente ha selezionato l’algoritmo di scheduling EDF se invece è uguale a ‘RM’ allora l’utente ha selezionato l’algoritmo RM. A seconda dell’algoritmo scelto verranno eseguite differenti istruzioni per elaborare i dati. Siccome il linguaggio C, come detto prima, è molto simile al linguaggio PHP, i ragionamenti illustrati in precedenza per il C sono identici a quelli da fare in PHP e il codice scritto in C e spiegato precedentemente viene riportato in PHP quasi identicamente nel nostro file .module. Di seguito verranno spiegate solamente le parti che si differenziano dagli algoritmi scritti in C. Il primo passo da compiere è quello della raccolta dei dati. Inizialmente viene fatto un controllo, con l’istruzione if, per verificare se la variabile $form_state[‘storage’][‘num_task’] non è vuota: Se l’esito è TRUE allora la variabile contiene un valore numerico che va da 2 a 10 (il taskset è composto anche dai task successivi al task1); il valore di questa variabile viene memorizzato in un'altra variabile chiamata, per comodità, $num_task. Se l’esito è FALSE allora la variabile contiene un valore vuoto, può essere anche lo 0, (il task-set è composto solo dal task1); la variabile $num_task viene inizializzata con il valore numerico 1, ad indicare la presenza di un solo task. (Nota: non posso memorizzare in $num_task il valore contenuto da $form_state[‘storage’][‘num_task’] perché il valore vuoto, che può essere anche 0, è diverso dal valore 1; e per sviluppare gli algoritimi di scheduling bisogna tener conto del numero effettivo di task che compongono il task-set). Quindi la variabile $num_task andrà a contenere il valore che indica il numero dei task che compongono il task-set. Successivamente nell’array bidimensionale $task vado a memorizzare i parametri di tutti i task inseriti dall’utente, quest’array ha come indici: [$i]: è il primo indice ed indica il task i-esimo preso in considerazione; [‘c’] o [‘d’] o [‘t’]: è il secondo indice ed indica rispettivamente o il tempo di computazione o la deadline relativa o il periodo, cioè i parametri del task i-esimo. 93 Anche in questo caso, siccome nel form gli indici del task1 sono stati definiti in maniera separata dagli indici dei task successivi, è necessario scindere in task1 dai task successivi ad esso. Per tener conto di tutti i task e di tutti i loro parametri è stato creato un array bidimensionale chiamato $task e avente la seguente sintassi: $task[‘posizione_task’][‘parametro_task’]. Quindi i tre parametri del task1, inseriti dall’utente nel primo riquadro del form, vengono memorizzati nell’array bidimensionale $task avente come indice il numero 0 (perché negli array l’indice parte da 0 e non da 1). Questo mi permette: • di memorizzare nell’elemento con indice [0] e indice [‘c’] dell’array associativo $task il valore del tempo di computazione del task1 inserito dall’utente nel form che è contenuto nell’elemento dell’array $form_state aventi indici [‘values’] e [‘comp1’]; • di memorizzare nell’elemento con indice [0] e indice [‘d’] dell’array associativo $task il valore della deadline relativa del task1 inserito dall’utente nel form che è contenuto nell’elemento dell’array $form_state aventi indici [‘values’] e [‘dead1’]; • di memorizzare nell’elemento con indice [0] e indice [‘t’] dell’array associativo $task il valore del tempo periodo del task1 inserito dall’utente nel form che è contenuto nell’elemento dell’array $form_state aventi indici [‘values’] e [‘perio1’]. Anche i tre parametri di ogni task successivo al task1, inseriti dall’utente nei vari riquadri del form, vengono memorizzati tutti nell’array bidimensionale $task grazie ad un ciclo for che itera l’array $task[$i], dove $i è la variabile contatore ed il ciclo parte da $i=1 e itera tante volte quant’è il valore contenuto in $num_task meno uno (perché il task1 è trattato a parte), in modo tale che i tre parametri corrispondenti al task i-esimo vengono memorizzati correttamente nel task i-esimo. Questo mi permette: • di memorizzare nell’elemento con indice i-esimo [$i] e indice [‘c’] dell’array associativo $task il valore del tempo di computazione del task i-esimo inserito dall’utente nel form che è contenuto nell’elemento i-esimo dell’array $form_state aventi indici [‘values’] e [‘comp’ . $i]; • di memorizzare nell’elemento con indice i-esimo [$i] e indice [‘d’] dell’array associativo $task il valore della deadline relativa del task i-esimo inserito dall’utente nel form che è contenuto nell’elemento i-esimo dell’array $form_state aventi indici [‘values’] e [‘dead’ . $i]; • di memorizzare nell’elemento con indice i-esimo [$i] e indice [‘t’] dell’array associativo $task il valore del tempo periodo del task i-esimo inserito dall’utente nel form che è contenuto nell’elemento i-esimo dell’array $form_state aventi indici [‘values’] e [‘perio’ . $i]. In sintesi l’array $task[0] con indice ‘0’ conterrà i tre parametri del task1, $task[1] con indice ‘1’ conterrà i tre parametri del task2, $task[2] con indice ‘2’ conterrà i tre parametri del task3, …, $task[9] con indice ‘9’ conterrà i tre parametri del task10. 94 Con questi passaggi sono andato a raccogliere tutti i dati inseriti nel form dall’utente, ma mancano ancora alcune variabili da dichiarare e da definire per poter iniziare l’algoritmo vero e proprio. Per rispettare i ragionamenti e le operazioni fatte per il linguaggio C anche qui vado ad aggiungere, grazie al ciclo for, altri due elementi all’array $task. Questi elementi, aventi indice [‘num_comp’] e [‘ready’], assumono lo stesso significato e lo stesso ruolo assunto in precedenza per il C; il primo tiene conto delle computazioni terminate dal task i-esimo (ecco perché inizialmente viene inizializzato a 0), il secondo tiene conto della presenza del task i-esimo nella ready queue (inizialmente viene messo ad 1, che indica la presenza del task nella ready queue, mentre 0 indica l’assenza del task i-esimo nella ready queue). In questo modo ora l’array bidimensionale $task[ ][ ] del PHP ha la stessa struttura dell’array bidimensionale task[ ][ ] del C. L’unica differenza è che mentre in C gli indici devono essere per forza numeri che vanno da 0 a n-1, in PHP gli indici possono essere anche stringhe qualsiasi; di seguito elenco la corrispondenza degli indici dal C al PHP per una maggiore comprensione: i $i (il primo indice è una variabile) 0 ‘c’ (il secondo indice è una precisa costante) 1 ‘d’ 2 ‘t’ 3 ‘num_comp’ 4 ‘ready’ Dopo la creazione dell’array $task vado a dichiarare e definire (come fatto in C) le altre variabili che verranno utilizzate per la codifica dell’algoritmo. Queste variabili sono: $secondi=0: definizione della variabile che porterà il conto di ogni secondo che passa dall’inizio dell’algoritmo di scheduling fino alla fine di questo (parto da 0 secondi); $overflow=0: definizione del flag che eventualmente fermerà il ciclo iterativo while (contenente le istruzioni dell’algoritmo) se il valore di questo flag sarà uguale a 1, in caso contrario il ciclo while continuerà ad iterare le istruzioni contenute in esso (all’inizio nessun task è in overlfow, quindi pongo 0 come valore); $min_task: dichiarazione della variabile che conterrà la posizione del task i-esimo avente deadline assoluta minore (in EDF) o periodo minore (in RM); 95 $min_dead (in EDF): variabile che conterrà il valore della deadline assoluta minore tra tutte quelle dei task presenti nella ready queue; $min_perio (in RM): variabile che conterrà il valore del periodo minore tra tutti quelli dei task presenti nella ready queue; $scheduling_array: dichiarazione dell’array di scheduling. Mi soffermo a spiegare in maniera più approfondita l’array $scheduling_array dato che questo è la vera e propria novità del PHP rispetto al codice C. La motivazione che mi ha spinto a dichiarare questo array sta nella necessità di tener conto del task specifico che computa in un preciso secondo di tempo, ecco anche perchè l’unico indice dell’array è la variabile $secondi. In questo array quindi verrà memorizzato il task i-esimo che computa (occupa la CPU) in quel preciso secondo, perciò come ad ogni iterazione la variabile $secondi aumenta di 1 (il tempo passa) anche l’array $scheduling_array ad ogni iterazione assumerà il valore della posizione del task avente priorità maggiore. Detto questo è di facile intuizione che quando l’indice $secondi è uguale a 0 anche il valore di $scheduling_array è 0, dato che nessun task computa nell’istante 0. Siccome anche qui c’è la possibilità che tutti i task inseriti dall’utente finiscano le loro computazioni prima dell’inizio del loro prossimo periodo, quindi che la CPU resti inutilizzata per alcuni secondi, bisogna tenere conto anche di questo caso. Quindi nell’array $task inserisco anche un task aggiuntivo per tener conto dei tempi morti della CPU (è come un task invisibile); questo task per non essere mai selezionato dall’algoritmo deve avere un tempo di computazione, una deadline relativa e un periodo molto maggiore rispetto ai task e, inoltre, deve avere per ipotesi il numero di computazioni=0 ed essere sempre presente nella ready queue quindi flag ready=1. Siccome gli elementi di un array partono dall’indice 0 (i=0 corrisponde al task1, i=1 al task2,…) l’ultimo task inserito dall’utente corrisponde all’indice $num_task-1, quindi l’indice che avrà il “task per i tempi morti” sarà $num_task. Infine, prima di procedere con l’elaborazione vera e propria dell’algoritmo, vado a creare anche qui l’array $ready_queue. In PHP, a differenza del C, posso copiare tutti i valori di un array in un altro array semplicemente uguagliandolo. 96 Con il ciclo while inizia l’algoritmo di scheduling vero e proprio. Qui quasi tutti i ragionamenti e, di conseguenza, tutte le istruzioni sono identiche a quelle fatte per il linguaggio C. L’algoritmo EDF dà la priorità al task avente deadline assoluta minore: Per l’algoritmo RM dà la priorità al task avente periodo minore: Le uniche differenze evidenti sono dovute alla presenza delle istruzioni che riguardano l’array $scheduling_array, quindi di seguito andrò a descrivere solo queste. $scheduling_array[$secondi] = $min_task+1 Quest’istruzione memorizza nell’array $scheduling_array, secondo per secondo, il numero che identifica la posizione del task con priorità maggiore, occupata nell’array $task, che ha computato in quel determinato secondo (metto $min_task+1 perché gli array partono dall’indice 0). Quest’istruzione è posizionata subito dopo che è stato scelto il task con maggiore priorità e che quest’ultimo ha computato, quindi nel blocco che riguarda la computazione del task. Nota: se in quel preciso secondo nessun task ha computato (perché nessuno è presente nella ready queue) nell’array $scheduling_array viene memorizzata la posizione del “task invisibile” con priorità maggiore (è l’unico task presente nella ready queue), occupata nell’array $task, che tiene conto dei tempi morti, questo viene automaticamente fatto con la stessa istruzione grazie al fatto di aver posto inizialmente nell’array $task la presenza di un altro task, appunto il “task invisibile”. 97 $scheduling_array[$secondi+1] = $i+1 Quest’istruzione memorizza nell’array $scheduling_array, al secondo successivo dell’ultima computazione ($secondi+1), la posizione del task i-esimo, occupata nell’array $task, che va in overflow (metto $i+1 perché gli array partono dall’indice 0). Quest’istruzione è posizionata nel blocco dove c’è il controllo di overflow su tutti task presenti nella ready queue. Considero il secondo successivo all’ultima computazione perché nel secondo corrente avviene l’ultima computazione e quando si verifica l’overflow di un task il ciclo while viene interrotto (grazie al flag $overflow=1) quindi i secondi non avanzano più e non ci sono più computazioni. Devo tener conto del task i-esimo che va in overflow in modo tale che successivamente, nel diagramma di Gantt, posso evidenziarlo con un colore diverso rispetto a quello dei task che computano (vedi specifiche). Successivamente, nella funzione algoritmi_schedula( ) è presente una chiamata alla funzione crea_gantt( ), la quale, in base agli argomenti passategli in ingresso, genera un digramma di Gantt che visualizza graficamente all’utente i risultati dell’elaborazione dell’algoritmo di scheduling selezionato. Gli argomenti passati alla funzione crea_gantt( ) sono: l’array $scheduling_array, la variabile $secondi, la variabile $num_task e l’array $task. Nota: $scheduling_array[$secondi] contiene la posizione del task i-esimo, occupata nell’array $task, che computa nell’ultima iterazione del ciclo while; mentre $scheduling_array[$secondi+1] contiene la posizione del task i-esimo, occupata nell’array $task, che va in overlflow, questa posizione viene memorizzata al secondo successivo dopo la terminazione del ciclo while. 98 Una volta che la funzione crea_gantt( ) è terminata, cioè ha generato e salvato il diagramma di gantt in un file immagine, devo poter visualizzare quest’immagine caricandola da questo file chiamato “diagramma.png”(Drupal non può visualizzare le immagini direttamente da codice PHP). L’istruzione print permette proprio di richiamare (quindi di caricare) e di visualizzare a schermo il contenuto di un file immagine. Il parametro da passare a quest’istruzione è semplicemente il percorso e il nome del file immagine che si vuole visualizzare. Creazione del file crea_gantt.php Nozioni principali sulla libreria GD del PHP Prima di passare alla spiegazione della funzione crea_gantt( ) da me realizzata, vado a fare un introduzione dei concetti generali e delle funzioni principali della libreria GD, dato che ho creato la mia funzione basandomi proprio su questa libreria. La libreria GD serve per la creazione di immagini tramite codice PHP. Le librerie GD sono infatti in grado di creare tramite semplice codice PHP numerosi tipi di immagini, quali jpeg, png, tiff, ecc. Il supporto al formato gif è stato recentemente abolito, in quanto si tratta di un formato proprietario della Sun, mentre le librerie GD sono open source. Dalla versione 2.0.28 tale supporto è stato però ripristinato, e dalla versione 2.0.32 le librerie GD hanno anche la possibilità di creare gif animate. L'utilità di questa libreria è enorme, basti pensare alla creazione di grafici o report in tempo reale, analizzando i dati da un database o inseriti da un utente tramite form. Per prima cosa bisogna assicurarsi che sul server la libreria GD sia installata: basta controllare che ci sia tra le librerie disponibili. Per fare ciò bisogna scrivere: <?php phpinfo( ); > e salvare il file php. Quando eseguite questo file avrete una tabella con tutte le caratteristiche del web server, di php e delle librerie disponibili. Oppure per controllare che siano presenti nella versione PHP installato sul server basta verificare che nella cartella C:/Programmi(x86)/phpDesigner/PHP/ext (extensions) ci siano i due file php_gd.dll e/o php_gd2.dll. Istruzioni basilari per la preparazione di un foglio da disegno Istruzione: imagecreate È un’istruzione che crea un foglio da disegno bianco su cui disegnare successivamente, cioè si va a creare la “base” per l’immagine. Su di esso si potrà disegnare utilizzando le funzioni fornite da PHP di interfaccia verso la libreria GD. 99 Istruzione: imagecreatetruecolor Prepara un foglio da disegno bianco su cui disegnare. Scrivendo ad esempio: <?php $immagine= imagecreatetruecolor (200,100); ?> viene creato un foglio di disegno che in orizzontale, ovvero sull’asse x, è lungo 200 pixel (cioè 200 punti luminosi elementari che costituiscono il disegno); mentre in verticale, sull’asse y, è di 100 pixel. I pixel si contano partendo dal punto superiore sinistro dello schermo, che ha coordinate x=0 ed y=0; inoltre l'asse x in orizzontale è orientato verso destra, mentre l'asse y verticale è orientato verso il basso, cioè più aumentano i pixel sull’asse x e più ci si sposta in orizzontale verso destra mentre più aumentano i pixel sull’asse y e più ci si sposta in verticale verso il basso. Questo foglio di disegno viene memorizzato in una variabile chiamata $immagine. Questo foglio da disegno viene disegnato nella memoria del server e non è quindi visibile all'utente; infatti, se viene eseguito non si vedrà niente. Per poterlo vedere occorre utilizzare l’istruzione: header; quest’istruzione è un pò delicata, in quanto essa deve essere la prima istruzione ad apparire nella pagina, non vi devono essere nemmeno spazi vuoti o altre scritte prima del suo uso, altrimenti non si vede niente; perché questa invia al browser un’intestazione che specifica che si tratta di un’immagine. Senza questa informazione il browser considererebbe i dati come codice HTML, e l'immagine non sarebbe interpretata come tale. Non si può nemmeno usare il tag iniziale <html>, altrimenti non funziona. L’istuzione header indica il formato che assumerà il disegno. Modificando, ora il codice scritto in precedenza nel modo seguente: <?php $immagine= imagecreatetruecolor (200,100); header ("Content-type: image/png"); ?> In quest’esempio l’istruzione header indica che il disegno sarà in formato png; si possono anche utilizzare altri formati come quello gif: header ("Content-type: image/gif"); però esso non funziona con le librerie php_gd2.dll ma solo con le librerie php_gd.dll oppure il formato jpeg: header ("Content-type: image/jpeg"); che funziona con entrambe le librerie. Infine ci serve un’istruzione image che mostri qual è il disegno vero e proprio che si è creato in memoria e lo restituisce al browser che provvede a stamparlo a schermo. - Per esempio: imagepng ($immagine); mostra sullo schermo un disegno in formato png che si trova in memoria nella variabile $immagine; - Invece: imagejpeg ($immagine); mostra sullo schermo un disegno in formato jpeg che si trova in memoria nella variabile $immagine; - Infine: imagegif ($immagine); mostra sullo schermo un disegno in formato gif che si trova in memoria nella variabile $immagine. 100 Dopo quest’introduzione, si può facilmente dedurre che il seguente programma mostrerà all’utente il foglio nero da disegno vuoto e pronto per essere riempito di dimensioni 200x100 pixel e in formato png: <?php $immagine= imagecreatetruecolor (200,100); header ("Content-type: image/png"); imagepng ($immagine); ?> Salvando il programma col nome di: disegno1.php ed eseguendolo (aprendo il file con un browser) si ottiene: I colori Per i colori si usa lo standard RGB, cioè i tre colori rosso, verde, blu. Mediante le combinazioni esadecimali dei tre colori fondamentali si ottengono tutti i 16 milioni di colori, cioè 16.581.375. Ricordiamo per inciso che un colore in esadecimale si indica con, ad esempio, #FFBB00. Dove i caratteri raggruppati a due a due corrispondono ai valori delle componenti RGB: quindi FF è il valore esadecimale per il rosso, BB per il verde e 00 per il blu. La funzione da utilizzare per definire un colore è: imagecolorallocate. A questa funzione deve essere passata l’immagine (nel nostro caso contenuta nella variabile $immagine), creata precedentemente con imagecreate( ), e i valori esadecimali corrispondenti ai colori RGB. Questa funzione restituisce un intero che identifica il colore. Questo intero può essere memorizzato in una vaiabile e passato ad altre funzioni per disegnare oggetti o testo di quel dato colore. - Scrivendo: $colore = imagecolorallocate ($immagine, 255, 255, 255); I valori da dare per ciascuno dei tre colori variano da 0 fino a 255; avendo messo: Rosso=255; Verde= 255; blu = 255; otterremo il colore bianco. - Scrivendo: $colore2 = imagecolorallocate ($immagine, 0, 0, 0); otteniamo il colore nero. - Scivendo: $colore3 = imagecolorallocate ($immagine, 255, 0, 0); otteniamo il colore rosso. 101 - Scrivendo: $colore4 = imagecolorallocate ($immagine, 0, 0, 255); otteniamo il colore blu, e così via. I colori vengono salvati nelle rispettive variabili da noi scelte a piacere ($colore, $colore2, ecc.) e potranno essere usati in ogni momento quando vi è la necessità. Disegnare una linea La funzione per disegnare una linea è: imageline. Se si scrive: imageline ($immagine, 10, 20, 100, 50, $colore); la funzione imageline disegna sull’immagine, contenuta nella variabile $immagine, che si trova in memoria, una linea che parte da due punti; i punti vanno messi nell'ordine con la rispettiva x ed y; nel nostro caso: x1 = 10; y1 = 20; x2 = 100; y2 = 50. Infine, come ultimo parametro da passare a questa funzione, ci sarà una variabile (nel nostro caso $colore), nella quale è stato memorizzato in valori esadecimali un colore che poi sarà quello assunto dalla linea. Scrivendo il seguente listato, salvandolo come disegno2.php ed eseguendolo si ottiene una linea rossa: <?php $immagine = imagecreatetruecolor (200,100); $colore = imagecolorallocate ($immagine, 255, 0, 0); imageline ($immagine, 10, 20, 100, 50, $colore); header ("Content-type: image/png"); imagepng ($immagine); ?> Liberare spazio in memoria La creazione di immagini occupa dello spazio in memoria che è opportuno liberare usando la funzione: imagedestroy. Scrivendo: imagedestroy ($immagine); 102 viene liberato lo spazio di memoria occupato dalla variabile $immagine; questo normalmente viene fatto alla fine del disegno, quando oramai si presuppone che la variabile non serva più. Disegnare un rettangolo L’istruzione utilizzata per creare un rettangolo è: imagerectangle. Scrivendo: imagerectangle ($immagine, 20,20, 180, 80, $colore); viene disegnato un rettangolo vuoto in base alla coordinate di due vertici, quello superiore sinistro (nel nostro esempio quello con x1=20 ed y1 = 20) e quello inferiore destro (nel nostro esempio quello con x2=180 ed y2 = 80). Un esempio di listato per il rettangolo sarà: <?php $immagine = imagecreatetruecolor (200,100); $colore = imagecolorallocate ($immagine, 255, 0, 0); imagerectangle ($immagine, 20,20, 180, 80, $colore); header ("Content-type: image/png"); imagepng ($immagine); imagedestroy ($immagine); ?> Salvandolo con il nome di disegno3.php ed eseguendolo possiamo notare che il rettangolo di contorno rosso è vuoto all’interno, cioè è stato disegnato solo il perimetro. Volendo riempire il rettangolo tutto di colore dobbiamo aggiungere “filled” all’istruzione imagerectangle, cioè: imagefilledrectangle ($immagine, 20,20, 180, 80, $colore); Salvandolo col nome di disegno4.php ed eseguendolo si ottiene un rettangolo tutto rosso sul foglio nero iniziale. 103 Scrivere un testo Per scrivere un testo si utilizza la funzione imagestring, la quale mi disegna su un immagine un testo in orizzontale. Aggiungendo al listato precedente: imagestring ($immagine, 4, 60, 82, "RETTANGOLO", $colore); ottenendo il seguente codice: <?php $immagine = imagecreatetruecolor (200,100); $colore = imagecolorallocate ($immagine, 255, 0, 0); $colore2 = imagecolorallocate ($immagine, 255, 255, 255); imagefilledrectangle ($immagine, 20,20, 180, 80, $colore); imagestring ($immagine, 4, 60, 82, "RETTANGOLO", $colore2); header ("Content-type: image/png"); imagepng ($immagine); imagedestroy ($immagine); ?> Salvandolo come disegno5.php ed eseguendolo si ottiene il disegno precedente (rettangolo tutto rosso) con in aggiunta la scritta RETTANGOLO di colore bianco (memorizzato nella variabile $colore) e avente dimensione 4 e coordinate x1=60 e y1=82. 104 Il primo numero (cioè nell’esempio 4) indica la dimensione del carattere (font) che va da 1, carattere molto piccolo a 5, carattere molto grande. Gli altri due numeri rappresentano nell'ordine le coordinate iniziali x1 e y1 in cui scrivere il testo (nell’esempio x1=60 ed y1 = 82), cioè la posizione del testo dall’angolo superiore sinistro dell’immagine. Successivamente viene scritto il testo da visualizzare tra virgolette oppure una variabile che contiene del testo. Infine vi è la variabile che contiene il colore del testo che verrà visualizzato. Tuttavia questa funzione è un pò “primitiva”. È molto più conveniente utilizzare imagettftext( ), che permette di definire più parametri, come ad esempio la rotazione, e utilizzare i font TrueType. Inoltre con la funzione imagettfbbox( ) è possibile conoscere le dimensioni del box entro il quale il testo sarà scritto prima di disegnarlo: ciò è molto utile nel caso in cui si voglia ad esempio effettuare in tempo reale alcuni test per definire la dimensione massima di un dato testo affinchè questo rimanga entro certi limiti di dimensioni. Disegnare una circonferenza e un cerchio L’istruzione per disegnare un cerchio vuoto, cioè una circonferenza è: imagearc. Scrivendo ad esempio: imagearc ($immagine, 100, 50, 60, 60, 0, 360, $colore3); I primi due numeri indicano le coordinate del centro della circonferenza (nel nostro caso: x1=100 ed y1 = 50). La seconda coppia di numeri indica la larghezza in orizzontale e in verticale (nel nostro caso larghezza=60 ed altezza=60); essendo questi due valori uguali si ottiene una circonferenza, ma se invece sono diversi quello che si ottiene sarà un’ellisse. L’ultima coppia di numeri riguarda i gradi di rotazione, cioè da 0° fino a 360°; se si vuole disegnare tutta la circonferenza si dovranno mettere come valori 0 e 360, mentre se si vuole disegnare solo un arco, cioè una parte della circonferenza basta decidere liberamente questi due valori (l’importante è che la rotazione non venga chiusa). Infine bisogna scegliere il colore della figura inserendo come ultimo parametro una variabile che contiene il valore di un colore (nel nostro caso $colore3). Il listato sarà: <?php $immagine = imagecreatetruecolor (200,100); $colore = imagecolorallocate ($immagine, 255, 0, 0); $colore2 = imagecolorallocate ($immagine, 255, 255, 255); $colore3 = imagecolorallocate ($immagine, 0,0, 255); 105 imagefilledrectangle ($immagine, 20,20, 180, 80, $colore); imagearc ($immagine, 100, 50, 60, 60, 0, 360, $colore3); imagestring ($immagine, 4, 0, 83, "Rettangolo e circonferenza", $colore2); header ("Content-type: image/png"); imagepng ($immagine); imagedestroy ($immagine); ?> Salvandolo come disegno6.php ed eseguendolo verranno visualizzati una circonferenza blu inscritta in un rettangolo pieno rosso e una scritta “Rettangolo e circonferenza” di colore bianco, il tutto sullo sfondo di disegno nero. Nota: la lettera “a” di “circonferenza” non viene visualizzata perché le dimensioni della stringa di testo e la lunghezza di questa risultano maggiori rispetto allo sfondo nero stabilito su cui ci si può disegnare. Mentre l’istruzione per disegnare un cerchio pieno è: imagefilledarc. Scrivendo quindi ad esempio: imagefilledarc ($immagine, 100, 50, 60, 60, 0, 360, $colore3, IMG_ARC_PIE); I primi due numeri indicano le coordinate del centro del cerchio (nel nostro caso: x1=100 ed y1 = 50). La seconda coppia di numeri indica la larghezza in orizzontale e in verticale (nel nostro caso larghezza=60 ed altezza=60); essendo questi due valori uguali si ottiene un cerchio, ma se invece sono diversi quello che si ottiene sarà un’ellisse piena. L’ultima coppia di numeri riguarda i gradi di rotazione, cioè da 0° fino a 360°; se si vuole disegnare tutto il cerchio si dovranno mettere come valori 0 e 360, mentre se si vuole disegnare solo un arco, cioè una parte del cerchio basta decidere liberamente questi due valori (l’importante è che la rotazione non venga chiusa). Successivamente bisogna scegliere il colore della figura inserendo come ultimo parametro una variabile che contiene il valore di un colore (nel nostro caso $colore3). L'ultimo argomento della funzione può essere: - IMF_ARC_PIE per disegnare un cerchio o semicerchio pieno; - IMF_ARC_CHORD per disegnare un semicerchio pieno con la corda che unisce i due raggi (settore circolare pieno); - IMF_ARC_NOFILL | IMF_ARC_EDGED per disegnare un semicerchio vuoto, con i due raggi ai bordi. 106 Scrivendo a titolo di esempio il listato seguente, salvandolo come disegno7.php ed eseguendolo si ottiene: <?php $immagine = imagecreatetruecolor (500,200); $colore = imagecolorallocate ($immagine, 255, 0, 0); $colore2 = imagecolorallocate ($immagine, 255, 255, 255); $colore3 = imagecolorallocate ($immagine, 0,0, 255); imagefilledarc($immagine, 100, 100, 100, 100, 0, 270, $colore3,IMG_ARC_PIE); imagefilledarc($immagine, 200, 100, 100, 100, 0, 270, $colore3,IMG_ARC_CHORD); imagefilledarc($immagine, 350, 100, 100, 100, 0, 270, $colore3, IMG_ARC_NOFILL|IMG_ARC_EDGED); header ("Content-type: image/png"); imagepng ($immagine); imagedestroy ($immagine); ?> Come ultimo appunto vado a citare per completezza altre due funzioni: imagepolygon( ): disegna un qualsiasi altro poligono vuoto; imagefilledpolygon( ): disegna un qualsiasi altro poligono pieno. File crea_gantt.php Come detto precedentemente, ho creato la funzione crea_gantt( ) basandomi principalmente sulla libreria GD e sulle istruzioni messe a disposizione da essa. Di seguito andrò a spiegare, blocco per blocco, il codice da cui è composto questa funzione. Nella parte iniziale del codice viene definito il nome della funzione e le variabili che dovranno essere passate come argomento alla funzione. Subito dopo, prima di iniziare le operazioni 107 vere e proprie della funzione, vengono fatti dei controlli sulle variabili passate come argomento per verificarne la coerenza. Questo controllo, fatto attraverso l’istruzione if, interrompe la funzione e ritorna il valore booleano FALSE anche se una sola delle condizioni contenute dall’if è rispettata: !isset($sceduling_array): verifica se l’array non è stato definito; empty($scheduling_array): verifica se l’array è vuoto; $num_task>10: verifica se il numero contenuto all’interno della variabile è maggiore di 10. Fatto questo si potrà passare alla fase relativa della creazione dell’immagine che conterrà i dati. Per fare questo verrà utilizzata la funzione imagecreatetruecolor( ), la quale ha il compito di creare una “tela” la cui dimensione viene stabilita sulla base di due semplici coordinate numeriche separate da una virgola (nel mio caso altezza=700 pixel e larghezza=350 pixel). La fase successiva sarà quella dedicata alla definizione dei colori che andranno a caratterizzare i diversi elementi del grafico. Per fare questo verrà utilizzata la funzione imagecolorallocate( ), la quale ha il compito di memorizzare il dato relativo ad un determinato colore per una specifica porzione di immagine identificata da una variabile. Nel mio caso ho previsto 17 diversi colori, ognuno avente uno scopo ben preciso, i quali sono stati definiti con valori RGB esadecimali e memorizzati in variabili a cui ho dato un nome semplice ed esplicativo, in modo tale da poter ricondursi facilmente allo scopo di ogni variabile che contiene un colore. Definita la parte relativa all’allocazione dei colori si potrà passare a quella necessaria per il riempimento dell’immagine di sfondo grazie all’utilizzo della funzione imagefill( ), questa si occupa di colorare una determinata immagine sulla base di quattro parametri: • la variabile relativa all'immagine da colorare; • la coordinata "x" che definisce il punto di partenza sull'asse orizzontale; • la coordinata "y" che definisce il punto di partenza sull'asse verticale; • il colore da utilizzare per la colorazione. Dato che nel caso specifico le coordinate relative ad entrambi gli assi cartesiani avranno un valore pari a "0", l'immagine verrà riempita completamente con il colore precedentemente definito per lo sfondo tramite l'utilizzo della funzione imagecolorallocate( ), nel mio caso il colore è contenuto nella variabile $colore_sfondo. 108 Sarà ora possibile passare alla fase relativa del disegno della griglia destinata a contenere i dati. Questo procedimento si basa in particolare sull'utilizzo della funzione imageline( ) che ha il compito di disegnare una linea sulla base delle coordinate passate come parametri. Creazione delle linee orizzontali del diagramma Le righe orizzontali del diagramma di Gantt andranno a separare un task da un altro task e l’ultima sarà l’asse x del diagramma. Essendo il numero dei task variabile (può deciderlo l’utente) da 1 a 10, anche il numero delle linee da disegnare sarà variabile. Quindi mentre lo spazio dedicato al diagramma di Gantt è fisso, quello dedicato alle linee orizzontali che separano i vari task sarà variabile (dipende dal numero dei task), cioè meno task ci sono e più grande sarà la distanza che separerà le linee orizzontali. Per essere delle linee orizzontali rette devo fare in modo che i valori delle cordinate x1,x2 devono essere fissi e y1,y2 devono essere uguali, altrimenti avrei delle linee oblique. In base a quanto detto prima, cioè che lo spazio tra una riga e l’altra è variabile in base al numero dei task, devo potermi calcolare il valore di quest’entità dinamica in modo tale da far risultare il diagramma proporzionato. Sapendo di avere a disposizione 300 pixel in verticale (definito in imagecreatetruecolor, ne sono 350 ma ne considero di meno perché lascio i restanti per visualizzare le stringhe di testo) e conoscendo il numero di task (quindi il numero di righe orizzontali) da visualizzare (contenuto nella variabile $num_task) posso calcolarmi il valore in pixel dello spazio dinamico che separerà, in base al numero di task, le varie righe orizzontali. Memorizzo quindi nella variabile $spazio il valore intero (grazie alla funzione intval) del rapporto tra pixel totali e numero di task, in modo tale da sapere quanto deve essere la distanza in pixel tra una linea orizzontale e la successiva. Ora grazie ad un ciclo for vado a creare tante linee orizzontali quant’è il numero dei task. Siccome l’ultima linea del diagramma è l’asse x deve avere un colore differente rispetto alle linee orizzontali che separano i vari task, quindi con un’istruzione if vado a trattarla in modo diverso, nel senso che mantengo sempre le stesse dimesioni ma vario il colore di questa. Con l’istruzione imageline traccio le linee orizzontali che vanno da 30+10 a 630 pixel in direzione X e si ripetono in direzione Y con una frequenza in pixel di $i*$spazio (qui vario la posizione delle linee orizzontali variando, grazie alla presenza di $i, le coordinate y1 e y2). Creazione delle linee verticali del diagramma Le colonne verticali del diagramma di Gantt andranno ad identificare la durata dei secondi, cioè ad ogni riga corrisponde un determinato secondo. La prima colonna verticale sarà l’asse y del diagramma. Essendo il numero dei secondi costante, la distanza tra un secondo e il successivo è un valore fisso, quindi anche le linee verticali da disegnare avranno la stessa distanza l’una dall’altra. Quindi sia lo spazio dedicato al diagramma di Gantt sarà fisso e sia lo spazio dedicato alle linee verticali che definiscono i secondi sarà fisso (durata sempre costante da un secondo ad un altro). Per essere delle linee verticali rette devo fare in modo che i valori delle cordinate y1,y2 devono essere fissi e x1,x2 devono essere uguali, altrimenti avrei delle linee oblique. 109 Essendo, come detto prima, la distanza tra una colonna e l’altra fissa, devo stabilire il valore di questa distanza in pixel in modo tale da far risultare il diagramma proporzionato, cioè graficamente la distanza tra un secondo e quello successivo deve essere uguale. Stabilisco come distanza tra una linea verticale e quella successiva il valore di 10 pixel. Ora grazie ad un ciclo for vado a creare tante linee verticali quanti sono il numero dei secondi, passati per computare il task-set e contenuti nella variabile $secondi. Siccome la prima linea del diagramma è l’asse y deve avere un colore differente rispetto alle linee verticali che rappresentano i secondi, quindi con un istruzione if vado a trattarla in modo diverso, nel senso che mantengo sempre le stesse dimesioni ma vario il colore di questa. Con l’istruzione imageline traccio le linee verticali che vanno da 15 a 310 pixel in direzione Y e si ripetono in direzione X con una frequenza in pixel di $i*10 (qui vario la posizione delle linee verticali variando, grazie alla presenza di $i, le coordinate x1 e x2). Creazione delle linee verticali del diagramma che evidenziano i periodi e le deadline Dopo aver creato la base del diagramma di Gantt devo andare ad evidenziare, con un diverso colore, alcune colonne verticali che identificano i vari periodi e le varie deadline di ogni task (ogni task avrà eventualmente questi due valori diversi dagli altri task). Siccome considero l’intero diagramma in entrambe le coordinate, perchè devo potermi spostare sia da task a task, (coordinata y) sia da secondo a secondo (coordinata x), devo poter variare (quindi avere una variabile, sia la coordinata x che la coordinata y, quindi avrò a che fare con una matrice di valori variabili (mentre nei due casi precendenti avevo una coordinata della matrice variabile e una fissa). Per essere delle linee verticali rette (deadline e periodo si verificheranno ogni tot secondi) devo fare in modo che i valori delle cordinate y1,y2 devono essere fissi e x1,x2 devono essere uguali, altrimenti avrei delle linee oblique. Essendo, come detto prima, la distanza tra una colonna e l’altra fissa, devo stabilire il valore di questa distanza in pixel in modo tale da far risultare il diagramma proporzionato, cioè graficamente la distanza tra un secondo e quello successivo deve essere uguale. Stabilisco come distanza tra una linea verticale e quella successiva il valore di 10 pixel; questo perché le deadline e i periodi si ripetono periodicamente ogni tot secondi. Ora, grazie ad un primo ciclo for e la variabile contatore $i, mi muovo lungo le righe orizzontali, cioè da task a task, solo dopo che un task ha terminato i suoi secondi. Memorizzo nelle variabili $d e $t il valore della deadline e del periodo del task i-esimo. Successivamente, grazie alla nidificazione di un secondo ciclo for e la variabile contatore $j, vado a creare tante linee verticali quante sono le deadline e i periodi del task i-esimo, scansionando un secondo alla volta muovendomi quindi lungo le colonne verticali (ad ogni secondo passato inoltre andavo a decrementare di 1 il valore delle variabili $d e $t). Utilizzando l’istruzione if sono andato poi a stabilire dei controlli, i quali mi hanno permesso di tracciare, non appena si verificava la condizione stabilita, le deadline e i periodi del task i-esimo. Una volta scaduto il periodo, oltre che disegnarlo graficamente, resetto (uguagliandolo al valore iniziale) i valori della deadline e del periodo del task i-esimo (perché tratto i task periodici). 110 Ricapitolando l’uso dei due cicli for: o Il primo (contatore $i) viene utilizzato per scorrere da task a task, cioè da riga a riga; o Il secondo (contatore $j) viene utilizzato per scorrere da secondo a secondo, cioè da colonna a colonna. Quindi faccio due scansioni: la prima (quella interna, variabile $j) è una scansione orizzontale, la seconda (quella esterna, variabile $i) è una scansione verticale. Con l’istruzione imageline traccio le linee verticali che vanno da 15+$i*$spazio a 15+$i*$spazio+$spazio pixel in direzione Y e si ripetono in direzione X con una frequenza in pixel di 30+10*$j (qui vario la posizione delle linee verticali variando, grazie alla presenza di $j, le coordinate x1 e x2 e la posizione delle linee orizzontali variando, grazie alla presenza di $i, le coordinate y1 e y2). Creazione delle stringhe di testo che conterranno il nome degli assi cartesiani Utilizzando l’istruzione imagestring scrivo le due stringhe di testo “task[t]” e “secondi[s]” ai lati degli assi cartesiani. La dimesione del font di queste due stringhe è 2. La prima stringa si riferisce all’asse Y, quindi le sue coordinate sono state scelte in modo tale che sia visualizzata in alto a sinistra di quest’asse; in proporzione al disegno le sue coordinate sono x=0 e y=0. La seconda stringa si riferisce all’asse X, quindi le sue coordinate sono state scelte in modo tale che sia visualizzata in basso a destra di quest’asse; in proporzione al disegno le sue coordinate sono x=620 e y=311 pixel. Creazione delle stringhe di testo che andranno ad identificare i task Grazie ad un ciclo for, che itera l’istruzione imagestring tante volte quant’è il numero dei task, vado a scrivere riga per riga il testo “task$i”. La variabile $i, venendo usata come variabile contatore del ciclo for, parte dal valore 1 e aumenta di 1 ad ogni iterazione, in modo tale da visualizzare ad ogni riga del diagramma di Gannt il valore del task i-esimo corrispondente. Queste stringhe di testo avranno la dimensione del font uguale a 1. Le coordinate in cui queste verranno visualizzate sono fisse per quanto riguarda x=7 pixel; mentre, siccome il numero di task è variabile e la distanza delle righe orizzontali proporzionale ad esso, sono variabili per quanto riguarda y=15+$i*$spazio-20 pixel (dove $i è la variabile contatore e $spazio è la variabile calcolata in precedenza che contiene il valore della distanza tra una riga orizzontale e la successiva). 111 Dopo aver terminato il disegno della griglia del diagramma di Gantt, posso passare all’ultima fase della funzione crea_gantt( ): la rappresentazione nel diagramma dei task che computano secondo per secondo. Questo giustifica la precedente creazione, nelle funzioni che hanno elaborato gli algoritmi (contenute in algoritmi_submit), dell’array $scheduling_array: il quale contiene il numero del task, secondo per secondo, che computa (scelto in base a determinati criteri di priorità). Con un ciclo for scansiono $scheduling_array secondo per secondo (dove $j è la variabile contatore dei secondi) e grazie a delle istruzioni if vado ad individuare il determinato del task iesimo che computa in quel determinato secondo. Essendo il numero dei task minimo uno e massimo dieci, prevedo dieci casi, cioè dieci controlli if, uno per ogni task nel caso si verificasse il caso limite con dieci task. Per disegnare le singole computazioni di ogni task utilizzo l’istruzione imagefilledrectangle, la quale mi permette di disegnare dei rettangoli pieni. Per ogni task ho previsto un determinato colore e una determinata posizione nella griglia (cioè le computazioni del task1 saranno disegnate esclusivamente nella prima riga, quelle del task2 nella seconda riga, e così via). Esaminando più a fondo il contenuto dell’istruzione imagefilledrectangle, essa disegna la computazione del task i-esimo: In direzione X: dal secondo j-esimo*10 (=x1) al j-esimo*10+10 pixel (=x2), dove 10 pixel è graficamente la durata di un secondo. Nota: ad x1 e x2 aggiungo 30 pixel in modo da shiftare il tutto di 30 pixel a destra, perché lo spazio lasciato libero è stato utilizzato per la visualizzazione delle stringhe di testo. In direzione Y: l’altezza del rettangolo da y1=15+$spazio/2+0*$spazio pixel (dove 0 è il valore riferito al task1, per il task2 devo mettere 1, per il task3 devo mettere 2, …, per il task10 devo mettere 9) a y2=15+1*$spazio pixel (dove 1 è il valore riferito al task1, per il task2 devo mettere 2, per il task3 devo mettere 3, …, per il task10 devo mettere 10). Nota: nella coordinata y1 vado a dividere per $spazio/2 in modo tale che il rettangolo è alto la metà rispetto allo spazio riservato ad ogni task e non copre interamente le linee che delimitano i secondi. Nota: ad y1 e y2 aggiungo 15 pixel in modo da shiftare il tutto di 15 pixel in basso, perché lo spazio lasciato libero è stato utilizzato per la visualizzazione delle stringhe di testo. 112 Nell’array $scheduling_array è contenuta pure l’informazione sul task che eventualmente va in overflow. Alla fine del ciclo for precedente, il valore di $scheduling_array contenuto al secondo j-esimo sarà quello del task in overflow; questo perché alla fine del ciclo for precedente $j è stato incrementato di 1, quindi $scheduling_array[$j] conterrà il task i-esimo al secondo successivo dell’ultima computazione, cioè il task in overflow. Anche in questo caso rappresento il task in overflow grazie all’istruzione imagefilledrectangle e per quanto riguarda la dimensione e la posizione del rettangolo di overflow le coordinate x1,x2,y1,y2 risultano essere calcolate con lo stesso ragionamento fatto in precedenza. Ho previsto, come scritto anche nelle specifiche, un colore preciso ed uguale per tutti i task in modo tale da evidenziare il concetto di overflow. Come ultima cosa da fare resta di scrivere la parte relativa alla generazione dell’output. Con l’istruzione header vado a definire il tipo di contenuto MIME dell’immagine (in questo modo il browser sarà in grado di riconoscere il tipo MIME inviato dal server): l’immagine avrà il formato png. Essendo Drupal un CMS non stampa a schermo l’immagine direttamente dal codice php; questa per essere visualizzata deve essere prima salvata su un file immagine (avente come estensione png, jpeg, gif, ecc.) e poi questo file immagine deve essere caricato da Drupal. Per salvare sul disco l’immagine appena creata, contenuta nella variabile $immagine, utilizzo l’istruzione imagepng (dato che l’immagine avrà il formato png), la quale ha i seguenti parametri: - nome della variabile che contiene l’immagine che si vuole salvare sul disco: questa variabile è contenuta in RAM e bisogna salvarla in maniera permanente in un file (nel mio caso il nome è $immagine); - percorso e nome del file in cui si deve salvare l’immagine (nel mio caso ho creato un apposita cartella, chiamata file_immagine, nella cartella del modulo “algoritmi”); - la qualità con cui salvare l’immagine: questo parametro è, di solito, caratteristico delle immagini jpeg e consiste in un indice che specifica la qualità con la quale salvare l'immagine. Tale indice può andare da un minimo di 1 (immagine di bassissima qualità praticamente inintellegibile) ad un massimo di 100 (immagine ad altissima qualità). Molto spesso valori troppo alti di questo parametro fanno crescere di molto le dimensioni (in Kb) delle immagini, senza portare reali benefici alla qualità complessiva. Consiglio quindi di utilizzare valori compresi tra 65 e 85 (75 è il valore predefinito). Quindi viene utilizzata solo per l’istruzione imagejpeg, ma non per le altre come imagepng e imagegif. Nota: ogni volta che si va a schedulare un task-set l’immagine del diagramma di gantt risultante verrà salvato sempre nello stesso file, quindi ci sarà una sovrascrittura di esso e di conseguenza non si verificherà uno spreco di memoria (ad esempio, se ogni immagine veniva salvata con un 113 nome diverso, si andavano a creare tanti file quante volte un utente andava a schedulare un taskset, di conseguenza tutti questi file andavano a riempire la memoria del disco). Un volta salvata l’immagine in un file, lo spazio di memoria RAM da esso occupato potrà essere liberato passando la variabile relativa all'immagine generata (nel mio caso $immagine) alla funzione imagedestroy che si occuperà di eliminarla. Dopo aver inserito queste ultime operazioni, con il comando return ritorno al file algoritmi.module (file da dove è stata chiamata la funzione crea_gantt( ) ), e il file crea_gantt.php può ritenersi terminato. Nota: per utilizzare la funzione crea_gantt( ) nel mio file principale algoritmi.module, devo: per prima cosa dichiararla, cioè tramite il comando include includerla all’inizio del file algoritmi.module (prima di fare qualsiasi altra operazione); successivamente, quando voglio utilizzarla in algoritmi.module, devo semplicemente chiamarla nel punto preciso di utilizzo e passargli i parametri relativi. Installazione e funzionamento del modulo algoritmi Una volta terminato di scrivere il codice del modulo, passo alla verifica, cioè la fase di testing. Ovvero vado ad installarlo su Drupal e a mostrare come esso deve essere utilizzato. Per quanto riguarda l’installazione essa viene fatta come un qualsiasi altro modulo di Drupal; cioè viene copiata la cartella “algoritmi”, contenente i file algoritmi.info, algoritmi.module, crea_gantt.php e la cartella file_immagine nella cartella C/xampplite/htdocs/drupal/sites/all/modules, dove vi sono tutti gli altri moduli di Drupal. Contenuto della cartella “algoritmi”: 114 “modules” Contenuto della cartella “modules”: Dopo aver fatto questo, bisogna andare su Drupal in Administer >> Site building >> Modules (indirizzo URL http://localhost/drupal/admin/build/modules) http://localhost/drupal/admin/build/modules e mettere la spunta sul checkbox del modulo “Algoritmi” per abilitarlo e cliccare sul bottone “Save configuration”. 115 Mentre prima dell’abilitazione nel menù rapido del frame a sinistra vi erano i collegamenti standard, ora, per come ho impostato i parametri del mio modulo, apparirà un altro link “Algoritmi “ Scheduling” che, al clic, porterà l’utente direttamente alla prima pagina di cui è composto il modulo modu “algoritmi”. Ora che il modulo è stato installato ed abilitato non resta che testarlo. testarlo Appena si clicca sul link “Algoritmi Scheduling” si verrà rediretti su una nuova pagina (con URL http://localhost/drupal/algoritmi). ). Questa è la pagina iniziale che mostra all’utente un form contenente inizialmente un solo riquadro (dove poter inserire i dati relativi a tempo di computazione, deadline e periodo dei task), un bottone che permette di aggiungere un task cioè cio un altro riquadro (assente solo quando sono presenti 10 righe della tabella, perché è il numero massimo di task stabilito), un bottone che permette di eliminare un task cioè l’ultimo riquadro (assente solo quando è presente una riga della tabella, perché è il numero minimo di task stabilito), una select che permette di scegliere un tipo di algoritmo tra quelli proposti nel menù a finesta che si apre al clic, cl un pulsante di submit “Schedula” che raccoglie i dati inseriti dall’utente e li invia al server per farli elaborare. Pagina visualizzata al clic del link “Algoritmi Scheduling” (Nota: è presente solo il bottone “Aggiungi task”): 116 Pagina ri-visualizzata dopo aver premuto una volta sul bottone “Aggiungi task” (Nota: sono presenti entrambi i bottoni “Aggiungi task” e “Rimuovi task”): Pagina ri-visualizzata dopo aver premuto nove volte consecutive sul bottone “Aggiungi task” (Nota: è presente solo il bottone “Rimuovi task”): 117 Pagina con form compilato dall’utente e pronta per essere sottomessa tramite il bottone “Schedula”: 118 Dopo che i dati sono stati inseriti dall’utente e inviati al server (cliccando sull’apposito bottone “Schedula”), essi vengono acquisiti correttamente, controllati nella loro esattezza ed elaborati. Il risultato di questa elaborazione viene poi visualizzato all’utente in maniera chiara ed esplicativa tramite un diagramma di Gantt. In quest’ultimo viene mostrato all’utente in maniera grafica (tramite un immagine) il comportamento di tutti i task secondo per secondo, quale di questi computa, quale di questi va in overflow, quale di questi computa anche dopo la sua deadline (sono dei task soft real-time), ecc. Reinserendo il task-set avente gli stessi parametri che sono stati utilizzati per l’esempio in C e per il testing fatto a mano; ottengo, per l’algoritmo EDF, il seguente diagramma di Gantt: 119 Reinserendo il task-set set avente gli stessi parametri che sono stati utilizzati per l’esempio in C e per il testing fatto a mano; ottengo, per l’algoritmo RM, il seguente diagramma di Gantt: 120 CONCLUSIONI 121 Utilizzo reale del progetto Il modulo realizzato ha come scopo principale quello di mostrare all’utente il comportamento di un algoritmo di scheduling real-time. Il grado di libertà lasciato all’utente è molto ampio per quanto riguarda l’inserimento dei dati; infatti non solo può arrivare ad inserire un massimo di 10 task ma di questi può deciderne liberamente i tre parametri caratteristici di ciascuno di essi (ovviamente i parametri, per coerenza, devono essere numeri interi e positivi e devono avere il periodo maggiore o al massimo uguale di deadline e tempo di computazione). Oltre alla composizione del task-set poi l’utente potrà, grazie ad una SELECT, scegliere con quale algoritmo di scheduling, tra quelli disponibili elaborare il task-set. A questo proposito il modulo è stato previsto anche per eventuali aggiornamenti e ampliamenti; cioè si possono facilmente aggiungere altri algoritmi di scheduling, oltre ai due già esistenti. Fare questo è semplicissimo e verrà descritto di seguito in brevi passi dove si andrà a modificare il file algoritmi.module: aggiungere alla SELECT un altro campo, ad esempio ‘#value3’, con il nome del nuovo algoritmo, in modo tale che l’utente possa selezionarlo; aggiungere alla funzione algoritmi_submit un altro controllo if(form_state[‘values’][‘tipo_alg’] == ‘nome nuovo algoritmo’), in modo tale che venga riconosciuto il nuovo algoritmo selezionato dall’utente; aggiungere, dopo il controllo if, il codice che comprenderà il nuovo algoritmo, in modo tale che i task-set vengano elaborati con le istruzioni riguardanti il nuovo algoritmo. Il risultato della schedulazione del task-set, inserito dall’utente, verrà mostrato come output con un diagramma di Gantt, il quale illustrerà le computazioni di ciascun task secondo per secondo e gli eventuali task che andranno in overlflow. Quindi un utente qualsiasi, accedendo alla piattaforma Drupal, utilizzando il suddetto modulo (se su questa è stato installato ed abilitato), sarà in grado di sapere subito qual è la schedulazione di un task-set rispetto ad un algoritmo di scheduling real-time e di prevedere eventualmente quale task e in quale secondo esso andrà in overflow. Valutazione personale Questo modulo risulterà molto utile a chiunque andrà ad utilizzarlo per vari motivi. Come prima cosa sarà utile a chi non conosce i principi su cui si basano gli algoritmi di scheduling EDF e RM e come essi lavorano, perché attraverso delle prove interattive (l’utente inserisce dei task-set in maniera casuale, giusto per farsi un’idea generale) vengono mostrate in maniera chiara e semplice i comportamenti dei vari task a seconda del diverso algoritmo di scheduling scelto, in modo tale che l’utente possa capire il funzionamento di ogni algortimo di scheduling. È utile anche a chi già è a conoscenza del funzionamento di questi algortimi, perché, utilizzando questo modulo, gli utenti principalmente possono: ottenere delle risposte rapide su come si comporta un task-set utilizzando un determinato algoritmo di scheduling, quindi ricavare subito i risultati senza doverli calcolarseli a mano; ottenere uno strumento di verifica, nel caso essi calcolino il comportamento di un task-set manualmente, quindi andranno a confrontare il risultato del loro lavoro con il risultato mostrato graficamente dal modulo, per correggere eventuali errori commessi durante la procedura manuale; 122 ottenere dei paragoni tra un algoritmo e un altro, ad esempio vedere in quale algoritmo di scheduling i task non vanno in overflow oppure, se l’overflow si presenta in ogni algoritmo utilizzato, vedere in quale algoritmo esso si presenta il più tardi possibile. Mi è piaciuto molto svolgere questo progetto perché gli argomenti trattati sono stati interessanti e soprattutto perché ho dovuto programmare un intero modulo per una piattaforma del Web. La programmazione C e quella per il Web (PHP, HTML, ecc.) sono le cose che mi sono piaciute particolarmente in questi anni di università, perché, anche se sono state per me argomenti totalmente nuovi, mi hanno appassionato da sempre e inoltre sono riuscito facilmente ad intuirle e a comprenderle. Aggiungo anche che facendo questo progetto ho accresciuto il mio sapere in campo informatico, facendo la conoscenza dei CMS, di Drupal e di come funziona, della schedulazione real-time e dei suoi principali algoritmi di scheduling. Oltre all’interesse del progetto e al piacere di svolgerlo vi è poi anche l’aspetto che riguarda l’utilità di questo. Infatti, come ho spiegato prima, questo modulo oltre che essere utile per vari scopi è anche utilizzabile da una fascia di utenti molto ampia e non è dedicato ad un settore preciso di utenza (può essere utilizzato da chi ci lavora con gli algoritmi di scheduling, da chi ne studia i comportamenti, da chi vorrebbe impararli, ecc.). Posso concludere quindi che sono soddisfatto del mio lavoro, in quanto è stato un progetto mirato, concreto, piacevole, utile a terzi e interessante. 123 Bibliografia Linguaggio C (A.Bellini, A.Guidi – McGraw-Hill 2006) Sistemi in tempo reale (G.Buttazzo - Pitagora Editrice 2006) http://www.deit.univpm.it/~dragoni/RealTime/index.html (sito internet del Prof. Aldo Franco Dragoni sui sistemi operativi in tempo reale) http://dida.laboratorium.dist.unige.it/AltreDispense/schedulazione.pdf (dispense sulla schedulazione real-time della facoltà di ingegneria di Genova) www.wikipedia.org (CMS, feed RSS, Drupal) www.espertoweb.it (feed RSS) http://www.scuolaelettrica.it/3ai/parte3/lezione17.shtml (sito internet sulla libreria GD del PHP) http://php.html.it/articoli/leggi/2896/come-creare-grafici-da-un-array-con-php-e-le-libreriegd/ (sito internet per creare grafici con la libreria GD) http://www.alessioluffarelli.it/guide_tutorial/web/ridimensionamento_immagini_thumbnail. php (sito internet che spiega come salvare un immagine su disco) http://www.alessioluffarelli.it/guide_tutorial/web/ridimensionamento_immagini_thumbnail. php (sito internet per salvare le immagini come file) Esperienza e sapere personale derivati da esami svolti in precedenza e ripetute prove nel comporre il codice http://www.drupalitalia.org/ (guida installazione Drupal) http://drupal.org/ (guida su Drupal e sui vari hook che ho utilizzato) http://www.apachefriends.org/it/xampp-windows.html#4532 (setup di Xampp) http://drupal.org/node/97368 (setup di Drupal 6) http://www.mpsoftware.dk/downloads.php (setup di phpDesigner 7) http://www.xnavigation.net/view/557/devc/download.html (setup di Dev C++) 124