Swift by example - creazione software per controllo irrigazione (o altro...fate voi!)

Qui potrete scambiare pareri, consigli, suggerimenti di programmazione su Swift, Java, SQL, C++, Pascal, VB, Basic, etc...

Moderatore: ModiMaccanici

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

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 :shock: ... interessante :?: ... o ... boh decidete voi (come sempre del resto :) ).
Dato che, un immagine vale più di 1000 parole Immagine

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

Elenco dei programmi impostati
Immagine

Programmazione giornaliera
Immagine

Programmazione personalizzata
Immagine

Funzionamento manuale
Immagine

Informazioni
Immagine

Avatar utente
mattleega
Stato: Non connesso
Pestifero
Pestifero
Avatar utente
Iscritto il: mar, 30 ott 2007 19:20
Messaggi: 18959

Top

Wow!

Solo una domanda:
quando proporrai un sw in grado di gestire la moglie non più occupata con l'irrigazione?
...

L’inerzia soddisfatta dei cittadini è all’origine di quella incredibile malattia che è la servitù volontaria

E se vivremo, sarà per calpestare i re.

Avatar utente
Scialla
Stato: Non connesso
Expert
Expert
Avatar utente
Iscritto il: mer, 12 ago 2009 19:27
Messaggi: 17347
Località: Torino

Top

Bravissima!!!

Con questo ti aggreghi alla mia mentalità domotica....

Con HomeAssistant si fa questo e molto altro, in YAML, senza bisogno di programmare

Poi ModBus è arabo per me e quindi ancora complimenti
 Il futuro (Apple)? Nammerda!

Di un costoso Mac si può fare a meno, di macOS no... (cit. fax)

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

Ok, pronti a partire.
Qui la struttura logica dell'applicazione

Immagine

Immagine

Immagine

Immagine

Immagine

Se non lo avete scaricate Xcode (io sto usando la versione 12.5) e seguite il video per creare il progetto di partenza (swift e macOS).

Selezionare App MacOS, impostare la versione target a macOS 11.0 e attivare nella sezione App Sandbox Incoming e Outcoming connection.

Dal menu Product selezionare Build for running, se tutto ok, compare la finestra dell'app. Bene! primo step a posto.

qui il video riepilogativo



Per oggi è tutto. A presto.

Avatar utente
franzphone
Stato: Non connesso
Expert
Expert
Avatar utente
Iscritto il: lun, 08 dic 2014 19:07
Messaggi: 1103
Località: Sierre

Top

Complimenti e grazie !
MacBook Pro 13" M1
16GB RAM 512GB SSD
macOS Big Sur 11.6

Viviamo in questo mondo per imparare e per illuminarci l'un l'altro.
Wolfang Amadeus Mozart.

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

La struttura del modello di base che Xcode crea per noi è così rappresentato

Immagine

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

Immagine

Concludo con la personalizzazione della classe, con l'aggiunta del metodo

Codice: Seleziona tutto

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.


Download progetto
https://www.germinara.it/disco/irriga/irriga_prj_01.zip
Buona cena.

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

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

Immagine

Immagine

Immagine

Immagine

Immagine

Immagine

Immagine

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.

Per approfondire https://developer.apple.com/documentati ... es=l_1_2_2

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.

Per chiarire meglio, nella nostra classe

Codice: Seleziona tutto

mainViewController
inseriremo l'outlet

Codice: Seleziona tutto

@IBOutlet weak var btnConfigura: NSButton! //Collegamento all'elemento grafico del pulsante
e l'action

Codice: Seleziona tutto

 @IBAction func btnConfigClicked(_ sender: Any) //Metodo eseguito quando si effettua il click sul pulsante


Seguono le videate dei vari passaggi per la creazione della classe personalizzata

Immagine

Immagine

Immagine

Immagine

Immagine

Immagine

A questo punto la cosa importante è cambiare il tipo della classe che sarà istanziata (creata a runtime) dall'applicazione quando andrà in esecuzione

Immagine

Quindi per vedere cosa succede, aggiungiamo alla nostra classe delle istruzioni

Codice: Seleziona tutto

print()
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).

Immagine

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.

Codice: Seleziona tutto

viewDidLoad
e

Codice: Seleziona tutto

btnConfigClicked
.



Nota1: il metodo

Codice: Seleziona tutto

viewDidLoad
è stato creato in automatico da Xcode quando abbiamo creato la classe derivandola dalla NSViewController, e presenta la keyword

Codice: Seleziona tutto

override
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

Codice: Seleziona tutto

super.viewDidLoad()
che chiama lo stesso metodo (inteso come nome) della classe

Codice: Seleziona tutto

super
ossia della classe base dalla quale deriva la nostra, ossia la NSViewController.

Nota2: il metodo

Codice: Seleziona tutto

btnConfigClicked
è 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

Codice: Seleziona tutto

@IBAction
che identifica la funzione come Action (collegata quindi all'oggetto pulsante). La keyword

Codice: Seleziona tutto

@IBOutlet
è stata generata sempre in automatico effettuando sempre la stessa operazione di trascinamento da IB verso il testo della classe, ma selezionando outlets.

Per approfondire la classe usata per rappresentare il pulsante https://developer.apple.com/documentati ... t/nsbutton

Qui il link del progetto allo stato attuale
http://germinara.it/disco/irriga/irriga_prj_02.zip

Buon we.

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

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

Immagine

Ora siamo pronti per modificare il metodo

Codice: Seleziona tutto

@IBAction func btnConfigClicked(_ sender: Any) 
della classe mainViewController per visualizzare il view controller relativo alla configurazione.

Codice: Seleziona tutto

 //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)
        
    }
Immagine
Nota: nell'immagine c'è un refuso (sequito invece di seguito...o forse che sono proprio un pò analfabeta... :lol: 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

Codice: Seleziona tutto

@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)
    }
Immagine

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

Immagine

Immagine

Il link per scaricare la versione corrente del progetto
https://www.germinara.it/disco/irriga/irriga_prj_03.zip

e link di approfondimento

https://developer.apple.com/documentati ... t/nsswitch

https://developer.apple.com/documentati ... boardsegue

Buona serata.

Germinara
Stato: Non connesso
Apprendista Maccanico
Apprendista Maccanico
Iscritto il: dom, 06 giu 2010 21:22
Messaggi: 33

Top

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
Immagine

Qui il video relativo al funzionamento dell'interfaccia utente


Qui il link del progetto allo stato attuale.
https://www.germinara.it/disco/irriga/irriga_prj_04.zip

Buona serata.