2016-05-28 3 views
0

У меня есть приложение, которое взаимодействует с микроконтроллером через последовательный порт. Я хочу периодически проверять статус контроллера в фоновом режиме, а также позволять пользователю взаимодействовать с контроллером асинхронно (через пользовательский интерфейс), отправляя команды и получая ответы.Как синхронизировать операции SerialPort?

Пользовательский интерфейс и фон Работник использовать SerialCommunication статический класс:

static class SerialCommunication 
{ 
    static SerialPort serialPort; 
    static int readWriteTimeout = 1000; // [ms] 
    static int waitForTransmissionTimeout = 2; // [s] 
    static string rxData = "", endOfString = "" + char.MinValue; 
    static bool WaitingForSerialData = false; // Set in SerialWrite(), cleared in SerialRead() 

    [...] 
    public static string SerialRead() 
    { 
     try 
     { 
      rxData = serialPort.ReadTo(endOfString); 
     } 
     catch (TimeoutException) 
     { 
      WaitingForSerialData = false; 
      throw new Exception(Properties.Resources.serial_read_timeout); 
     } 
     WaitingForSerialData = false; 
     return rxData; 
    } 

    public static void SerialWrite(string text) 
    { 
     DateTime start = DateTime.Now; 
     while (WaitingForSerialData) // Avoids the situation in which a command executed on a thread receives the response for the command executed from a different thread 
     { 
      if (!WaitingForSerialData) 
      { 
       try 
       { 
        WaitingForSerialData = true; // All commands wait for confirmation/data, so it is normal to set this boolean value for every serial transmission 
        serialPort.Write(text); 
       } 
       catch (TimeoutException) 
       { 
        throw new Exception(Properties.Resources.serial_write_timeout); 
       } 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(100); 
       if ((DateTime.Now - start).Seconds >= waitForTransmissionTimeout) 
       { 
        throw new Exception("Timeout"); 
       } 
      } 
     } 
    } 
} 

Последовательный порт инициализируется при запуске приложения. SerialWrite и SerialRead вызываются в пользовательском интерфейсе или в фоновом режиме последовательно.

Я хочу избежать ситуаций, когда команда получает ответ от другой команды, выполненной в другом потоке. В настоящее время я выполнил ожидание в SerialWrite для получения команды (для завершения SerialRead) перед отправкой команды, но я боюсь, что это может заблокировать пользовательский интерфейс (до waitForTransmissionTimeout секунд), если SerialWrite выполняется из там.

Каков наилучший способ синхронизации операций SerialPort?

+1

Итак, _все_ команды SerialWrite с последующим SerialRead для получения ответа? Если да, почему бы вам не объединить операции чтения и записи в одиночном SendCommand или что-то еще? – Evk

+0

@Evk Но что будет, если SendCommand выполняется, например, в потоке Background Worker и сразу после (до его завершения) в пользовательском интерфейсе? –

+1

Вы должны защищать всю операцию (писать + с последующим считыванием) с помощью простой блокировки. Затем, если UI отправляет команду перед завершением работы фонового работника - он будет ждать, пока это не будет выполнено. Конечно, вы не должны выполнять эту операцию из потока пользовательского интерфейса в любом случае. Выполните его из фонового потока, а затем верните результат обратно в поток пользовательского интерфейса, когда закончите (чтобы показать пользователю результат). Никогда не выполняйте такие действия непосредственно в потоке пользовательского интерфейса. – Evk

ответ

1

Я не знаю всей ситуации, но мне кажется, что вы можете просто использовать простой замок, чтобы сериализовать записи следует читать, как это:

static class SerialCommunication 
{ 
    static SerialPort serialPort; 
    static string endOfString = "" + char.MinValue; 
    static readonly object _commandLock = new object(); 

    public static string SendCommand(string text) { 
     lock (_commandLock) { 
      SerialWrite(text); 
      return SerialRead(); 
     } 
    } 

    private static string SerialRead() { 
     try { 
      return serialPort.ReadTo(endOfString); 
     } 
     catch (TimeoutException) { 
      throw new Exception(Properties.Resources.serial_read_timeout); 
     }    
    } 

    private static void SerialWrite(string text) { 
     try { 
      serialPort.Write(text); 
     } 
     catch (TimeoutException) { 
      throw new Exception(Properties.Resources.serial_write_timeout); 
     } 
    } 
} 
+0

Спасибо! Что касается пользовательского интерфейса, следует ли использовать фоновый рабочий для последовательных операций или есть ли решение для написания более компактного кода? –

+0

Вы можете использовать фоновый поток или использовать Task.Run или использовать async \ await, если ваша версия компилятора C# поддерживает это. Вы можете прочитать об этом во многих местах - например, https://stephenhaunts.com/2014/10/14/using-async-and-await-to-update-the-ui-thread/. – Evk

+0

Еще раз спасибо! –

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