2012-05-07 2 views
0

SerialPort를 사용하여 하드웨어와 인터페이스해야하는 C# 다중 스레드 응용 프로그램이 있습니다.serialport 다중 스레드 원치 않는 메시지

프로그램은 대부분 명령 응답 시퀀스이지만 하드웨어는 내부 오류로 인해 원치 않는 "RESET"메시지를 보낼 수 있습니다.이 메시지는 소프트웨어가 특정 값을 설정하는 일련의 명령을 보내서 다시 초기화해야합니다. (스레드에서)

더 이상의 스레드 내가 datareceived 이벤트에 응답을 확인하고 TryInitialize()와 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); 
     } 
    } 

TakeSampleNow()를 수행하려고 할 수 있습니다 잠금을 해제하려면 _alResponse에 "RESET"이 포함되어 있고 메서드에서 반환되는 경우 각 응답을 확인해야합니다. 어느 것이 더 복잡한가.

내가 더 잘할 수있는 방법에 대한 제안. 나는 그것이 상태 기계 일 수 있지만 그것을 개념화 할 수 없다고 생각한다.

+1

원치 않는 응답은 큰 문제입니다. 특히 "RESET"이라고 말하면 "나 빠졌고 나는 일어날 수 없다"는 반응입니다. 당신이 노력해야 할 것은 결코 그런 반응을 얻지 않는 것입니다. 그게 장치이기 때문에 당신이 그것을 얻을 때 또는 재부 팅 기계. –

+0

서비스를 다시 시작하고 싶습니다. 그러나 브라우저를 통해 사용자가 모니터링하는 Windows 서비스입니다. – cheedep

답변

1

프로토콜의 세부 사항을 많이 제공하지는 않습니다. 명령/rssponse 쌍이 겹칠 수 있는지 여부와 응답이 명령과 일치하는 방식을 말하지 않습니다.

상태 엔진으로이를 수행 할 수 있어야합니다. 이벤트에 대한 BlockingCollection을 기다리는 자체 스레드로 상태 머신을 실행하십시오. 프로토콜을 실행하고 수신 바이트를 메시지로 구문 분석하려면 'SerialRecv'스레드가 필요합니다.

하나의 'SerialEvent'클래스를 사용하여 이벤트를 SM 큐로 전달할 것입니다. 클래스에는 rx 버퍼, txData, 구문 분석 된 데이터, tx 문자열을 어셈블 할 데이터, 예외/errorMess 필드 - 모든 이벤트 및 앞으로의 목적에 필요한 모든 것을 설명하는 enum이 있어야합니다 (예 : SM은 완료된 요청/응답을 디스플레이 또는 로거로 전달). 내가 직선 생각할 수

일부 이벤트 : EsmError, EsmLog, EsmDisplay : EsmNewRequestResponse, EsmRxData, EsmResetRx

는 이벤트 열거, 어떤 단계로는 예를 들어, SM, 사용되지 않는 다른 값을 가질 수있다.

시간 초과가 필요한 경우 SM 입력 대기열에서 take()를 시간 초과하여 생성 할 수 있습니다.

예, 제가 빠뜨린 것들이 있습니다.

여러 스레드가 동시에 'SerialEvent 인스턴스'를 발행하면 SM은 첫 번째 스레드를 처리하는 동안 새 SerialEvent를 얻습니다. SM은 처리를 기다리는 SerialEvent를 보유하기 위해 또 다른 큐/큐를 필요로 할 것이다. BlockingCollection/thread에 의한 SM의 직렬화로 인해,이 '보류 중'대기열은 스레드로부터 안전하지 않아도됩니다. 요청/응답이 완료된 후에 SM은 처리 할 다른 대기열이 있는지 확인하기 위해 보류중인 대기열을 확인해야합니다.

여러 스레드에서 요청/응답을 동 기적으로 처리하려면 요청 스레드가 대기해야합니다. SerialEvent 클래스의 AutoResetEvent가 처리합니다. SerialEvent를 시스템에 송신하면 (자), SerialEvent 인스턴스가 큐에 넣어 져 AutoResetEvent가 대기합니다. 인스턴스 처리가 완료되면 (즉,응답 수신, 오류 또는 시간 초과) SM은 이벤트를 설정하고 원래 스레드는 해당 SerialEvent 인스턴스가 데이터로 채워져 실행됩니다.

다음 - SerialEvent 클래스가 지속적으로/CG를 만드는 대신 풀링하는 것이 더 나은 방향으로 가고 있습니다. 그러면 다른 BlockingCollection이 풀로 작동해야합니다.

+0

Martin 감사합니다. 명령 응답 시퀀스가 ​​겹치지 않습니다. 주어진 시점에 하나의 스레드 만 하드웨어에 명령을 보내고 \ r \ n로 끝나는 응답이 수신됩니다. 수신 된 메시지는 _alResponse 필드에 저장되며,이 필드는 명령 전송 스레드에 의해 읽혀집니다. 나는 일반적으로 귀하의 답변을 이해하지만, 가능한 경우, 내 경우에 그것을 사용할 수있는 방법을 이해하는 데 시간이 필요합니다. – cheedep

1

직렬 포트를 읽는 데 여러 스레드를 사용하고 싶지는 않습니다. 아무것도 읽지 않고 포트를 읽는 단일 스레드가 있어야합니다. 데이터를 가져 오면 여러 샘플 스레드에서 처리 할 수있는 큐 또는 유사한 데이터 구조에 메시지를 넣습니다. 이렇게하면 단일 독자 스레드가 안정적으로 RESET 메시지를 찾아 대응할 수 있습니다.

+0

감사합니다. TJD. ReadLine() 후에 DataReceived 이벤트의 스레드에 의해 트리거되는 AutoResetEvent를 기다리는 지정된 시간에 명령을 보내는 스레드는 하나뿐입니다. – cheedep

관련 문제