2016-07-21 10 views
0

Ich versuche, eine Anwendung zu erstellen, die mit der Hardware über die serielle Schnittstelle kommuniziert und die Ergebnisse an die GUI meldet.C# GUI aktualisieren und asynchrone serielle Schnittstelle Kommunikation

Momentan läuft die GUI durch KeyEvents, die das Zeichnen der nächsten "Seite" der GUI auslösen. Aber in einem Schritt (nachdem die Taste gedrückt wurde) muss ich eine neue Seite zeichnen und einige Befehle über die serielle Schnittstelle senden.

Der Befehl wird bei Versand via getan:

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

Ich warte auf die Antwort dann für DataReceivedHandler warten auslösen - es Stifte nur darauf hin, dass es Daten gibt, warten und Daten in einem anderen Verfahren verarbeitet werden.

Zuerst habe ich einfach senden & empfangenden Befehl in der Funktion Zeichnen der Seite nach der "Teile ziehen" aber es machte es stecken - die Daten wurden übertragen, aber die Seite wurde nicht gezeichnet - es wurde eingefroren.

Dann machte ich eine Asynchron-Methode:

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

, die wie die verwendet wird:

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

Es funktioniert gut, aber ich muss "reload" die Seite (wieder LoadPage nennen) . Wenn ich es in der Asynchron-Methode wie folgt tun:

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

Dann offensichtlich wird das Bild nicht aktualisiert werden, wenn die Daten über die serielle Schnittstelle gesendet. Ist es möglich, irgendwie zu überprüfen, ob die Async-Funktion beendet ist und ein Ereignis auszulösen, bei dem ich die Seite neu laden könnte?

Bisher habe ich versucht, die BackGroundWorker Work Complete und Property Change zu verwenden. Die Daten wurden erneut gesendet, aber das Bild wurde nicht erneut geladen. Irgendeine Idee, wie ich das erreichen kann?

Vielen Dank im Voraus für die Hilfe, Mit freundlichen Grüßen

+1

Stopp mit 'async void', [es nur für Event-Handler verwendet werden soll] (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). –

Antwort

1

Sie benötigen eine Zustandsmaschine zu verwenden und delegates zu erreichen, was Sie zu tun versuchen. Sehen Sie den Code unten, ich empfehle das alles in einem anderen Thread als Main. Sie behalten den Überblick über den Zustand, in dem Sie sich befinden, und wenn Sie eine Antwort erhalten, analysieren Sie sie mit der richtigen Callback-Funktion, und wenn Sie erwartet werden, wechseln Sie zum nächsten Status des Send-Befehls.

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

Ich habe dies in einem Hintergrund-Arbeiter würde, und wenn Sie einen Knopf oder etwas drücken Sie den aktuellen Status SendCmd1 einstellen.

Knopf drücken

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

     backgroundWorker1.RunWorkerAsync(); 
    } 
} 

Hintergrund Arbeiter tun Arbeit Veranstaltung

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while (true) 
    { 
     if (backgroundWorker1.CancellationPending) 
      break; 

     do_State_Machine(); 
     Thread.Sleep(100); 
    } 
} 

bearbeiten: Sie können die GUI von Ihrem Hintergrund Worker-Thread zu aktualisieren verwenden aufzurufen.

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

Ich habe einen etwas anderen Weg gewählt, aber Ihr Beitrag hat mir sehr geholfen, meine Lösung zu finden. Ich war in der Lage, mein Problem zu umgehen, indem ich synchron Befehle sendete und sie von einem asynchronen seriellen Handler empfing, an den ich Objekte weitergab und Funktionen von dort aus aufruft. –

+0

Ich bin froh, dass es geholfen hat. – Baddack