ピンチェンジとは
RS-232Cには相手の情報をレベル(電圧)で読み込むコネクタピンが4つ有ります。
1、CD
2、DSR (相手はDTR)
3、CTS (相手はRTS)
4、RI
この中で通常は1番と4番は結線されていません(PC対PCのクロスケーブルを例にしています)。
これらのピンの電圧が変化するとserialPort(SerilPort)クラスのpinChangedイベントが発生して、変化を
知らせます。
このイベントの中でDSRはDSRHoldingのValuを CTSはCTSHoldingのValuを読み取ることにより、
ピンの状態を取得することが出来ます。
ValueはHighレベルの時はTrueにLowレベルの時はFalseとなります(RS-232CはHighアクティブです)。
ここではserialPort(SerilPort)クラスを使って、RS-232Cのピンチェンジの表示と、其のログを書き出す
方法を紹介します。
マルチスレッド
serialPortクラスのイベントは全て(3つ)別スレッドで発生します。
別スレッド上での処理は幾つかの注意点があります、特に別スレッドからメインスレッドの
コントロールにアクセスする場合はマルチスレッドの知識が必要になります。
コードサンプルを見ていただくと、serialPortクラスのイベントの別スレッド上の処理が理解できると思います。
基礎知識としては、 「マルチスレッド」
「デリゲート」 「RS-232Cの基礎」 が必要となります。
画面設計
先ずフォームに ラベルを3つ貼り付け、名前をそれぞれ、lblOpen、lblDSR、lblCTSとします。
(VB.NETの場合はLblOpen、LblDSR、LblCTSとなります、以下小文字大文字は省略します。)
次に、ボタンとテキストボックスを貼り付け、butOpenとtxtLogという名前にします。
lblDSRは相手の機器のDTRがOnになっていてこちら側のDSRHoldingがTrueになっている場合は、
明るい緑で表示され、Offの場合(こちらのDSRHoldingがFalseの場合)は暗い緑で表示されます。
lblCTSも同じでCTSHoldingのValueにより表示が変化します。
オープンボタン
butOpenを押すとCom1がオープンされlblOpenが明るい緑色のバックカラーで表示されます。
又ログにはオープンの時間とOpenのログが表示されます。
butOpenボタンはComオープン時に押すとComはOffとなります。
こちらのComOpen時に接続先Comが既にOpen状態で、DTRがOnになっている場合はDSRHoldingがTrueに、
RTSがOnになっている場合はCTSHoldingがTrueになりますから、
其の場合はlblDSR又はlblCTSのバックカラーを明るい緑に変えます。
上記にの場合はピンチェンジイベントは発生しません。
butOpenの表示はCom1がCloseの場合は「Open」にCom1がOpenの場合は「Close」となります。
実際のコード
ラベルの色の変更はsetHiColor()、setDarkColor()メソドに変更するラベルを引数にして処理します。
以下がそのコードです。
C#のコード
//ラベルを引数に取って、ラベルの文字色を背景色を変える
private void setHiColor(Label label)
{
//Onの表示色に変える
label.ForeColor = Color.Blue;
label.BackColor = Color.LightGreen;
}
private void setDarkColor(Label label)
{
//Offの表示食に変える
label.ForeColor = Color.White;
label.BackColor = Color.DarkGreen;
}
VB.NETのコード
'//ラベルを引数に取って、ラベルの文字色を背景色を変える
Private Sub setHiColor(ByVal label As Label)
'//Onの表示色に変える
label.ForeColor = Color.Blue
label.BackColor = Color.LightGreen
End Sub
Private Sub setDarkColor(ByVal label As Label)
'//Offの表示色に変える
label.ForeColor = Color.White
label.BackColor = Color.DarkGreen
End Sub
マルチスレッドとデリゲート
さて肝心のピンチェンジイベントですが、これはRS-232CのI信号のDSRとCTSが変化した場合に
イベントが起きます。
(詳しくは 「RS-232Cの基礎」 を御覧下さい。)
このイベントはメインスレッドとは別のスレッドで起きる為に、この情報をメインスレッドのテキスト・ボックスに
直接書き込むことは出来ません。
そのためにデリゲートを使用します。
#ラベルの色は他のスレッドからでも変更できます、ラベルのテキストは変更できません。
#マルチスレッドに関しては、 「マルチスレッドの基礎」 を御覧下さい。
#デリゲートに関しては、 「デリゲートの基礎」 を御覧下さい。)
デリゲートの宣言は以下のコードになります。
このデリゲートはBoolの引数を取ります。
C#のコード
//デリゲートを宣言
private delegate void myDelegate(bool flg);
VB.NETのコード
'//デリゲートを宣言
Delegate Sub myDelegate(ByVal flg As Boolean)
デリゲートから呼ばれるメソド
上のデリゲートから呼ばれるメソドを以下に示します。
デリゲートに渡される引数は、Onの場合がTrueでOffの場合がFalseとなります。
この中ではラベルの色の変更とテキストボックスへのLogの書き込みを行なっています。
テキストボックスのテキストに新しいテキストを「+」や「&」で足しこもうと考えた方が居るかもしれませんが、
其の考えはもう止めましょう。
文字列の足し算は時間がかかる処理です、FrameWorkのテキストボックスのテキストにはAppendText()と言う、
StringBuilder.Append()から継承されたメソドがありますのでこれを使用します。
C#のコード
//DSRHolding表示用
private void setDSR(bool boolOn)
{
if (boolOn)
{
setHiColor(lblDSRhold);
txtLog.AppendText(DateTime.Now.ToString() + ": DSR On \r\n");
}
else
{
setDarkColor(lblDSRhold);
txtLog.AppendText (DateTime.Now.ToString() + ": DSR Off \r\n");
}
}
//CTSHolding表示用
private void setCTS(bool boolOn)
{
if (boolOn) //CTSHoldingがOn
{
setHiColor(lblCTSHold );
txtLog.AppendText(DateTime.Now.ToString() + ": CTS On \r\n");
}
else //CTSHoldingがOff
{
setDarkColor(lblCTSHold);
txtLog.AppendText ( DateTime.Now.ToString() + ": CTS Off \r\n");
}
}
VB.NETのコード
'//DSRHolding表示用
Private Sub setDSR(ByVal boolOn As Boolean)
If boolOn Then
setHiColor(lblDSRhold)
txtLog.AppendText(DateTime.Now.ToString() + ": DSR On" & Environment.NewLine)
Else
setDarkColor(lblDSRhold)
txtLog.AppendText(DateTime.Now.ToString() + ": DSR Off" & Environment.NewLine)
End If
End Sub
'//CTSHolding表示用
Private Sub setCTS(ByVal boolOn As Boolean)
If boolOn Then '//CTSHoldingがOn
setHiColor(lblCTSHold)
txtLog.AppendText(DateTime.Now.ToString() + ": CTS On" & Environment.NewLine)
Else '//CTSHoldingがOff
setDarkColor(lblCTSHold)
txtLog.AppendText(DateTime.Now.ToString() + ": CTS Off" & Environment.NewLine)
End If
End Sub
ピンチェンジイベント内でのコード
前にも書きましたが、ピンチェンジイベントは4つのピンの状態状態の変化が起こった時に発生します。
1、CD
2、DSR
3、CTS
4、RI
ただしこの内1番ピンと4番ピンは接続されていませんので、イベントは起こりません。
(詳しくは 「RS-232Cの基礎」 を参照して下さい。)
serialPortクラスのPinChengedイベントハンドラー内では、どのイベントが発生したかを
SerialPinChangedEventArgsのEventTypeで判別して、DSRの場合とCTSの場合に分けて
処理を行ないます。
先ず、
C#のコード
myDelegate dlgDSR = new myDelegate(setDSR);
myDelegate dlgCTS = new myDelegate(setCTS);
VB2005のコード
Dim dlgDSR As New myDelegate(AddressOf setDSR)
Dim dlgCTS As New myDelegate(AddressOf setCTS)
とデリゲートのインスタンスを2つ作成し、それぞれsetDSR、setCTSの参照を設定します。
VB.NETの場合は参照のイベント名にAddressOfが付くことに注意して下さい。
さて実際の呼び出しは、
C#のコード
if (serialPort1.CtsHolding) //CTS On
this.Invoke(dlgCTS,true);
else
this.Invoke(dlgCTS,false ); //CTS Off
break;
VB2005のコード
If SerialPort1.CtsHolding Then '//CTS On
Me.Invoke(dlgCTS, True)
Else
Me.Invoke(dlgCTS, False) ' //CTS Off
End If
this.Invoke()(VB.NERの場合Me.Invoke())はフォームの持つInvokeメソドによりメインフォームでの処理を強要しているのです。
すなわち、this.Invoke(dlgCTS,true);(VB.NETの場合 Me.Invoke(dlgCTS, True))はFormの有るメインスレッドで
setCTSメソドを参照しているデーゲートをtrue(True)の引数付きで実行せよと言うことです。
textBox(TextBiox)クラスやlabel(Label)は全てFormから継承されていますので、全てInvokeメソドを持っています。
従って、上記のごとく書く代わりにtxtLog.Invoke(dlgCTS, True)やlblCTS.Invoke(dlgCTS, True)と言った
書き方でもOKです。
txtLog.Invoke(dlgCTS, True)これはtxtLogが有るメインスレッドでTrueの引数を持ったdlgCTSデリゲートを実行しなさいと言うことです。
Invoke(dlgCTS,true);(Invoke(dlgCTS, True))でもOKですが、これはthis(Me)が省略された書き方になります。
C#とVB.NETのサンプルコード
C#の場合は
using System.IO.Ports;
VB.NETの場合は
System.IO.Ports
をコードの最初に入れます。
C#はserialPortコントロールを
VB.NETはSerialPortコントロールを貼り付けます。
C#のコード
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports; //Portsを追加します
namespace Rs232cC
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
txtLog.AppendText(DateTime.Now.ToString() + ": プログラムスタート \r\n");
SetAllColor(); //ラベルの色を初期化します。
}
//デリゲートを宣言
private delegate void myDelegate(bool flg);
//ピンチェンジイベントはメインスレッドとは別のスレッドで行なわれる
private void serialPort1_PinChanged(object sender,
System.IO.Ports.SerialPinChangedEventArgs e)
{
//デリゲートのインスタンス作成と参照を設定
myDelegate dlgDSR = new myDelegate(setDSR);
myDelegate dlgCTS = new myDelegate(setCTS);
//ピンチェンジの判別
switch(e.EventType)
{
case SerialPinChange.CtsChanged:
//CTSのPinが変更された
if (serialPort1.CtsHolding) //CTS On
this.Invoke(dlgCTS,true);
else
this.Invoke(dlgCTS,false ); //CTS Off
break;
case SerialPinChange.DsrChanged:
//DSRのPinが変更された
if (serialPort1.DsrHolding) //DSR On
this.Invoke(dlgDSR ,true);
else
this.Invoke(dlgDSR,false); //DSR off
break;
}
}
//ポートOn/Offボタン押下
private void butOpen_Click(object sender, EventArgs e)
{
//既にOpenされているか?
if (!serialPort1.IsOpen)
{
//Openされていない
serialPort1.PortName = "Com1"; //ComNameの設定
serialPort1.Open(); //ポートオープン
butOpen.Text = "Close"; //ポートクローズ
SetAllColor(); //表示の初期化
if (serialPort1.IsOpen)
{
//Open表示用のLabelの色を変える
setHiColor(lblOpen);
//ログを書き込む
txtLog.AppendText(DateTime.Now.ToString() + ": PortOpen \r\n");
}
else
//オープン出来ない
MessageBox.Show("ポートがオープンで出来ません。",
"ポートオープンエラー");
}
else
{
//既にオープンされているので閉じる。
butOpen.Text = "Open"; //ボタンの表示変更
serialPort1.Close() ; //ポートをクローズする
//Logの書き込み
txtLog.AppendText(DateTime.Now.ToString() + ": PortClose \r\n");
//表示の初期化
SetAllColor();
}
}
//DSRHolding表示用
private void setDSR(bool boolOn)
{
if (boolOn)
{
setHiColor(lblDSRhold);
txtLog.AppendText(DateTime.Now.ToString() + ": DSR On \r\n");
}
else
{
setDarkColor(lblDSRhold);
txtLog.AppendText (DateTime.Now.ToString() + ": DSR Off \r\n");
}
}
//CTSHolding表示用
private void setCTS(bool boolOn)
{
if (boolOn) //CTSHoldingがOn
{
setHiColor(lblCTSHold );
txtLog.AppendText(DateTime.Now.ToString() + ": CTS On \r\n");
}
else //CTSHoldingがOff
{
setDarkColor(lblCTSHold);
txtLog.AppendText ( DateTime.Now.ToString() + ": CTS Off \r\n");
}
}
//ラベルを引数に取って、ラベルの文字色を背景色を変える
private void setHiColor(Label label)
{
//Onの表示色に変える
label.ForeColor = Color.Blue;
label.BackColor = Color.LightGreen;
}
private void setDarkColor(Label label)
{
//Offの表示色に変える
label.ForeColor = Color.White;
label.BackColor = Color.DarkGreen;
}
//表示色のリセット
private void SetAllColor()
{
//通信相手のDTR,RTSがOnの場合はPortをOpenしたら
//ピンチェンジイベント無しでDsrHolding,CtsHoldingが
//Onとなる。
if (serialPort1.IsOpen)
{
if (serialPort1.DsrHolding)
{
setHiColor(lblDSRhold);//DsrHolding On
}
else
{
setDarkColor(lblDSRhold);//DsrHolding Off
}
if (serialPort1.CtsHolding)
{
setHiColor(lblCTSHold); //CtsHolding On
}
else
{
setDarkColor(lblCTSHold); //CtsHolding Off
}
}
else
{
//全ての表示をOffにする
setDarkColor(lblOpen);
setDarkColor(lblDSRhold);
setDarkColor(lblCTSHold);
}
}
}
}
VB.NETのコード
Option Strict On
Imports System.IO.Ports
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
txtLog.AppendText(DateTime.Now.ToString() + ": プログラムスタート\r\n")
SetAllColor() '//ラベルの色を初期化します。
End Sub
'//ラベルを引数に取るデリゲートを宣言
Delegate Sub myDelegate(ByVal flg As Boolean)
'//ピンチェンジイベントはメインスレッドとは別のスレッドで行なわれる
Private Sub serialPort1_PinChanged(ByVal sender As Object, _
ByVal e As SerialPinChangedEventArgs)
'//デリゲートのインスタンス作成と参照を設定
Dim dlgDSR As New myDelegate(AddressOf setDSR)
Dim dlgCTS As New myDelegate(AddressOf setCTS)
'//ピンチェンジの判別
Select Case e.EventType
Case SerialPinChange.CtsChanged
'//CTSのPinが変更された
If SerialPort1.CtsHolding Then '//CTS On
Me.Invoke(dlgCTS, True)
Else
Me.Invoke(dlgCTS, False) ' //CTS Off
End If
Case SerialPinChange.DsrChanged
'//DSRのPinが変更された
If SerialPort1.DsrHolding Then '//DSR On
Me.Invoke(dlgDSR, True)
Else
Me.Invoke(dlgDSR, False) '//DSR Off
End If
End Select
End Sub
'//ポートOn/Offボタン押下
Private Sub ButOpen_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles butOpen.Click
'//既にOpenされているか?
If Not SerialPort1.IsOpen Then
'//Openされていない
SerialPort1.PortName = "Com1" '//ComNameの設定
SerialPort1.Open() '//ポートオープン
butOpen.Text = "Close" '//ポートクローズ
SetAllColor() '//表示の初期化
If (SerialPort1.IsOpen) Then
'//Open表示用のLabelの色を変える
setHiColor(lblOpen)
'//ログを書き込む
txtLog.AppendText(DateTime.Now.ToString() + ": PortOpen" & _
Environment.NewLine)
Else
'//オープン出来ない
MessageBox.Show("ポートがオープンで出来ません。", _
"ポートオープンエラー")
End If
Else
'既にオープンされているので閉じる。
butOpen.Text = "Open" '//ボタンの表示変更
SerialPort1.Close() '//ポートをクローズする
'//Logの書き込み
txtLog.AppendText(DateTime.Now.ToString() + ": PortClose " & _
Environment.NewLine)
'//表示の初期化
SetAllColor()
End If
End Sub
'//DSRHolding表示用
Private Sub setDSR(ByVal boolOn As Boolean)
If boolOn Then
setHiColor(lblDSRhold)
txtLog.AppendText(DateTime.Now.ToString() + ": DSR On" & _
Environment.NewLine)
Else
setDarkColor(lblDSRhold)
txtLog.AppendText(DateTime.Now.ToString() + ": DSR Off" & _
Environment.NewLine)
End If
End Sub
'//CTSHolding表示用
Private Sub setCTS(ByVal boolOn As Boolean)
If boolOn Then '//CTSHoldingがOn
setHiColor(lblCTSHold)
txtLog.AppendText(DateTime.Now.ToString() + ": CTS On" & _
Environment.NewLine)
Else '//CTSHoldingがOff
setDarkColor(lblCTSHold)
txtLog.AppendText(DateTime.Now.ToString() + ": CTS Off" & _
Environment.NewLine)
End If
End Sub
'//ラベルを引数に取って、ラベルの文字色を背景色を変える
Private Sub setHiColor(ByVal label As Label)
'//Onの表示色に変える
label.ForeColor = Color.Blue
label.BackColor = Color.LightGreen
End Sub
Private Sub setDarkColor(ByVal label As Label)
'//Offの表示食に変える
label.ForeColor = Color.White
label.BackColor = Color.DarkGreen
End Sub
'//表示色のリセット
Private Sub SetAllColor()
'//通信相手のDTR,RTSがOnの場合はPortをOpenしたら
'//ピンチェンジイベント無しでDsrHolding,CtsHoldingが
'//Onとなる。
If SerialPort1.IsOpen Then
If SerialPort1.DsrHolding Then
setHiColor(lblDSRhold) '//DsrHolding On
Else
setDarkColor(lblDSRhold) '//DsrHolding Off
End If
If SerialPort1.CtsHolding Then
setHiColor(lblCTSHold) '//CtsHolding On
Else
setDarkColor(lblCTSHold) '//CtsHolding Off
End If
Else
'//全ての表示をOffにする
setDarkColor(lblOpen)
setDarkColor(lblDSRhold)
setDarkColor(lblCTSHold)
End If
End Sub
End Class
|