Actualización de la GUI de C # y comunicación del puerto serial asíncrono

Intento crear una aplicación que se comunique con el hardware a través del puerto serie e informa los resultados a la GUI.

Actualmente, el movimiento a través de la GUI se realiza mediante KeyEvents, que activan el dibujo de la siguiente “página” de 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 comando de envío se realiza a través de:

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

Luego espero la respuesta esperando a que DataReceivedHandler se active, simplemente indica que hay datos en espera y que los datos se están procesando en otro método.

Al principio, simplemente puse el comando enviar y recibir en la función dibujar la página después de las “partes de dibujo”, sin embargo, lo atascó: los datos se transfirieron, pero la página no se dibujó, se congeló.

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 de esta manera:

 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 de sincronización se terminó y desencadenar un evento donde podría volver a cargar la página?

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

Gracias de antemano por la ayuda, Saludos cordiales

Debe usar una máquina de estado y delegates para lograr lo que está tratando de hacer. Mira el código a continuación, te recomiendo que hagas todo esto en un hilo aparte de Main. Mantiene un registro del estado en el que se encuentra y, cuando obtiene una respuesta, lo analiza con la función de callback 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); } } } 

Me gustaría poner esto en un trabajador de segundo plano, y cuando presiona un botón o algo, puede configurar el estado actual en SendCmd1 .

Botón de presionar

 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 de 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; });