c# winforms serial-port async-await

Actualización de GUI C#y comunicación de puerto serie asíncrono



winforms serial-port (1)

Debe usar una máquina de estado y delegates para lograr lo que está tratando de hacer. Vea el código a continuación, le recomiendo hacer todo esto en un hilo separado que no sea Main. Realiza un seguimiento del estado en el que se encuentra y, cuando obtiene una respuesta, la analiza con la función de devolución de llamada correcta y, si es lo que espera, pasa al siguiente estado de comando de envío.

private delegate void CallbackFunction(String Response); //our generic Delegate private CallbackFunction CallbackResponse; //instantiate our delegate private StateMachine currentState = ATRHBPCalStateMachine.Waiting; SerialPort sp; //our serial port private enum StateMachine { Waiting, SendCmd1, Cmd1Response, SendCmd2, Cmd2Response, Error } private void do_State_Machine() { switch (StateMachine) { case StateMachine.Waiting: //do nothing break; case StateMachine.SendCmd1: CallbackResponse = Cmd1Response; //set our delegate to the first response sp.Write("Send first command1"); //send our command through the serial port currentState = StateMachine.Cmd1Response; //change to cmd1 response state break; case StateMachine.Cmd1Response: //waiting for a response....you can put a timeout here break; case StateMachine.SendCmd2: CallbackResponse = Cmd2Response; //set our delegate to the second response sp.Write("Send command2"); //send our command through the serial port currentState = StateMachine.Cmd2Response; //change to cmd1 response state break; case StateMachine.Cmd2Response: //waiting for a response....you can put a timeout here break; case StateMachine.Error: //error occurred do something break; } } private void Cmd1Response(string s) { //Parse the string, make sure its what you expect //if it is, then set the next state to run the next command if(s.contains("expected")) { currentState = StateMachine.SendCmd2; } else { currentState = StateMachine.Error; } } private void Cmd2Response(string s) { //Parse the string, make sure its what you expect //if it is, then set the next state to run the next command if(s.contains("expected")) { currentState = StateMachine.Waiting; backgroundWorker1.CancelAsync(); } else { currentState = StateMachine.Error; } } //In my case, I build a string builder until I get a carriage return or a colon character. This tells me //I got all the characters I want for the response. Now we call my delegate which calls the correct response //function. The datareceived event can fire mid response, so you need someway to know when you have the whole //message. private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { string CurrentLine = ""; string Data = serialPortSensor.ReadExisting(); Data.Replace("/n", ""); foreach (char c in Data) { if (c == ''/r'' || c == '':'') { sb.Append(c); CurrentLine = sb.ToString(); sb.Clear(); CallbackResponse(CurrentLine); //calls our correct response function depending on the current delegate assigned } else { sb.Append(c); } } }

SendCmd1 esto en un trabajador en segundo plano, y cuando presiona un botón o algo, puede establecer el estado actual en SendCmd1 .

Presione el botón

private void buttonStart_Click(object sender, EventArgs e) { if(!backgroundWorker1.IsBusy) { currentState = StateMachine.SendCmd1; backgroundWorker1.RunWorkerAsync(); } }

Trabajador de fondo hacer evento de trabajo

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while (true) { if (backgroundWorker1.CancellationPending) break; do_State_Machine(); Thread.Sleep(100); } }

editar: puede usar invoke para actualizar la GUI desde su hilo de trabajo en segundo plano.

this.Invoke((MethodInvoker)delegate { image = Image.FromFile(path); //do some stuff on image using Graphics, adding texts etc. picturebox1.Image = image; });

Estoy tratando de crear una aplicación que se comunique con el hardware a través del puerto serie e informe los resultados a la interfaz gráfica de usuario.

Actualmente, KeyEvents realiza el movimiento a través de la GUI que activa el dibujo de la próxima "página" de la GUI. Sin embargo, en un paso (después de presionar la tecla) necesito dibujar una nueva página y enviar algunos comandos a través del puerto serie.

El envío del comando se realiza a través de:

port.Write(data, 0, data.Length);

Luego espero la respuesta esperando que se active DataReceivedHandler: solo señala que hay datos en espera y los datos se están procesando en otro método.

Al principio, simplemente puse el comando de envío y recepción en la función de dibujo de la página después de las "partes de dibujo", sin embargo, se quedó atascado: los datos se estaban transfiriendo, pero la página no se dibujó, estaba congelada.

Luego hice un método asíncrono:

private async void SendData() { await Task.Run(() => serialClass.SendAndReceive(command)); // process reply etc. }

Que se usa así:

public void LoadPage() { image = Image.FromFile(path); //do some stuff on image using Graphics, adding texts etc. picturebox1.Image = image; SendData(); }

Funciona bien, sin embargo, necesito "volver a cargar" la página (para volver a llamar a LoadPage). Si lo hago dentro del método asíncrono como este:

private async void SendData() { await Task.Run(() => serialClass.SendAndReceive(command)); // process reply etc. LoadPage(); }

Entonces, obviamente, la imagen no se actualizará, aunque los datos se enviarán a través del puerto serie. ¿Es posible verificar de alguna manera si la función asincrónica se terminó y desencadenar un evento en el que pueda volver a cargar la página?

Hasta ahora he intentado usar BackGroundWorker Work Complete y Property Change. Los datos se enviaron nuevamente, pero la imagen no se volvió a cargar. ¿Alguna idea de cómo puedo lograr eso?

Gracias de antemano por la ayuda, Saludos cordiales