Premessa.
Sono un firmwarista assuefatto dal linguaggio assembler (Atmel, microchip, ecc) che ogni tanto fa firmware anche in ANSI C.
E’ da un mese che ho acquistato un paio di chili di libri e sto cercando di imparare C# su visual studio 2010 per creare applicazioni windows form capaci di interfaccarsi alle schede che progetto. Schede che verso il PC comunicano con seriali (COM) o come ultimamente succede, con prese di rete.
Sono arrivato a un punto che non mi piace. Qualcosa che mi fa NON piacere questo genere di linguaggio e vorrei tanto che qualcuno mi dicesse: “Stai sbagliando, la cosa è più semplice e si fa così:”
Ora espongo il mio problema partendo dall’inizio.
Tra le innumerevoli applicazione che ho realizzato per “allenarmi”, quasi tutte hanno una seriale come porta di interfacciamento.
Nelle schede che progetto, come sistema di comunicazione uso un protocollo abbastanza semplice e sicuro composto da vari byte di controllo cecksum e altro. Una delle prime cose che ho fatto è stato quello di implementare la codifica e decodifica di tale protocollo a lato PC.
Un metodo prende un array di byte, lo trasforma secondo il protocollo e poi lo invia.
L’alto metodo riceve i byte codificati dalla seriale, li decodifica e li passa a un array formattati.
Fino a qui tutto OK.
Una delle cose interessanti di questo linguaggio (e penso anche di altri) è quello di poter costruirsi la propria raccolta di classi e di compilarle come delle .dll da usare in altri programmi.
Nel costruire la mia .dll che da sola facesse la codifica e decodifica dei miei dati dovevo creare una classe che fondamentalmente avesse i 2 metodi sopra citati e ovviamente istanziare una porta.
Per gestire la ricezione, utilizzo l’evento SerialDataReceivedEventArgs che ha la classe Serialport.
All’interno della mia:
codice:
private void EventoRicezionePortaSeriale(object sender, SerialDataReceivedEventArgs e)
{
//ci sono una serie di controlli dei byte ma ciò che è più importante è che quando questo evento,
//trova una trasmissione valida, deve generare un altro evento per far si che la classe principale //venga a sapere che è avvenuta una ricezione valida.
}
Per centrare l’obbiettivo mi sono fatto 3 giorni intensi sui delegati e gli eventi e apparentemente ne sono venuto a capo creando un delegate, un evento e una classe:
codice:
public delegate void MessaggioTHARGAricevutoEventHandler(object sender, MessaggioTHARGAricevutoEventArgs e);
public event MessaggioTHARGAricevutoEventHandler MessaggioTHARGAricevuto;
public class MessaggioTHARGAricevutoEventArgs : EventArgs
{
}
Devo ammettere che non ho ben capito del tutto perché funziona ma quello che volevo che facesse lo fa, ma con un problema che ora andrò ad esporre.
Nel provare se la mia .dll funziona, ho fatto un programmino stupido
Che istanzia la classe principale della mia .dll ne prende il metodo di ricezione e lo collega a una textbox per mostrare il risultato. Nel PC è connessa una mia scheda che alla pressione di un pulsante, manda un messaggio codificato attraverso la seriale.
Il programmino è semplice e lo posso postare tutto:
codice:
public partial class Form1 : Form
{
public TARGHAprotocoll Protocollo = new TARGHAprotocoll();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Protocollo.PortaComunicazione.PortName = "COM1";
Protocollo.PortaComunicazione.BaudRate = 115200;
Protocollo.PortaComunicazione.Open();
Protocollo.MessaggioTHARGAricevuto += new TARGHAprotocoll.MessaggioTHARGAricevutoEventHandler(Protocollo_MessaggioTHARGAricevuto);
textBox1.Text = "Attesa ricezione";
}
private void Protocollo_MessaggioTHARGAricevuto(object sender, MessaggioTHARGAricevutoEventArgs e)
{
textBox1.Text += "Ricezione arrivata";
}
Mettendo il break point in
textBox1.Text += "Ricezione arrivata";
All’invio del messaggio da parte della mia scheda il programma si ferma proprio li. Segno che la dll ha ricevuto il messaggio, lo ha ritenuto giustamente valido e mi ha fatta scatenare l’evento proprio come mi ero prefissato. Con il debugger ho controllato che effettivamente la dll ha pronto il messaggio formattato.
Il problema è da qui in poi. Se tento di far eseguire la semplicissima istruzione successiva
textBox1.Text += "Ricezione arrivata";
mi esce un’errore che dice che:
InvalidOperationException non è stata gestita
Operazione cross-thread non valida: è stato eseguito l’accesso al controllo ‘textbox1’ da un thread diverso da quello da cui è stata eseguita la creazione.
Ma che vuol dire?
Sono a un passo dal traguardo e mi capita anche questa?
Cercando e ricercando soluzioni a questo problema ho dovuto fare così:
codice:
private Thread demoThread = null;
delegate void SetTextCallback(string text);
private BackgroundWorker backgroundWorker1;
private void Protocollo_MessaggioTHARGAricevuto(object sender, MessaggioTHARGAricevutoEventArgs e)
{
this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
}
private void ThreadProcSafe()
{
this.SetText("Ricezione arrivata");
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
Sinceramente non so cosa ho fatto. Ho adattato un esempio trovato su msdn. La cosa però NON MI PIACE.
Possibile che debba riempire con chilometri di codice un’applicazione così stupida?
Spero vivamente di essere io che sto complicando il tutto e chiedo un’aiuto da parte vostra.
Il mio sospetto è che non vada bene creare un evento all’interno di un altro evento.
C’è un modo semplice per far funzionare la cosa.
Ho aggiunto un semplice tasto con questo evento:
codice:
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text += "Tasto premuto";
}
E funziona.
Perché questo (che tra l’altro è subito sotto) non va?
codice:
private void Protocollo_MessaggioTHARGAricevuto(object sender, MessaggioTHARGAricevutoEventArgs e)
{
textBox1.Text += "Ricezione arrivata";
}
Grazie in anticipo.