Tutorial relativa al Web-IO digitale:
Web-IO digitale con Visual C# controllo e monitoraggio
Per la creazione di applicazioni Windows, Visual C++ è stata una delle piattaforme di sviluppo più utilizzate fino a poco tempo fa. Nel frattempo sempre più programmatori lavorano con il framework .Net e creano le loro applicazioni in C# (C Sharp).
Con il seguente esempio di programma C# potete riprodurre il vostro Web-IO digitale con i suoi input e output in un’applicazione Windows. Inoltre potete collegare gli output del Web-IO.
Preparativi
- Mettere sotto tensione il Web-IO e collegare gli IO
- Collegare il Web-IO alla rete
- Assegnazione di indirizzi IP
Nel Web-IO nell’area Vie di comunicazione >> API socket Attivare i socket ASCII TCP e autorizzare gli output per l’attivazione
Collocazione dei diversi elementi di comando e oggetti di visualizzazione nel modulo
Importazione di risorse e dichiarazione di variabili locali
Come prima cosa vengono importate tutte le classi necessarie per il collegamento di rete e la GUI (Graphical User Interface).
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Text;
Successivamente i componenti dell’applicazione e le variabili importanti per un collegamento TCP vengono dichiarati variabili locali della classe e con ciò resi accessibili ai metodi della classe.
public class mainWindow : System.Windows.Forms.Form
{
private CheckBox cb_Output0;
private CheckBox cb_Output1;
private CheckBox cb_Input0;
private CheckBox cb_Input1;
private CheckBox cb_Polling_Counter;
private CheckBox cb_Polling_Outputs;
private CheckBox cb_Polling_Inputs;
private Button bt_Readall_Outputs;
private Button bt_Readall_Inputs;
private Button bt_Clear_Counter0;
private Button bt_Clear_Counter1;
private Button bt_Clearall_Counter;
private Button bt_Read_Counter0;
private Button bt_Read_Counter1;
private Button bt_Readall_Counter;
private Label lb_Counter0;
private Label lb_Counter1;
private Label lb_Intervall;
private TextBox tb_Counter0;
private TextBox tb_Counter1;
private TextBox tb_Intervall;
private Button bt_Connect;
private Button bt_Disconnect;
private TextBox tb_Password;
private TextBox tb_Port;
private TextBox tb_IP;
private GroupBox gb_ioControlBox;
private GroupBox gb_conControlBox;
private StatusBar statusBar;
private Socket client;
private int intervall;
private byte[] buffer = new byte[256];
private System.Windows.Forms.Timer counter;
private System.Windows.Forms.Timer outputs;
private Label label2;
private Label label1;
private Label label3;
private System.Windows.Forms.Timer inputs;
Avvio del programma
Inizializzazione degli elementi di comandoIl gruppo con gli elementi di comando per il Web-IO viene innanzitutto bloccato per l’uso. Non appena viene realizzato un collegamento, vengono abilitati tutti gli elementi per i quali ha senso l’abilitazione.
Il nome del relativo elemento di comando può essere derivato in base al contesto dall’elemento stesso. I primi due caratteri del nome indicano il tipo dell’elemento (cb -> checkbox, bt -> button, gb -> groupbox e tb-> textbox).
public mainWindow()
{
InitializeComponent();
gb_ioControlBox.Enabled = false;
bt_Disconnect.Enabled = false;
cb_Input0.Enabled = false;
cb_Input1.Enabled = false;
tb_Counter0.Enabled = false;
tb_Counter1.Enabled = false;
}
Controllo del collegamento
Inizializzazione del collegamentoImmettendo l’indirizzo IP del Web-IO nel campo di testo tb_IP e la porta 42280 nel campo di testo tb_Port può essere creato un collegamento azionando il pulsante bt_Connect. Se non viene inserito alcun indirizzo IP o alcuna porta, compare un messaggio prodotto dall’applicazione nella barra di stato.
Creazione del collegamentoPer poter ora stabilire un collegamento TCP viene creata e inizializzata una nuova socket. L’indirizzo IP e il numero di porta vengono raccolti in un endpoint IP. Aprendo il BeginConnect viene assegnato l’endpoint IP.
Affinché il programma possa funzionare in modo asincrono, non aspetta gli eventi ma funziona con routine di callback. I metodi di callback vengono inizializzati se viene avviato e richiamato un processo, nel caso in cui compaia il corrispondente evento, pertanto ad es. nella creazione del collegamento, all’invio o alla ricezione.
private void bt_Connect_Click(object sender, System.EventArgs e)
{
try
{
if((tb_IP.Text != "") && (tb_Port.Text != ""))
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(tb_IP.Text), int.Parse(tb_Port.Text));
client.BeginConnect(ipep, new AsyncCallback(connectCallback), client);
}
else
statusBar.Text = "IP and Port needed!";
}
catch(SocketException)
{ statusBar.Text = "Connection not possible!"; }
}
Di seguito viene rappresentata la routine di callback che viene richiamata alla realizzazione del collegamento. L’applicazione comincia subito a prepararsi a ricevere. A tale scopo viene inizializzata la routine di callback "receiveCallback" che viene spiegata dettagliatamente più avanti.
private void connectCallback(IAsyncResult ar)
{
try
{
client = (Socket) ar.AsyncState;
client.EndConnect(ar);
connectupdatedisplay();
intervall = 1000;
client.BeginReceive(buffer, 0, 255, SocketFlags.None, new AsyncCallback(receiveCallback), client);
}
catch(Exception)
{ disconnect();
}
}
Inoltre avviando la procedura connectupdatedisplay()
vengono autorizzati tutti gli elementi di comando necessari dell’applicazione e viene disattivato il Connect Button.
Nella versione attuale di C# non è più consentito accedere da un thread (qui la funzione Callback) a elementi grafici di un altro thread (forma).
Qui offre una soluzione l’uso di delegate e invoke.
Di seguito viene rappresentata la routine di callback che viene richiamata alla realizzazione del collegamento. L’applicazione comincia subito a prepararsi a ricevere. A tale scopo viene inizializzata la routine di callback "receiveCallback" che viene spiegata dettagliatamente più avanti.
delegate void delegateSub();
private void connectupdatedisplay()
{
if (InvokeRequired)
{
BeginInvoke(new delegateSub(connectupdatedisplay), new object[] { });
return;
}
statusBar.Text = "Connected!";
gb_ioControlBox.Enabled = true;
bt_Disconnect.Enabled = true;
bt_Connect.Enabled = false;
}
Disinserzione del collegamento
Il collegamento rimane fino a quando non viene terminato dall’utente facendo clic sul pulsante Disconnect oppure dal Web-IO.
private void bt_Disconnect_Click(object sender, System.EventArgs e)
{
try
{
client.Shutdown(SocketShutdown.Both);
client.Close();
disconnectupdatedisplay();
}
catch (Exception)
{
statusBar.Text = "Not able to disconnect!";
}
}
Anche se la connessione del Web-IO viene smantellata, l’applicazione deve chiudere il socket utilizzato ed è necessario rimettere tutti gli elementi nella loro posizione di partenza. Non deve più essere possibile azionare il pulsante Disconnect.
private void disconnect()
{
try
{
client.Shutdown(SocketShutdown.Both);
client.Close();
disconnectupdatedisplay();
}
catch (Exception)
{
statusBar.Text = "Not able to disconnect!";
}
}
Per un accesso sicuro in termini di thread il ripristino degli elementi del modulo è stato trasferito alla procedura disconnectupdatedisplay()
.
private void disconnectupdatedisplay()
{
if (InvokeRequired)
{
BeginInvoke(new delegateSub(disconnectupdatedisplay), new object[] { });
return;
}
statusBar.Text = "Disconnected!";
gb_ioControlBox.Enabled = false;
bt_Disconnect.Enabled = false;
bt_Connect.Enabled = true;
}
Ora il collegamento è stato di nuovo terminato e l’applicazione viene riportata al suo stato iniziale.
Utilizzo e comunicazione della parte client
Non appena viene realizzato un collegamento con il Web-IO, l’utente può inviare comandi al Web-IO utilizzando i corrispondenti elementi del programma.
Invio di comandi
All’invio di un messaggio al Web-IO viene richiamata una routine di Callback esattamente come alla ricezione.
private void sendCallback(IAsyncResult ar)
private void sendCallback(IAsyncResult ar)
{
try
{
Socket tmp_client = (Socket) ar.AsyncState;
int bytessend = tmp_client.EndSend(ar);
}
catch(Exception)
{
statusBar.Text = "Error while sending";
}
}
Impostazione degli output
Gli output del Web-IO possono essere commutati per mezzo delle due caselle di spunta cb_Output0 e cb_Output1. Quando si fa clic sulla casella di spunta essa attiva un’azione. In base all’eventuale preimpostazione della casella di spunta, l’output viene impostato su ON o su OFF.
private void cb_Output0_CheckedChanged(object sender, System.EventArgs e)
{
if(cb_Output0.Checked)
send("GET /outputaccess0?PW=" + tb_Password.Text + "&State=ON&");
else
send("GET /outputaccess0?PW=" + tb_Password.Text + "&State=OFF&");
}
private void cb_Output1_CheckedChanged(object sender, System.EventArgs e)
{
if(cb_Output1.Checked)
send("GET /outputaccess1?PW=" + tb_Password.Text + "&State=ON&");
else
send("GET /outputaccess1?PW=" + tb_Password.Text + "&State=OFF&");
}
Interrogazione dello stato degli output/input
private void bt_Readall_Outputs_Click(object sender, System.EventArgs e)
{
send("GET /output?PW=" + tb_Password.Text + "&");
}
private void bt_Readall_Inputs_Click(object sender, System.EventArgs e)
{
send("GET /input?PW=" + tb_Password.Text + "&");
}
Interrogazione o cancellazione dei counter
Il seguente metodo invia una richiesta a un determinato counter e richiede una risposta con lo stato attuale del contatore o cancella lo stato del contatore di un counter.
private void bt_Read_Counter0_Click(object sender, System.EventArgs e)
{
send("GET /counter0?PW=" + tb_Password.Text + "&");
}
private void bt_Read_Counter1_Click(object sender, System.EventArgs e)
{
send("GET /counter1?PW=" + tb_Password.Text + "&");
}
private void bt_Clear_Counter0_Click(object sender, System.EventArgs e)
{
send("GET /counterclear0?PW=" + tb_Password.Text + "&");
}
private void bt_Clear_Counter1_Click(object sender, System.EventArgs e)
{
send("GET /counterclear1?PW=" + tb_Password.Text + "&");
}
Naturalmente tutti gli stati dei counter possono essere interrogati o cancellati anche con un comando.
private void bt_Readall_Counter_Click(object sender, System.EventArgs e)
{
send("GET /counter?PW=" + tb_Password.Text + "&");
}
private void bt_Clearall_Counter_Click(object sender, System.EventArgs e)
{
send("GET /counterclear?PW=" + tb_Password.Text + "&");
}
Ricezione di dati del Web-IO e valutazione
Struttura dei dati di ricezione- Tutti i comandi e le richieste al Web-IO vengono confermati con una stringa di risposta. Le risposte hanno una struttura specifica in base al tipo.
- Per gli output: output;<valore binario dello stato degli output in formato esadecimale>
- Per un output specifico: outputx;<ON o OFF>
- Per gli input: input;<valore binario dello stato degli input in formato esadecimale>
- Per un input specifico: inputx;<ON o OFF>
- Quindi segue la stringa di risposta per un counter che appare come segue.
- Counter: counterx;<stato del conteggio decimale>
- oppure counter;<stato del conteggio decimale 0 >; <stato del conteggio decimale 0 >; ... se tutti i counter devono essere letti in un’unica volta.
- Tutte le stringe di risposta terminano con 0 byte.
Nella nostra applicazione per la ricezione di un tale messaggio viene usato il medoto receiveCallback()
. La cosa particolare di questo metodo è l’avvio controllato dagli eventi. Esso avviene non appena il Web-IO invia dati all’applicazione.
private void receiveCallback(IAsyncResult ar)
{
int bytesRead;
string rcv = string.Empty;
try
{ bytesRead = client.EndReceive(ar);
rcv = Encoding.ASCII.GetString(buffer);
client.BeginReceive(buffer, 0, 255, SocketFlags.None, new AsyncCallback(receiveCallback), client);
}
catch (Exception)
{
bytesRead = 0;
}
if (bytesRead == 0)
{
if (client.Connected)
{
disconnect();
}
}
else if (rcv != null)
{
IOupdatedisplay(rcv.Substring(0, bytesRead - 1));
}
}
Valutazione dei dati ricevuti
La stringa di risposta viene letta e assegnata alla procedura sicura in termini di thread IOupdatedisplay
.
delegate void delegateIOSub(string rcv);
private void IOupdatedisplay(string rcv)
{
if (InvokeRequired)
{
BeginInvoke(new delegateIOSub(IOupdatedisplay), new object[] { rcv });
return;
}
if (rcv[0] == ’o’)
{
int i = Int32.Parse(rcv.Substring(7), System.Globalization.NumberStyles.HexNumber);
if((i & 1) == 1)
cb_Output0.Checked = true;
else
cb_Output0.Checked = false;
if((i & 2) == 2)
cb_Output1.Checked = true;
else
cb_Output1.Checked = false;
}
if(rcv[0] == ’i’)
{
int i = Int32.Parse(rcv.Substring(6), System.Globalization.NumberStyles.HexNumber);
if ((i & 1) == 1)
cb_Input0.Checked = true;
else
cb_Input0.Checked = false;
if((i & 2) == 2)
cb_Input1.Checked = true;
else
cb_Input1.Checked = false;
}
if(rcv[0] == ’c’)
{
if(rcv[7] == ’0’)
tb_Counter0.Text = rcv.Substring(9);
if(rcv[7] == ’1’)
tb_Counter1.Text = rcv.Substring(9);
if(rcv[7] == ’;’)
readAndClearCounter(rcv.Substring(8));
}
}
Poiché tutti gli stati dei counter possono essere letti o azzerati con un comando, occorre implementare ancora un metodo che elabori la stringa di risposta del Web-IO in modo tale che a ogni counter nell’applicazione venga assegnato lo specifico stato di contatore
private void readAndClearCounter(string data)
{
int j = 0;
string[] counter = new string[2];
for(int i = 0; i < data.Length; i++)
{
if((data[i].CompareTo(’;’)) == 0)
j++;
else
counter[j] += data[i].ToString();
}
tb_Counter0.Text = counter[0];
tb_Counter1.Text = counter[1];
}
7. Polling
Interrogazione ciclica di determinati valoriÈ auspicabile che lo stato di un singolo componente si aggiorni da sé e con ciò che l’applicazione presenti sempre il suo stato corrente. A tale scopo viene utilizzato in questo programma un timer che invia ciclicamente interrogazioni al Web-IO in un intervallo di tempo determinato dall’utente.
L’intervallo di tempo può essere stabilito nel campo tb_Intervall.
Naturalmente viene intercettato anche il caso in cui l’utente ha inserito un’indicazione senza senso come ad es. un valore di tempo negativo.
private void tb_Intervall_TextChanged(object sender, System.EventArgs e)
{
try
{
if(Convert.ToInt32(tb_Intervall.Text) > 0)
{
intervall = Convert.ToInt32(tb_Intervall.Text);
statusBar.Text = "New range: " + intervall.ToString() + " ms!";
}
else
statusBar.Text = "Only positiv Integer allowed!";
}
catch(Exception)
{
statusBar.Text = "Only positiv Integer allowed!";
}
}
Per eseguire anche l’interrogazione ciclica degli stati del Web-IO, definito anche polling, è possibile scegliere tra polling degli output, degli input o dei counter.
Per ogni variante di polling viene inizializzato un proprio timer.
Se si attiva la casella di spunta cb_polling_outputs, il polling viene utilizzato sugli output. A tale scopo viene inizializzato il corrispondente timer.
private void cb_Polling_Outputs_CheckedChanged(object sender, System.EventArgs e)
{
if(cb_Polling_Outputs.Checked)
{
outputs = new System.Windows.Forms.Timer();
outputs.Interval = intervall;
outputs.Start();
outputs.Tick += new EventHandler(timer_handler);
}
else
outputs.Stop();
}
Lo stesso vale per gli input e i counter.
private void cb_Polling_Inputs_CheckedChanged(object sender, System.EventArgs e)
{
if(cb_Polling_Inputs.Checked)
{
inputs = new System.Windows.Forms.Timer();
inputs.Interval = intervall;
inputs.Start();
inputs.Tick += new EventHandler(timer_handler);
}
else
inputs.Stop();
}
private void cb_Polling_Counter_CheckedChanged(object sender, System.EventArgs e)
{
if(cb_Polling_Counter.Checked)
{
counter = new System.Windows.Forms.Timer();
counter.Interval = intervall;
counter.Start();
counter.Tick += new EventHandler(timer_handler);
}
else
counter.Stop();
}
In questo metodo l’evento attuale del timer, appena segnalato, viene rilevato e assegnato a una determinata azione.
private void timer_handler(object sender, System.EventArgs e)
{
if(sender == counter) bt_Readall_Counter_Click(sender, e);
if(sender == outputs) bt_Readall_Outputs_Click(sender, e);
if(sender == inputs) bt_Readall_Inputs_Click(sender, e);
}
Il programma esempio supporta tutte le comuni funzioni del Web-IO nella modalità stringa di comando, ottimizzata per il Web-IO 2x input digitale, 2x output digitale. Gli altri modelli Web-IO devono eventualmente essere adattati al programma. Ulteriori esempi di programma per la programmazione socket sono riportati nelle pagine dei tool per il Web-IO. Una descrizione dettagliata sull’interfaccia socket dei modelli Web-IO digitali è riportata nel manuale di riferimento.