2017-05-16 20 views
-1

私は以前開発したC#/ .NETプロジェクトの再構築/改良に取り組んでいます。 シリアルコミュニケータは128-255バイトを特殊文字列に変換できません。私はこのプロジェクトの作業バージョンで見られる文字に基づいてUnicodeエンコーディングを適用しようとしました。エンコードが適用されると、「メッセージの送信に失敗しました」というメッセージがスローされます。この問題をさらに解決するためのあらゆる指針は非常に高く評価されています。おかげさまで バイトの文字列表現128-255

public virtual string WriteData(string s, int expectedResponseLength) 
    { 

     lock (messageLock) 
     { 
      resetEvent.Reset(); 
     } 

     message = string.Format("WriteData: {0}\r\nMessageType={1}\r\n", s, MessageType.Outgoing); 

     expectedLength = expectedResponseLength; 

     if (!comPort.IsOpen) 
      comPort.Open(); 


     byte[] bs = new byte[s.Length]; 
     for (int i = 0; i < s.Length; ++i) 
      bs[i] = (byte)s[i]; 

     for (int i = 0; i < 3; i++)  
     { 
      response = string.Empty; 
      comPort.Write(bs, 0, bs.Length); 

      if (resetEvent.WaitOne(5000)) 
      { 
       return response; 
      } 
     } 

     throw new Exception("Message failed to send."); 
    } 

    public virtual bool OpenPort() 
    { 
     try 
     { 
      if (comPort.IsOpen) 
       comPort.Close(); 
      comPort.Encoding = new UnicodeEncoding(); 
      comPort.DtrEnable = false; 
      comPort.RtsEnable = false; 
      comPort.ReceivedBytesThreshold = 1; 
      comPort.ReadTimeout = -1; 
      comPort.ReadBufferSize = 1024; 
      comPort.BaudRate = int.Parse(BaudRate); 
      comPort.DataBits = int.Parse(DataBits); 
      comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), StopBits); 
      comPort.PortName = PortName; 
      comPort.Open(); 
      message = string.Concat("OpenPort: ", MessageType.Normal, ", Port opened at ", DateTime.Now); 
      return true; 
     } 
     catch (Exception ex) 
     { 
      message = "OpenPort: " + MessageType.Error + ex.Message; 
      return false; 
     } 
    } 

    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 

        string RcvByte = comPort.ReadExisting(); 

     // Encode the string. 
        byte[] encodedBytes = Encoding.Unicode.GetBytes(RcvByte); 


     // Decode bytes back to string.    
        RcvByte = Encoding.Unicode.GetString(encodedBytes); 
        response += RcvByte; 


     lock (messageLock) 
     { 
      if (response.Length >= expectedLength) 
      { 
       resetEvent.Set(); 
      } 
     } 
    } 
+0

送信する前にBase64としてエンコードしてください。それは大きくなりますが、Base64でエンコードされたバイトは明確に定義された文字を持ちます。あなたはもう一方の側でそれを解読する必要がありますが、送信メカニズムはもはやチョークしてはいけません。 –

+1

@Bertus:Unicodeで始まっているのであれば、UTF-7もうまく動作します。 – Joey

+0

作業バージョンでは、Unicodeエンコーディングと同じöにマップされたF6が表示されます。 UTF7はこの文字を2バイトにマップします。私はBase64エンコーディングを調べます。ありがとう。 – PAN

答えて

0

まず、他のデバイス(シリアルポート経由で通信しているデバイス)が期待するコードページを知る必要があります。それは理論上はUTF-8(Unicode)かもしれませんが、古いデバイスではほとんどのシングルバイトコードページです。たとえば、西ヨーロッパのWindowsコードページの場合はWindows-1252です。ですから、このようなものにOpenPortでごcomPort.Encoding = new UnicodeEncoding();を変更する必要があり、他のデバイスに応じて:

comPort.Encoding = new Encoding.GetEncoding(1252); 

次に、あなただけのSerialPort.Write(String)を使用し、文字列(すなわち、通信プロトコルにおける無バイナリデータ)と交換するように見えるようオーバーロードはSerialPort.Write(Byte[], Int32, Int32)ではなくEncodingプロパティを正しく設定していれば、前者はコードページ変換を処理します。これは、すべてのbs配列のものを削除し、ちょうどあなたのWriteData方法で

comPort.Write(s); 

を起動する必要があるということです。

ところで、非ASCII文字が入力文字列に表示される可能性がある場合は、bsの埋め込みコードが正しくありませんでした。あなたは、より良い、それを得るためにEncoding.GetBytesを使用したい:byte[] bs = Encoding.Unicode.GetBytes(s);

SerialPort_DataReceived方法でReadExisting()を使用する準備ができているstringとしての応答を、読み込み(再び、あなたは正しくEncodingプロパティを設定して提供しました)。 、GetBytes/GetStringで再変換するだけresponseを設定するので、必要はありません:

response += comPort.ReadExisting(); 

あなたはまた、あなたのコード内で同期を修正する必要があります。あなたはresponse変数へのアクセスをlock ...でラップする必要があります。なぜなら、それは2つのスレッドからの読み書きのためにアクセスされるからです。また、WriteDataが同時に呼び出されないようにしようと考えました(以下のサンプルでは実装されていません)。

結果として、私は次のようにシリアルポートの読み書き方法を書き換えることをお勧め:

public virtual string WriteData(string s, int expectedResponseLength) 
{ 
    resetEvent.Reset(); // no need to lock this 

    expectedLength = expectedResponseLength; 

    if (!comPort.IsOpen) 
     comPort.Open(); 

    for (int i = 0; i < 3; i++)  
    { 
     lock (messageLock) 
     { 
      response = string.Empty; 
     } 
     comPort.Write(s); 
     if (resetEvent.WaitOne(5000)) 
      lock (messageLock) 
      { 
       return response; 
      } 
    } 

    throw new Exception("Message failed to send."); 
} 

private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    string received = comPort.ReadExisting(); 
    lock (messageLock) 
    { 
     response += received; 
     if (response.Length >= expectedLength) 
     { 
      resetEvent.Set(); 
     } 
    } 
} 

これは、すべてのまだWriteData戻り代わり"Message failed to send."例外をスローするいくつかの応答を保証しないことがあります。デバイスは、プロトコル違反またはエンコーディングの問題が発生した場合でも、指定されたタイムアウト時間内に要求長の応答を提供しない可能性があります。問題を追跡するには、ReadExisting()によって返されたデータをトレースする必要があります。

+0

今日は5つの異なるエンコーディングを試しました。コード437だけが例外なしで実行されました。フォームには何も印刷されませんでした(空白の画面が表示されました)。 – PAN

+0

コメントウィンドウで試したコードを貼り付けることができません。 WriteDataは、シリアル通信プロトコルで指定された正確なバイト数で呼び出されます。 – PAN

関連する問題