2012-05-07 4 views
0

У меня есть многопоточное приложение на C#, которое должно взаимодействовать с аппаратным обеспечением с помощью SerialPort.serialport многопоточное незапрашиваемое сообщение

Программа представляет собой в основном последовательность ответа команд, но аппаратное обеспечение может отправлять незапрашиваемое сообщение «СБРОС» из-за внутренней ошибки, в течение которой программное обеспечение должно повторно инициализировать его, отправив последовательность команд, устанавливающих определенные значения.

Более один поток (от ThreadPool) можно попробовать сделать TakeSampleNow()

public class ALComm 
{ 
    private readonly AutoLoaderManager _manager; 
    private readonly AutoResetEvent dataArrived = new AutoResetEvent(false); 
    private SerialPort _alPort; 
    private string _alResponse; 

    .... Code to init _alPort and attach datareceived event etc 

    public void TakeSampleNow() 
    { 
     if (Monitor.TryEnter(_alPort, 1000)) //let's wait a second 
     { 
      _manager.MessageList.Enqueue("Try sampling"); 
      try 
      { 
       Send("Command1"); 
       string response = Receive(); 

       switch(response) 
       { 
        case "X": blah blah.. 
        case "Y": blah blah.. 
       } 

       Send("Command2"); 
       string response = Receive(); 

       while(response != "OK") 
       { 
        Send("Command3"); 
        string response = Receive(); 
        Send("Command2"); 
        string response = Receive(); 
       } 
      } 
      finally 
      { 
       Console.WriteLine("Releasing port"); 
       //Thread.CurrentThread.Priority = ThreadPriority.Normal; 
       Monitor.Exit(_alPort); 
      } 
     else 
     { 
      _manager.MessageList.Enqueue("Port is busy!!!"); 
     } 
    } 

    public string Receive() 
    { 
     string inString = null; 

      dataArrived.WaitOne(1000); 
      inString = _alResponse; 

     return inString; 
    } 

    private void AlPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     _alResponse = _alPort.ReadLine(); 

     //if (_alResponse.ToUpper().Contains("RESET")) 
     //{ 
     // _alState = AlState.Reset; 
     // TryInitialize(); 
     //} 

     dataArrived.Set();    
    } 

    private void TryInitialize() 
    { 
     Monitor.Enter(_alPort);  //lock so other threads do not access samplenow during initialization 
     try 
     { 
      string response; 

      Console.WriteLine("Initializing ... The AutoLoader"); 
      _alPort.DiscardInBuffer(); 

      Send("CommandX"); 
      response = Receive(); 

      --- blah blah 

      _alResponse = ""; 
     } 
     finally 
     { 
      Monitor.Exit(_alPort); 
     } 
    } 

я могу проверить реакцию в DataReceived события и ждать на замке в TryInitialize() и для других потоков в TakeSampleNow чтобы освободить блокировку, мне пришлось бы проверять каждый ответ, если _alResponse содержит «RESET», и если он возвращается из метода. Это делает его более запутанным.

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

+1

Незапрошенные ответы - большая проблема. Особенно, когда он говорит «СБРОС», это ответ «Я упал и не могу встать». Вам нужно стремиться к тому, чтобы * никогда не получить такой ответ. Или перезагрузите компьютер, когда вы его получите, потому что это то, что сделал устройство. –

+0

Я бы тоже хотел перезапустить службу .. но это служба Windows, которая контролируется пользователем через браузер. – cheedep

ответ

1

Вы не указали подробные сведения о своем протоколе - вы не говорите, могут ли пары команды/rsponse перекрываться, и если да, то как ответы сопоставляются с командами.

Вы должны иметь возможность сделать это с помощью двигателя состояния. Запустите state-machine с собственным потоком, который ждет события BlockingCollection для событий. Вам понадобится поток SerialRecv, чтобы запустить протокол и проанализировать входящие байты в сообщениях.

Я бы использовал только один класс SerialEvent для переноса событий в очередь SM. Класс должен иметь перечисление для описания события и членов для rx-буфера, txData, проанализированных данных, данных для сборки tx-строки из поля исключения/errorMess - все, что необходимо для любого события и/или цели (например, SM может переслать завершенный запрос/ответ на дисплей или регистратор).

Некоторые события я могу думать о откладывая: EsmNewRequestResponse, EsmRxData, EsmResetRx

Перечисление событий может, в несколько этапов, есть и другие значения, которые не используются в СМ, например: EsmError, EsmLog, EsmDisplay.

Если вам нужны таймауты, вы можете сгенерировать их путем выключения take() в очереди ввода SM.

Да, есть вещи, которые я забыл.

Если несколько потоков выдают экземпляры SerialEvent одновременно, SM будет получать новые SerialEvents, пока он все еще обрабатывает первый. SM потребуется другая очередь/deque для хранения SerialEvents, ожидающих обработки. Из-за сериализации SM с помощью BlockingCollection/thread эта очередь ожидания не должна быть потокобезопасной. SM должен проверить эту ожидающую очередь после завершения любого запроса/ответа, чтобы проверить, есть ли другой процесс для обработки.

Чтобы обрабатывать запрос/отклик синхронно из нескольких потоков, запрашивающие потоки должны иметь что-то, что нужно ждать. AutoResetEvent в классе SerialEvent. Передача SerialEvent в систему будет приостанавливать экземпляр SerialEvent и ждать AutoResetEvent. Когда обработка экземпляра завершена (т.е.полученный ответ, ошибка или таймаут), SM установит событие, а инициирующий поток будет работать с экземпляром SerialEvent, заполненным данными.

Далее - класс SerialEvent приближается к точке, где может быть лучше объединить их, а не постоянно создавать/CG. Для этого в качестве пула потребуется другой BlockingCollection.

+0

Спасибо Мартин, последовательности ответов команд не пересекаются. В данный момент в аппаратное обеспечение передается только один поток, и принимаются ответы, заканчивающиеся на \ r \ n. Полученное сообщение сохраняется в поле _alResponse, которое считывается потоком передачи команд. Я понимаю ваш ответ в целом, но нужно некоторое время, чтобы понять, как, если можно, использовать его в моем случае. – cheedep

1

Вы не хотите, чтобы несколько потоков пытались прочитать ваш последовательный порт. У вас должен быть единственный поток, который ничего не делает, кроме чтения порта. Когда он получает некоторые данные, он помещает сообщение в очередь или аналогичную структуру данных, которые могут обрабатываться несколькими потоками выборки. Таким образом, ваш единственный поток читателей может надежно находить и реагировать на сообщения RESET.

+0

Спасибо TJD. Существует только один поток, отправляющий команды в данный момент времени, который ждет AutoResetEvent, который запускается потоком в событии DataReceived после ReadLine(). – cheedep

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