13,000
回編集
(→受信) |
|||
441行目: | 441行目: | ||
<br><br> | <br><br> | ||
== その他のシリアル通信の機能 == | == その他のシリアル通信の機能 : バッファリング、再接続機能 == | ||
==== 送信 ==== | ==== 送信 ==== | ||
以下の例では、バッファリング、再接続機能を使用して、非同期でデータを送信している。<br> | 以下の例では、バッファリング、再接続機能を使用して、非同期でデータを送信している。<br> | ||
748行目: | 748行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== その他のシリアル通信の機能 : ACK信号待機 == | |||
以下の例では、RS-232C通信で送受信の制御を行うものであり、送信後にACK信号を待機して、ACK信号を受信した場合は待機解除して次のデータを送信している。<br> | |||
<br> | |||
送受信は非同期処理で行い、特に受信処理は別タスクで常時監視を行う。<br> | |||
<br> | |||
また、以下の例では、5秒のタイムアウト処理を実装している。<br> | |||
<br> | |||
* 通信設定 | |||
*: 8N1形式 (データ長 8[bit]、パリティ無し、ストップビット 1[bit]) | |||
*: デフォルトのボーレートは9600[bps] | |||
*: ハンドシェイクは無効化 | |||
*: <br> | |||
* ACK制御 | |||
*: ACKの待機には、SemaphoreSlimを使用する。 | |||
*: 送信後、5秒のタイムアウトでACKを待機する。 | |||
*: ACK信号 (0x06) を受信した後、セマフォを解放して待機を解除する。 | |||
<br> | |||
最初のデータを送信する時、以下に示すような順序で処理を実行する。<br> | |||
# 送信データをUTF-8でエンコードして送信する。 | |||
# ACK待機状態に入る。(最大10[秒]) | |||
# ACK信号を受信した場合は待機を解除する。 | |||
# 次のデータを送信する | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
using System; | |||
using System.IO.Ports; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
/// <summary> | |||
/// RS-232C シリアル通信を制御するクラス | |||
/// 8N1形式(8ビットデータ、パリティなし、ストップビット1)での通信を行い、 | |||
/// 送信後にACK応答の待機と制御を行います。 | |||
/// </summary> | |||
class SerialCommunication | |||
{ | |||
// 任意のACK信号の値 (ここでは、16進数で0x06とする) | |||
// ASCIIコードでは、Acknowledgmentの意味を持つ制御文字 | |||
private const byte ACK = 0x06; | |||
// シリアルポートの制御用オブジェクト | |||
private readonly SerialPort _serialPort; | |||
// ACK信号の待機制御用セマフォ | |||
// 初期値0, 最大値1のセマフォでACK受信時に解放される | |||
private readonly SemaphoreSlim _ackSemaphore; | |||
// 受信タスクのキャンセル制御用トークンソース | |||
private CancellationTokenSource _cts; | |||
// 受信処理を行う非同期タスク | |||
private Task _receiveTask; | |||
/// <summary> | |||
/// コンストラクタ | |||
/// シリアルポートの初期設定と必要なオブジェクトの初期化を行う | |||
/// </summary> | |||
/// <param name="portName">使用するシリアルポート名 (例: COM1, /dev/ttyS0)</param> | |||
/// <param name="baudRate">ボーレート (デフォルト: 9600[bps])</param> | |||
public SerialCommunication(string portName, int baudRate = 9600) | |||
{ | |||
// シリアルポートの設定 | |||
_serialPort = new SerialPort(portName, baudRate) | |||
{ | |||
Parity = Parity.None, // パリティビット無し | |||
DataBits = 8, // データビット長 8[ビット] | |||
StopBits = StopBits.One, // ストップビット 1[ビット] | |||
Handshake = Handshake.None, // フロー制御なし | |||
ReadTimeout = 1000, // 読み取りタイムアウト 1秒 | |||
WriteTimeout = 1000 // 書き込みタイムアウト 1秒 | |||
}; | |||
// ACK待機用セマフォの初期化 (初期値0, 最大値1) | |||
_ackSemaphore = new SemaphoreSlim(0, 1); | |||
// キャンセルトークンソースの初期化 | |||
_cts = new CancellationTokenSource(); | |||
} | |||
/// <summary> | |||
/// 通信処理を開始する非同期メソッド | |||
/// ポートのオープン、受信タスクの起動、メッセージ送信ループを実行する | |||
/// </summary> | |||
public async Task StartAsync() | |||
{ | |||
try | |||
{ | |||
// シリアルポートをオープン | |||
_serialPort.Open(); | |||
Console.WriteLine("シリアルポートをオープンしました"); | |||
// ACK信号の受信待機タスクを開始 | |||
// 別スレッドで常時受信監視を行う | |||
_receiveTask = ReceiveDataAsync(_cts.Token); | |||
// メッセージ送信ループ | |||
while (true) | |||
{ | |||
Console.Write("送信するメッセージを入力 (終了する場合は 'exit'): "); | |||
string message = Console.ReadLine(); | |||
// 終了コマンドの確認 | |||
if (message?.ToLower() == "exit") break; | |||
// メッセージを送信して、ACK信号の待機を行う | |||
await SendMessageWithAckAsync(message); | |||
} | |||
} | |||
finally | |||
{ | |||
// 終了時の後処理 | |||
await StopAsync(); | |||
} | |||
} | |||
/// <summary> | |||
/// データを送信して、ACK信号の応答を待機する非同期メソッド | |||
/// </summary> | |||
/// <param name="message">送信するメッセージ</param> | |||
private async Task SendMessageWithAckAsync(string message) | |||
{ | |||
try | |||
{ | |||
// メッセージをUTF-8でバイト配列にエンコード | |||
// 末尾に改行コードを追加 | |||
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message + Environment.NewLine); | |||
// データをシリアルポートに送信 | |||
await _serialPort.BaseStream.WriteAsync(buffer, 0, buffer.Length); | |||
Console.WriteLine("データを送信しました"); | |||
Console.WriteLine("ACK待機中..."); | |||
// ACK信号待機用のタイムアウト設定 (10秒) | |||
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); | |||
// メインのキャンセルトークンとタイムアウトトークンを連結 | |||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, _cts.Token); | |||
try | |||
{ | |||
// ACK信号を待機 (最大10秒) | |||
await _ackSemaphore.WaitAsync(linkedCts.Token); | |||
Console.WriteLine("ACKを受信しました"); | |||
} | |||
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested) | |||
{ | |||
// タイムアウト発生時の処理 | |||
Console.WriteLine("ACK待機がタイムアウトしました"); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.WriteLine($"送信エラー : {ex.Message}"); | |||
} | |||
} | |||
/// <summary> | |||
/// データ受信を監視する非同期メソッド | |||
/// 別タスクで常時実行されて、受信データの処理とACKの検出を行う | |||
/// </summary> | |||
/// <param name="cancellationToken">キャンセル制御用トークン</param> | |||
private async Task ReceiveDataAsync(CancellationToken cancellationToken) | |||
{ | |||
// 受信バッファ (1024バイト) | |||
byte[] buffer = new byte[1024]; | |||
// キャンセルされるまでループ | |||
while (!cancellationToken.IsCancellationRequested) | |||
{ | |||
try | |||
{ | |||
// データの非同期読み取り | |||
int bytesRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); | |||
if (bytesRead > 0) | |||
{ | |||
// 受信データ内のACK信号の検索 | |||
for (int i = 0; i < bytesRead; i++) | |||
{ | |||
if (buffer[i] == ACK) | |||
{ | |||
// ACK信号を検出した場合は、セマフォを解放して待機を解除 | |||
_ackSemaphore.Release(); | |||
break; | |||
} | |||
} | |||
// ACK信号以外の受信データの表示処理 | |||
string receivedData = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead); | |||
if (!string.IsNullOrWhiteSpace(receivedData)) | |||
{ | |||
Console.WriteLine($"受信データ : {receivedData.Trim()}"); | |||
} | |||
} | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
// キャンセル時は上位に例外を再スロー | |||
throw; | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.WriteLine($"受信エラー : {ex.Message}"); | |||
} | |||
} | |||
} | |||
/// <summary> | |||
/// シリアル通信を停止して、リソースを解放する非同期メソッド | |||
/// </summary> | |||
public async Task StopAsync() | |||
{ | |||
// 受信タスクをキャンセル | |||
_cts.Cancel(); | |||
if (_receiveTask != null) | |||
{ | |||
try | |||
{ | |||
// 受信タスクの完了を待機 | |||
await _receiveTask; | |||
} | |||
catch (OperationCanceledException) | |||
{ | |||
// キャンセルによる例外は無視 | |||
} | |||
} | |||
// 使用したリソースの解放 | |||
_serialPort.Close(); // シリアルポートを閉じる | |||
_cts.Dispose(); // キャンセルトークンソースの破棄 | |||
_ackSemaphore.Dispose(); // セマフォの破棄 | |||
} | |||
/// <summary> | |||
/// プログラムのエントリーポイント | |||
/// </summary> | |||
static async Task Main(string[] args) | |||
{ | |||
// ポート名 | |||
string portName = "<ポート名 例: /dev/ttyS0>"; | |||
// シリアル通信を行う | |||
var serialComm = new SerialCommunication(portName); | |||
await serialComm.StartAsync(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
// 使用例 | |||
var serialComm = new SerialCommunication("<シリアルポート名 例: /dev/ttyS0>"); | |||
await serialComm.StartAsync(); | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C_Sharp]] | [[カテゴリ:C_Sharp]] |