2016-07-21 2 views
0

Я пытаюсь создать приложение, которое связывается с оборудованием через последовательный порт и передает результаты в gui.Обновлен интерфейс C# GUI и асинхронный последовательный порт

В настоящее время через GUI выполняется KeyEvents, который запускает чертеж следующей «страницы» графического интерфейса пользователя. Однако на одном шаге (после нажатия клавиши) мне нужно нарисовать новую страницу и отправить несколько команд через последовательный порт.

Команда отсылка осуществляется с помощью:

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 Complete и изменение свойств. Данные были отправлены снова, но изображение не было перезагружено. Любая идея, как я могу это достичь?

Заранее спасибо за помощь, С наилучшими пожеланиями

+1

Прекратите использование 'асинхронной 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

Смежные вопросы