クラス構成図
using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Timers; using Util; namespace SerialModule { public class Serial { enum State { IDLE, // アイドル STX_WAITING, // STX待ち ETX_WAITING, // ETX待ち BCC_WAITING, // BCC待ち EOT_WAITING, // EOT待ち ACK_WAITING // ACK待ち(ETBブロック送出後/ETXブロック送出後) } public delegate void Delegate(string message); Delegate callback; SerialPort serialPort; State state = State.IDLE; Queue
messageBlock = new Queue (); List receiveBuffer = new List (); StringBuilder receiveMessage = new StringBuilder(); Timer workingTimer;
コンストラクタ
string portName ポート名
int bauRate ボーレート
string p パリティ
Delegate function ファンクション
なし
・初期処理
・指定したポート名、ボーレート、パリティビット、データビット(Archでは8ビット固定)、およびストップビット(Archでは1ビット固定)を使用して、SerialPort クラスのインスタンスを初期化
・コールバック登録
・データ受信イベントハンドラ登録
// コンストラクタ
public Serial(string portName, int bauRate, string p, Delegate function) {
Parity parity = (Parity)Enum.Parse(typeof(Parity), p);
serialPort = new SerialPort(portName, bauRate, parity, 8, StopBits.One);
callback = function;
serialPort.DataReceived += new SerialDataReceivedEventHandler(ReceiveMsg);
}
シリアルポートを開く
なし
bool 処理結果
・ポートオープン
// シリアルポートオープン
public bool PortOpen() {
bool isOpen = false;
// ポートが開放されていない場合は開放する
if (!serialPort.IsOpen) {
serialPort.Open();
}
if (serialPort.IsOpen) {
isOpen = true;
}
return isOpen;
}
シリアルポートを閉じる
なし
なし
・ポートクローズ
// シリアルポートクローズ
public void PortClose() {
// ポートオープン前はオブジェクトが無い為何もしない
if (serialPort.IsOpen) {
serialPort.Close();
}
}
要求電文を送信する
string msg 要求電文
※各サービスの要求電文の設定内容については接続仕様書参照
int 処理結果
・Base64エンコード処理呼び出し
・ブロック分割
・JIS-8符号化
・制御コード付与
// 電文送信 public int SendMsg(string msg) { // シリアルポートがオープンされていれば電文を送信する。 if (serialPort.IsOpen) { if (!string.IsNullOrEmpty(msg) && state == State.IDLE) { // 引数がnullもしくは""でない かつ 状態がアイドルならば送信 // Base64エンコード msg = Utility.EncodeBase64(msg); // ブロック分割 messageBlock.Clear(); int blocksz = 2048 - 3; // 指定ブロックサイズ-制御コード(STX,ETX,BCC) int cnt = 1; for (int i = 0; i < msg.Length; i += blocksz) { if (msg.Length > (cnt * blocksz)) { // ブロック分を取り出してJIS-8符号化 byte[] msgArray = Utility.EncodeJis8(msg.Substring(i,blocksz)); // 制御コードを付与して格納 messageBlock.Enqueue(Utility.CreateMessage(msgArray, false)); } else { // ブロック分を取り出してJIS-8符号化 byte[] msgArray = Utility.EncodeJis8(msg.Substring(i)); // 制御コードを付与して格納 messageBlock.Enqueue(Utility.CreateMessage(msgArray, true)); } cnt++; } // 電文送信 SendData(messageBlock.Peek(), 0, messageBlock.Peek().Length); // ACK待ちへ移行 state = State.ACK_WAITING; ReceiveTimerStart(OnSendTimeOver); return 0; } else { // 引数がnullもしくは""である または 状態がアイドルでないならば1(エラー)を返却 return 1; } } else { // ポートが開いてなければ1(エラー)を返却 return 1; } }
シリアル送信する
byte[] data 送信データを格納したバイト型の配列
int i 送信を開始する配列の位置
int length 送信するバイト数
なし
・シリアル送信
// シリアル送信
void SendData(byte[] data, int i, int length) {
serialPort.Write(data, i, length);
}
応答電文を受信する
object sender
System.IO.Ports.
SerialDataReceivedEventArgs e
なし
・シリアル受信
・伝送制御(状態遷移)
・シリアル送信呼び出し
・タイムアウト処理呼び出し
・分割ブロックの結合
・Base64デコード処理呼び出し
// 電文受信 void ReceiveMsg(object sender, SerialDataReceivedEventArgs e) { // ポートからデータを受け取る string readdata = serialPort.ReadExisting(); byte[] bdata = Encoding.GetEncoding("UTF-8").GetBytes(readdata); // フェーズ00:アイドル状態ならSTX待ちへ移行 if (state == State.IDLE) { state = State.STX_WAITING; } // データ受信イベント処理 foreach (byte byteData in bdata) { // 状態確認 switch (state) { case State.STX_WAITING: // フェーズ01:STX待ちのとき // 受信データ確認 switch (byteData) { case 0x02: // イベントR01:STX受信 // 受信バッファをクリアしてSTXを格納 receiveBuffer.Clear(); receiveBuffer.Add(byteData); // ETX待ちへ移行 state = State.ETX_WAITING; ReceiveTimerStart(OnReceiveTimeOver); break; } break; case State.ETX_WAITING: // フェーズ02:ETX待ちのとき // 受信データ確認 switch (byteData) { case 0x02: // イベントR01:STX受信 // 何もしない break; case 0x17: // イベントR03:ETB受信 case 0x03: // イベントR04:ETX受信 // 受信バッファに格納 receiveBuffer.Add(byteData); // BCC待ちへ移行 state = State.BCC_WAITING; ReceiveTimerStart(OnReceiveTimeOver); break; default: // イベントR02:一般データ受信 // イベントR05:ACK受信 // イベントR06:NAK受信 // イベントR07:EOT受信 // 受信バッファに格納 receiveBuffer.Add(byteData); ReceiveTimerStart(OnReceiveTimeOver); break; } break; case State.BCC_WAITING: // フェーズ02:BCC待ちのとき // BCCチェックをしてACK/NAK応答 byte receiveResponse = Utility.CheckMessage(receiveBuffer.ToArray(), byteData); SendData(new byte[]{receiveResponse}, 0, 1); // ブロック受信後処理 if (receiveResponse == 0x06) { // ACK応答の場合 // 受信バッファからデータを取り出す byte[] msgArray = receiveBuffer.Skip(1).Take(receiveBuffer.Count - 2).ToArray(); // JIS-8符号復号化 string msg = Utility.DecodeJis8(msgArray); // 受信電文に結合 receiveMessage.Append(msg); // 状態遷移処理 if (receiveBuffer.Last() == 0x03) { // ETXブロック受信のとき // EOT待ちへ移行 state = State.EOT_WAITING; ReceiveTimerStart(OnEotReceiveTimeOver); } else { // ETBブロック受信のとき // STX待ちへ移行 state = State.STX_WAITING; ReceiveTimerStart(OnReceiveTimeOver); } } else { // NAK応答の場合 // STX待ちへ移行 state = State.STX_WAITING; ReceiveTimerStart(OnReceiveTimeOver); } break; case State.EOT_WAITING: // フェーズ03:EOT待ちのとき // 受信データ確認 switch (byteData) { case 0x04: // イベントR07:EOT受信 // 受信電文をBase64デコード string message = Utility.DecodeBase64(receiveMessage.ToString()); receiveMessage.Clear(); // アイドルへ移行 state = State.IDLE; ReceiveTimerStop(); // デコードした電文を返却 callback(message); break; } break; case State.ACK_WAITING: // フェーズ04:ACK待ちのとき // フェーズ05:ACK待ちのとき // 受信データ確認 switch (byteData) { case 0x06: // イベントR05:ACK受信 // 次ブロック読み込み messageBlock.Dequeue(); if (messageBlock.Count == 0) { // 次ブロックがない場合 // フェーズ05:ETXブロック送出後なのでEOT送信 SendData(new byte[]{0x04}, 0, 1); // アイドルへ移行 state = State.IDLE; ReceiveTimerStop(); } else { // 次ブロックがある場合 // フェーズ04:ETBブロック送出後なので次ブロック送信 SendData(messageBlock.Peek(), 0, messageBlock.Peek().Length); // ACK待ちへ移行 state = State.ACK_WAITING; ReceiveTimerStart(OnSendTimeOver); } break; case 0x15: // イベントR06:NAK受信 // ブロック再送 SendData(messageBlock.Peek(), 0, messageBlock.Peek().Length); // ACK待ちへ移行 state = State.ACK_WAITING; ReceiveTimerStart(OnSendTimeOver); break; } break; } } // データ受信イベント処理終了 }
受信タイマーを開始する
ElapsedEventHandler handler
なし
・タイマー開始(10秒)
// 受信タイマー開始
void ReceiveTimerStart(ElapsedEventHandler handler) {
// 動作中のタイマーを停止
if (workingTimer != null) {
workingTimer.Stop();
}
// 10秒のタイマーを作成
workingTimer = new Timer(10000);
workingTimer.AutoReset = false;
workingTimer.Elapsed += handler;
// タイマー開始
workingTimer.Start();
}
受信タイマーを停止する
なし
なし
・タイマー停止
// 受信タイマー停止
void ReceiveTimerStop() {
if (workingTimer != null) {
workingTimer.Stop();
workingTimer = null;
}
}
// フェーズ04イベントT01
// フェーズ05イベントT01
送信タイムアウト処理を行う
object sender
ElapsedEventArgs e
なし
・タイマー停止
・送信ブロック破棄
・フェーズを00に更新
// 送信タイムアウト処理(タイムアウトイベント処理)
void OnSendTimeOver(object sender, ElapsedEventArgs e) {
// タイマーの停止
Timer timer = sender as Timer;
if (timer != null) {
timer.Stop();
}
// 送信ブロック破棄
messageBlock.Clear();
// アイドルへ移行
state = State.IDLE;
}
// フェーズ01イベントT01
// フェーズ02イベントT01
データ受信タイムアウト処理を行う
object sender
ElapsedEventArgs e
なし
・タイマー停止
・NAK送信
・フェーズを01に更新
// データ受信タイムアウト処理(タイムアウトイベント処理)
void OnReceiveTimeOver(object sender, ElapsedEventArgs e) {
// タイマーの停止
Timer timer = sender as Timer;
if (timer != null) {
timer.Stop();
}
// NAK送信
SendData(new byte[]{0x15}, 0, 1);
// STX待ちへ移行
state = State.STX_WAITING;
ReceiveTimerStart(OnReceiveTimeOver);
}
// フェーズ03イベントT01
EOT受信タイムアウト処理を行う
object sender
ElapsedEventArgs e
なし
・タイマー停止
・NAK送信
・フェーズを03に更新
// EOT受信タイムアウト処理(タイムアウトイベント処理)
void OnEotReceiveTimeOver(object sender, ElapsedEventArgs e) {
// タイマーの停止
Timer timer = sender as Timer;
if (timer != null) {
timer.Stop();
}
// NAK送信
SendData(new byte[]{0x15}, 0, 1);
// EOT待ちへ移行
state = State.EOT_WAITING;
ReceiveTimerStart(OnEotReceiveTimeOver);
}
}
}
Base64エンコードする
string msg 変換前文字列
string 変換後文字列
・Base64エンコード(76 文字ごとに改行を挿入)
namespace Util {
public static class Utility {
// Base64エンコード
public static string EncodeBase64(string msg) {
return Convert.ToBase64String(Encoding.UTF8.GetBytes(msg), Base64FormattingOptions.InsertLineBreaks);
}
Base64デコードする
string msg 変換前文字列
string 変換後文字列
・Base64デコード
// Base64デコード
public static string DecodeBase64(string msg) {
return Encoding.UTF8.GetString(Convert.FromBase64String(msg));
}
JIS-8単位符号化する
string msg 変換前文字列
byte[] 変換後バイト型配列
・JIS-8単位符号化
// JIS8単位符号化
public static byte[] EncodeJis8(string msg) {
return Encoding.UTF8.GetBytes(msg);
}
JIS-8単位符号復号化する
byte[] msgArray 変換前バイト型配列
string msg 変換後文字列
・JIS-8単位符号復号化
// JIS8単位符号復号化
public static string DecodeJis8(byte[] msgArray) {
return Encoding.UTF8.GetString(msgArray);
}
制御コードを付与する
byte [] msgArray 変換前バイト型配列
bool flg 最終電文フラグ
byte[] 変換後バイト型配列
・STX、ETB、ETXの付与
・BCC作成処理呼び出し
・BCCの付与
// STX,ETX,BCC等の付与 public static byte[] CreateMessage(byte[] msgArray, bool flg) { // STX byte[] startbit = {0x02}; // ETB,ETX byte[] endbit = {0x17}; // BCC byte[] bcc = new byte[1]; if (flg) { // 最終電文ならETXを付与する endbit[0] = 0x03; } //BCCの計算 msgArray = Enumerable.Concat(startbit, msgArray).ToArray(); msgArray = Enumerable.Concat(msgArray, endbit).ToArray(); bcc[0] = Convert.ToByte(CreateBcc(msgArray)); msgArray = Enumerable.Concat(msgArray, bcc).ToArray(); return msgArray; }
BCCを作成する
byte[] msg バイト型配列
byte BCC
・引数「バイト型配列」を元にBCCを作成
// BCC作成
public static byte CreateBcc(byte[] msg) {
byte bcc = 0;
for (int i = 0; i < msg.Length; i++){
bcc = (byte)(bcc ^ msg[i]);
}
return bcc;
}
受信電文の妥当性チェックを行う
受信電文のBCCチェックを行い、内容に応じて
ACK/NAKを返す
byte[] msgArray バイト型配列
byte receiveBcc バイト型配列のBCC
byte ACK/NAK
・引数「バイト型配列」を元にBCCを作成
・引数「バイト型配列のBCC」と作成したBCCを比較
・一致した場合ACKを返却、一致しなかった場合NAKを返却
// 受信電文妥当性チェック
// 内容に応じてACK/NAKを返す
public static byte CheckMessage(byte[] msgArray, byte receiveBcc){
byte buff;
byte checkbcc = CreateBcc(msgArray);
// BCCチェック
if(receiveBcc == checkbcc){
// ACK
buff = 0x06;
} else {
// NAK
buff = 0x15;
}
return buff;
}
}
}
コードをコピーしました