Uhm...per chi ha voglia di smanettare un pò sui bit e su schede elettroniche, magari imparando o rinfrescando un pò di Swift, e magari, come me, vuole eliminare la centralina di irrigazione del giardino, seguire queste puntate potrebbe essere ... divertente ... interessante ... o ... boh decidete voi (come sempre del resto ).
Dato che, un immagine vale più di 1000 parole
Vi presento Irriga!
Il software vi consentirà di gestire la programmazione giornaliera di un massimo di 4 zone di irrigazione (in realtà è una gestione schedulata di un massimo di 4 uscite Digitali o Relè, quindi collegateci quello che volete gestire).
Per il colloquio con il dispositivo elettronico ho usato il protocollo modbus ed un modulo wifi con 4 uscite a Relè, ma potrete implementare quello che più vi piace in funzione dell'hardware che, eventualmente, utilizzerete.
Quindi, riassumendo, il progetto così com' è ha queste caratteristiche
Nome applicazione : Irriga
Sistema Operativo : macOS Big Sur
Linguaggio programmazione : Swift e C
Protocollo comunicazione: modbus TCP/IP
Hardware I/O supportato: qualsiasi dispositivo Ethernet cablata o Wireless con 4 canali di uscita con protocollo modbus TCP/IP
Detto ciò, dato che nelle varie puntate, saranno presentate le varie parti software che andremo a realizzare, al termine di queste puntate, sarete in grado di personalizzare qualsiasi aspetto di questo progetto per creare quello che vi necessità.
Se invece volete usarlo così com'è per gestire un dispositivo (a patto che supporti il protocollo modbus TCP/IP) e che abbia sino a 4 canali digitali o relè di uscita, potete utilizzarlo liberamente e gratuitamente.
Vi lascio con le videate del software, poi dalla prossima puntata inizieremo con la descrizione dell'architettura software.
A presto, spero...
Francesco
MacBook Pro 13" M1 16GB RAM 512GB SSD macOS Sequoia 15.0.1 iPhone 13 mini 128GB iOS 18.0.1 Viviamo in questo mondo per imparare e per illuminarci l'un l'altro. - Wolfang Amadeus Mozart.
La struttura del modello di base che Xcode crea per noi è così rappresentato
Una delle cose, a mio avviso importanti, nello sviluppo di applicazioni è mantenere il codice ordinato e dove necessario commentato.
La prima cosa che faccio è quindi di creare una cartella all'interno della struttura del progetto di Xcode in cui mettere insieme tutti i files inerenti un determinato argomento o specifica funzionalità.
La prima classe che incontriamo e che andiamo a personalizzare è la Classe Delegata dell'applicazione, ossia la classe che risponde alle operazioni effettuate genericamente sull'applicazione (es. quando la si lancia, quando viene chiusa ecc.)
per una completa descrizione consultare https://developer.apple.com/documentati ... ondelegate
La classe appDelegate, deriva (ossia eredita le proprietà ed i metodi dalle classi NSObject e NSApplicationDelegate
Concludo con la personalizzazione della classe, con l'aggiunta del metodo
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool{
return true //vero=> l'applicazione può essere chiusa quando si chiude l'ultima finestra
}
Provare ad implementare tale metodo e verificare il diverso comportamento alla chiusura della finestra, con e senza tale metodo.
Iniziamo ora a personalizzare il progetto. La prima cosa è cambiare il titolo della finestra dell'applicazione e modificarne qualche proprietà in base alle proprie esigenze. Per la personalizzazione utilizziamo Interface Builder (IB) integrato in Xcode, selezionare il file main.storyboard ed iniziare a prendere confidenza con le videate di IB
Fatto questo, provate a compilare e mandare in esecuzione in programma, ora la finestra presenta il nuovo titolo e non è ridimensionabile.
Ora ci occupiamo di aggiungere gli elementi nella nostra interfaccia, ossia i quattro pulsanti che saranno usati per le quattro funzioni previste (configurazione, test zone, funzionamento manuale, gestione della programmazione).
Qui il video dei vari passaggi, in sintesi si aggiungono gli elementi (viste, pulsanti, icone, tabelle ecc.) selezionandoli dalla libreria presente in IB e si trascinano sulla vista (view) nella quale farli comparire. Al momento la nostra interfaccia dell'applicazione è molto semplice, abbiamo una finestra, che ha un legame con un view controller che rappresenta il suo contenuto. A sua volta il view controller contiene una vista (view) ed è proprio all'interno di questa vista che inseriamo
gli altri elementi (comprese eventuali altre viste se dovessero servire). La struttura, visibile nell'albero a sinistra della zona di disegno, è gerarchica.
Per semplificare la visualizzazione e la gestione delle finestre, view controller e view, interface builder raggruppa gli elementi che contengono dei view controller in "scene".
Una volta posizionato e dimensionato i quattro pulsanti, compiliamo e mandiamo in esecuzione, abbiamo la nostra GUI (interfaccia grafica utente) per la nostra applicazione.
Se non siete troppo stanchi, vediamo ancora come fare in modo che premendo un pulsante l'applicazione lo riconosca e "faccia" qualcosa.
Per fare questo dobbiamo creare una nostra classe che sarà derivata (ossia eredità tutte le caratteristiche) della classe NSViewController, la chiameremo mainViewController.
Nella nostra classe mainViewController, andremo a creare un Outlet che è un collegamento tra un oggetto dell'interface builder (es. il pulsante configura) con la nostra variabile nella classe (se servirà per accedere all'elemento pulsante, di tipo NSButton, dal programma ad esempio per abilitare o meno il pulsante, o per nasconderlo ecc.). Un altra cosa che andremo a creare è una Action che è un collegamento tra un evento generato dall'oggetto dell'interfaccia (es. il pulsante configura quando viene premuto) e un metodo che sarà eseguito dal codice della nostra classe.
che ci consentono di vedere nell'area di debug il testo che decidiamo noi. (esempio quando la nostra classe viene creata o quello che accade ogni volta che premiamo il pulsante).
Qui il video con le varie attività necessarie per effettuare i collegamenti dell'outlet e della action di cui abbiamo parlato in precedenza, direttamente da IB e dell'implementazione delle funzioni di debug (print) dei due metodi implementati dalla nostra classe.
perché "sovrascrive" il comportamento della class base (NSViewController). Infatti da notare che nel corpo della funzione (tra aperta graffa e chiusa graffa) c'è l'istruzione
è stata creata in automatico quando da IB abbiamo trascinato mantenendo premuto CTRL+Clic il puntatore del mouse dal oggetto pulsante configura nell'vesto della classe mainViewController (tramite l'assistente di IB). Da notare la keyword
è stata generata sempre in automatico effettuando sempre la stessa operazione di trascinamento da IB verso il testo della classe, ma selezionando outlets.
E' giunto il momento di dedicarci alle attività necessarie per visualizzare le varie finestre quando si premono i pulsanti dalla finestra principale della nostra applicazione.
Per visualizzare la finestra relativa alla configurazione dei parametri che vogliamo gestire per l'interfaccia modbus (indirizzo ip, porta e i 4 registri delle zone) occorre per prima cosa aggiungere nello storyboard, tramite interface builder, un view controller che andremo a personalizzare.
Una volta trascinato l'oggetto view controller dentro area di disegno dello storyboard, occorre inserire gli oggetti che vogliamo far comparire nella finestra, quindi delle etichette di testo es. Indirizzo IP e i rispettivi campi di input.
Servirà inoltre un pulsante di Conferma ed uno di uscita.
Tutti questi elementi dovranno essere collegati al codice del nostro programma e per fare questo dobbiamo creare una nostra classe derivata (ConfigurazioneViewController) da view controller ed andare ad indicare in interface builder che il view controller è una istanza della nostra nuova classe.
Una volta completata questa attività possiamo procedere a creare e collegare i vari elementi dell'interfaccia grafica.
Nei video che seguiranno, ho usato diversi metodi per fare questi collegamenti, usando l'assistente di IB (interface builder) oppure creando prima nel codice le variabili ed i metodi che mi servivano nella classe ConfigurazioneViewController e poi successivamente li ho collegati in IB.
Una volta completati i collegamenti degli Outlets e delle Actions, rimane da decidere come fare a far apparire il nostro view controller di configurazione quando si preme il pulsante Configurazione.
Per fare questo possiamo operare in due modalità: tramite codice nel programma oppure tramite storyboard ed IB.
Per la visualizzazione del view controller di Configurazione ho utilizzato la modalità tramite codice da programma, per il view controller Test zone ho invece utilizzato la modalità tramite IB.
Per visualizzare il view controller tramite codice, occorre prima di tutto, definire in IB un valore che è un Identificativo univoco dell'oggetto view controller che vogliamo istanziare via codice, tale valore va indicato nel campo Storyboard ID e nel nostro caso ho messo configuraID
//Metodo eseguito quando si effettua il click sul pulsante
@IBAction func btnConfigClicked(_ sender: Any) {
print("premuto pulsante Config!")
//Metodo 1: visualizzazione del view controller da codice
// richiede di impostare un ID (es. configuraID ) nello
// storyboard sull'oggetto view controller nel campo
// Storyboard ID
let configurazioneViewController = (NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "configuraID")) as! ConfigurazioneViewController
self.presentAsSheet(configurazioneViewController)
}
Nota: nell'immagine c'è un refuso (sequito invece di seguito...o forse che sono proprio un pò analfabeta... pardon! ... anche per tutti quelli che non vedo...)
Oltre a visualizzare la finestra di configurazione, occorre anche pensare a come chiudere la finestra una volta che non ci serve più per tornare alla finestra precedente, per fare questo andiamo ad implementare la chiamata del metodo dismiss(self) nella classe ConfigurazioneViewController.
Modifichiamo quindi l'azione che viene eseguita quando si preme il pulsante Esci del view controller
@IBAction func btnEsciClicked(_ sender: Any) {
//Per chiudere la visualizzazione del view controller
//si usa il metodo dismiss(viewController: NSViewController)
// nota: self => è instanza di questa classe, identifica se stesso.
dismiss(self)
}
Compiliamo e mandiamo in esecuzione per verificare che tutto funzioni regolarmente.
Vi lascio con il video relativo all'implementazione del view controller TestzoneViewController che deve comparire quando si preme il pulsante Test zone dalla finestra principale. In questo caso la visualizzazione è ottenuta utilizzando direttamente IB ed il collegamento di tipo NSStoryBoardSegue.
Dall'interface builder, nello storyboard, possiamo vedere se i view controller sono istanziati tramite codice oppure tramite collegamenti di tipo Segue
Con le indicazioni viste la scorsa volta ho provveduto a realizzare la struttura completa dell'interfaccia utente in Interface Builder e a definire le varie classi che si occuperanno della gestione delle diverse funzionalità dell'applicazione.
Qui la sintesi della struttura del progetto allo stato attuale
Qui il video relativo al funzionamento dell'interfaccia utente