2016-07-21 23 views
0

我试图创建一个应用程序,它通过串口与硬件进行通信并将结果报告给gui。C#GUI刷新和异步串口通信

当前通过GUI进行的移动是由触发GUI的下一个“页面”绘制的KeyEvent完成的。然而,在一个步骤中(按下键后),我需要绘制新页面并通过串口发送少量命令。

命令发送通过完成:

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

然后我通过等待DataReceivedHandler触发等待答案 - 它只是针从有数据等待和数据在另一个方法处理。

起初我只是把发送&接收命令放在绘制页面的函数之后的“绘制部分”,但是让它卡住了 - 数据被转移了,但是页面没有绘制 - 它被冻结了。

然后我做了一个异步方法:

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

这是用这样的:

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

它工作正常,但我需要“刷新”的页面(再次呼叫LoadPage) 。如果我这样做的异步方法里面是这样的:

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

那么很明显,图像不会被刷新,但该数据将通过串口发送。是否有可能以某种方式检查异步函数是否完成,并触发一个事件,我可以重新加载页面?

到目前为止,我尝试过使用BackGroundWorker工作完成和属性更改。数据再次发送,但图像未重新加载。任何想法我怎么能实现呢?

在此先感谢您的帮助, 最好的问候

+1

停止使用'async void',[它只能用于事件处理程序](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx)。 –

回答

1

你需要使用一个状态机和delegates达到什么你正在尝试做的。请参阅下面的代码,我建议在除Main之外的单独线程中完成所有这些操作。你跟踪你所处的状态,当你得到一个响应时,你用正确的回调函数解析它,如果它是你期望的,你将进入下一个发送命令状态。

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

按下按钮

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

     backgroundWorker1.RunWorkerAsync(); 
    } 
} 

背景工人做的工作事件

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

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

编辑:您可以使用调用来更新您的后台工作线程的GUI。

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

我用了一点不同的方式,但是你的帖子大大帮助了我解决问题。我能够通过同步发送命令并通过异步串行处理程序接收它们来解决我的问题,我向其传递对象并从中调用函数。 –

+0

很高兴帮助。 – Baddack