前提・実現したいこと
WindowsFormアプリケーションでシリアル通信プログラムを作っています。
以下に示すような手順で同期的な(順序的な)手続きに従う、
データの送受信をしたいのですが実現できていません。
イベントハンドラの使用には拘らないです。
どのようにすれば実現できるかご教示ください。
【実現したい手順】
フォームボタンをクリックすると以下の手順を繰り返す。
繰り返し間隔は1秒周期を考えています。
(全データのバイト数は70バイト程度なので1秒程度の周期であれば
問題なく送受信可能と考えています。19200baudなので)
手順1.スレーブ機器にコマンド1を送信する(機器の状態を設定するため)
手順2.コマンド1に対する応答を受信する(通信仕様で自動的に応答が戻ります)
↓ 受信したデータから機器の状態を遷移させる判断を行い、
自動的に手順3のコマンド送信を行う。
手順3.スレーブ機器にコマンド2を送信する(機器の状態を読み込む)
手順4.コマンド2に対する応答を受信する(通信仕様で自動的に応答が戻ります)
1~4を繰り返す。
発生している問題・エラーメッセージ
問題1:所望する”順序的に確実に受信を検出し、データ受信する”ことができていません。
問題2:Timerで周期的な処理をさせようとすると、バッファオーバーランが発生します。
1巡目の手順2で受信したデータは所望するバイト数で正しくデータを受信できていますが、
1巡目の手順4で受信したデータは所望するバイト数ではなく、
1巡目の手順2で受信したバイト数+手順4で受信したバイト数となるときもあれば、
1巡目の手順2で受信したバイト数+2巡目の手順2で受信したバイト数となるとき
などなど・・・・
MSDNなどの情報など検索した限り、実現手段は千差万別ですが、
私は「試したこと」に書いてますが、DataRecieveイベントを確実に制御できる
レベルではないです。
そこで、イベントハンドラに頼らない下記のような事例紹介がありますが、
このような手段で実現したいことが可能なのか?
★印をつけた部分が特に理解できていません。
また、bufferの配列数がどのようにデバッグ、確認を行えばよいかも含め。
事例 Example implementation using synchronous API and dynamic timeout properties update: static byte[] SendMessage(byte[] message, TimeSpan timeout) { // Use stopwatch to update SerialPort.ReadTimeout and SerialPort.WriteTimeout // as we go. var stopwatch = Stopwatch.StartNew(); // Organize critical section for logical operations using some standard .NET tool. lock (_syncRoot) { var originalWriteTimeout = _serialPort.WriteTimeout; var originalReadTimeout = _serialPort.ReadTimeout; try { // Start logical request. _serialPort.WriteTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0); _serialPort.Write(message, 0, message.Length); // Expected response length. Look for the constant value from // the device communication protocol specification or extract // from the response header (first response bytes) if there is // any specified in the protocol. int count = ...; byte[] buffer = new byte[count]; int offset = 0; // Loop until we recieve a full response. while (count > 0) { _serialPort.ReadTimeout = (int)Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0); var readCount = _serialPort.Read(buffer, offset, count); offset += readCount; count -= readCount; } return buffer; } finally { // Restore SerialPort state. _serialPort.ReadTimeout = originalReadTimeout; _serialPort.WriteTimeout = originalWriteTimeout; } } } And example usage: byte[] request = ...; TimeSpan timeout = ...; var sendTask = Task.Run(() => SendMessage(request, timeout)); try //★ これ以降の意図が特に判らない。 { await await Task.WhenAny(sendTask, Task.Delay(timeout)); } catch (TaskCanceledException) { throw new TimeoutException(); } byte[] response = await sendTask;
該当のソースコード
C#
1 private void btnRemote_Click(object sender, EventArgs e) 2 { 3 short[] tmpData = new short[] { }; 4 //レジスタ数 5 ushort numOfPoint = 3; 6 //スレーブIDの取得 7 slaveID = GetSlaveID(); 8 //以下の処理は送受信が同期的に行われているか?を確認するための 9 //ボタン表示切替処理 10 switch (btnRemote.Text) 11 { 12 case "リモート接続": 13 try 14 { 15 // 手順1の設定送信(FunctionCode6) 16 short[] data = new short[] { Convert.ToInt16(1) }; 17 byte[] frame = mb.WriteSingleRegMsg(slaveID, Convert.ToUInt16(100), (byte)FunctionCode.FC6, data); 18 sp.Write(frame, 0, frame.Length); 19 //手順2 スレーブが正しく設定メッセージを受け取ったか? 20 // の確認を応答メッセージとして受信したい。 21 // 〇一巡目は正しいバイト数で受信できている。 22 23 // ---- 判断処理 始まり 24 // 手順1で送信したメッセージを正しく受信できた 25 // ことを確認できれば手順3で当該のレジスタ情報を読込む 26 // ---- 判断処理 終わり 27 28 // 手順3の設定確認(FunctionCode3) 29 numOfPoint = 1; 30 frame = mb.ReadHoldingRegiMsg(slaveID, Convert.ToUInt16(100), (byte)FunctionCode.FC3, (ushort)numOfPoint); 31 sp.Write(frame, 0, frame.Length); 32 //手順4 スレーブが正しく設定メッセージを受け取ったか? 33 // の確認を応答メッセージとして受信したい。 34 // × 一巡目で受信できなかったり、できていたり・・・ 35 36 // ---- 判断処理 始まり 37 // 手順4で受信した応答メッセージに含まれるデータを用いて 38 // 判断処理を行う。 39 // ---- 判断処理 終わり 40 41 if (mb.Check_FunctionCode_3(ref tmpData,slaveDataByteArry)) 42 { 43 if ((tmpData[0] == 1)&& (tmpData[2] == 0)) 44 { 45 } 46 break; 47 48 case "リモート解除": 49 try 50 { 51 // 同じような手順に基づく送受信データ手続き。 52 } 53 break; 54 } 55 56 } 57 public void serialPort_DataReceived(object sender,System.IO.Ports.SerialDataReceivedEventArgs e) 58 { 59 if (sp.IsOpen == false) 60 { 61 return; 62 } 63 try 64 { 65 //! 受信データを読み込む. 66 //string data = sp.ReadExisting(); 67 byte[] data = new byte[sp.BytesToRead]; 68 sp.Read(data, 0, data.GetLength(0)); 69 70 short[] tmpShort = dc.ByteToShort_Array(data); 71 72 for (int index = 0; index < data.GetLength(0); index++) 73 intermediate_list.Add(data[index]); 74 75 //this.Invoke(new MethodInvoker(() => ReadCode += data)); 76 // ↑試み1:ReadCodeに受信した配列のデータを追加する。 77 //data.ToList().ForEach(b => slaveDataByteArry.Enqueue(b)); 78 // ↑試み2:リストに受信データを追加する。 79 80 } 81 catch(Exception ex) 82 { 83 MessageBox.Show(ex.Message); 84 } 85 }
試したこと
受信イベントがどのようなタイミングで発生しているのか?
たとえ、不規則でも順序は守られているか?を確認するため、ハンドラ内部で
ブレークポイントは設けず、以下のようにして受信データを確認。
その結果、
・イベント自身は発生しているが、同期的に(順序的に)発生していることはない。
● この結果から、受信データを割り込みを使わず、順序的に確実に取り込む方法が
必要と判断したが、
・ 受信が終わったことはどのように判断できるのか?判らない。
結論:イベントハンドラの利用はあきらめる。
試み1:mainスレッドにコントロールを戻し、受信データが同期的に取得できているか?
試み2:publicなリストを作り、そのリストのデータが同期的に取得できているか?
補足情報(FW/ツールのバージョンなど)
.NET framework4.5,Visual Studio 2017 enterprize
回答4件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/09/29 03:22