簡體   English   中英

C#GUI刷新和異步串行端口通信

[英]C# GUI refresh and async serial port communication

我正在嘗試創建一個通過串行端口與硬件通信並將結果報告給gui的應用程序。

當前在GUI中移動是由KeyEvents進行的,這些事件觸發GUI的下一個“頁面”的繪制。 但是,第一步(按下鍵后),我需要繪制新頁面並通過串行端口發送一些命令。

通過以下命令發送命令:

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工作完成和屬性更改。 數據已再次發送,但是圖像未重新加載。 知道我該如何實現嗎?

預先感謝您的幫助,最好的問候

您需要使用狀態機和委托來實現您想要做的事情。 參見下面的代碼,我建議在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);
    }
}

編輯:您可以使用invoke從后台工作線程更新GUI。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM