パソコンでWindowsプログラムを作成するときに、Visual Studioのツールボックスからコントロールをドラッグ&ドロップで、Windowsフォーム上に配置する方法は、パソコン画面を見ながら各コントロールを配置できるので、画面を作成し易くなります。Windowsフォームを動的に切り替えて、様々なデータを入力してメッセージに編集するシリアル転送プログラムを作成します。
動作環境
- Windows 10 Professional
- Visual Studio 2019
作成するシリアル転送プログラムについて
シリアル転送プログラムの仕様
2階層のメニューがあり、それぞれ入力するデータが異なります。入力されたデータをメッセージに編集し、シリアルインタフェースで相手装置に送信します。相手装置からは、そのメッセージの応答が送信されます。
フォームデザイン方針
2階層のメニューは、上位のメニューはラジオボタンで表現し、下位のメニューはコンポボックスで表現します。メニューごとに異なるコントロールを抽出してパネル上に配置し、これをテンプレートとして一つのフォーム上にメニューごとのパネルを配置します。出来上がったパネルの情報の部分だけを関数化して、プログラム実行時に呼び出し、以降、ラジオボタンで切り替えるごとに、対応するパネルを画面に表示します。コンポボックスでメニューを切り替えるごとに、各コントロールの有効/無効を制御します。
シリアル転送プログラムの画面の作成手順
フォームデザイン方針に従って、次の手順で今回のプログラム「シリアル転送プログラム」の画面ーGUIを作成します。
- 作成したプログラムの画面を次に示します。
- テンプレートとして作成したメニューごとのコントロール配置を次に示します。
- テンプレートのフォームからパネルの情報の部分だけを切り出して関数化したコードを次に示します。テンプレートフレーム自身は削除し、各パネルの配置のコード(25行目と66行目)をコメントします。
- シリアル転送プログラムコードを次に示します。
- 2階層メニュー処理は、上位のメニューを左側のラジオボタンに割り当てます。番号4のシリアル転送プログラムコードの64行目から76行目が実行コマンドボタン、78行目から88行目が設定コマンドボタンが押された時の処理を示します。ここでは、テンプレートで作成したパネルから選択して表示します。下位のメニューは上部に配置しているコンポボックスに割り当てます。番号4のシリアル転送プログラムコードの49行目から62行目でラジオボタンの状態により、表示されているコントロールの有効無効を次のExecMessageメソッドあるいはSetMessageメソッドで制御しています。
rivate void InitializeTemplate()
{
this.PnelExec = new System.Windows.Forms.Panel();
this.txbDist = new System.Windows.Forms.TextBox();
this.cmbArea = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.pnlSet = new System.Windows.Forms.Panel();
this.txbTemp = new System.Windows.Forms.TextBox();
this.cmbFlower = new System.Windows.Forms.ComboBox();
this.txbCount = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.PnelExec.SuspendLayout();
this.pnlSet.SuspendLayout();
this.SuspendLayout();
//
// pnlExec
//
this.PnelExec.Controls.Add(this.txbDist);
this.PnelExec.Controls.Add(this.cmbArea);
this.PnelExec.Controls.Add(this.label1);
this.PnelExec.Controls.Add(this.label2);
//this.PnelExec.Location = new System.Drawing.Point(28, 22);
this.PnelExec.Location = new System.Drawing.Point(0, 0);
this.PnelExec.Name = "pnlExec";
this.PnelExec.Size = new System.Drawing.Size(387, 72);
this.PnelExec.TabIndex = 6;
//
// txbDist
//
this.txbDist.Location = new System.Drawing.Point(46, 12);
this.txbDist.Name = "txbDist";
this.txbDist.Size = new System.Drawing.Size(55, 19);
this.txbDist.TabIndex = 2;
//
// cmbArea
//
this.cmbArea.FormattingEnabled = true;
this.cmbArea.Items.AddRange(new object[] {
"エリアA",
"エリアB",
"エリアC"});
this.cmbArea.Location = new System.Drawing.Point(151, 12);
this.cmbArea.Name = "cmbArea";
this.cmbArea.Size = new System.Drawing.Size(102, 20);
this.cmbArea.TabIndex = 4;
//
// label1
//
this.label1.AutoSize = true;
・・・
this.label2.Size = new System.Drawing.Size(29, 12);
this.label2.TabIndex = 3;
this.label2.Text = "領域";
//
// pnlSet
//
this.pnlSet.Controls.Add(this.txbTemp);
this.pnlSet.Controls.Add(this.cmbFlower);
this.pnlSet.Controls.Add(this.txbCount);
this.pnlSet.Controls.Add(this.label4);
this.pnlSet.Controls.Add(this.label8);
this.pnlSet.Controls.Add(this.label10);
//this.pnlSet.Location = new System.Drawing.Point(28, 117);
this.pnlSet.Location = new System.Drawing.Point(0, 0);
this.pnlSet.Name = "pnlSet";
this.pnlSet.Size = new System.Drawing.Size(387, 72);
this.pnlSet.TabIndex = 6;
//
// txbTemp
//
this.txbTemp.Location = new System.Drawing.Point(151, 38);
this.txbTemp.Name = "txbTemp";
this.txbTemp.Size = new System.Drawing.Size(55, 19);
this.txbTemp.TabIndex = 2;
//
// cmbFlower
//
this.cmbFlower.FormattingEnabled = true;
・・・
this.label10.Size = new System.Drawing.Size(17, 12);
this.label10.TabIndex = 3;
this.label10.Text = "花";
}
private Serial serial;
int read = 0;
byte[] data;
private int btnID;
private string[] execMessage = {
"exec-0",
"exec-1 ",
"exec-2"};
private string[] setMessage = {
"set-0",
"set-1 ",
"set-2"};
public Panel()
{
InitializeComponent();
InitializeTemplate();
cmbMessage.SelectedIndex = 0;
btnSend.Enabled = false;
btnID = 0;
this.pnlInput.Controls.Add(this.PnelExec);
cmbArea.SelectedIndex = 0;
ExecMessage();
serial = new Serial();
}
private void btnSend_Click(object sender, EventArgs e)
{
string outdata = null;
switch (btnID)
{
case 0:
outdata = ExecSendData();
break;
case 1:
outdata = SetSendData();
break;
default:
break;
}
serial.writeserial(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ") + outdata + "\r\n");
}
private void cmbMessage_SelectedIndexChanged(object sender, EventArgs e)
{
switch (btnID)
{
case 0:
ExecMessage();
break;
case 1:
SetMessage();
break;
default:
break;
}
}
private void rdoExec_CheckedChanged(object sender, EventArgs e)
{
if (rdoExec.Checked)
{
btnID = 0;
this.pnlInput.Controls.Clear();
this.pnlInput.Controls.Add(this.PnelExec);
this.cmbMessage.Items.Clear();
this.cmbMessage.Items.AddRange(execMessage);
cmbArea.SelectedIndex = 0;
cmbMessage.SelectedIndex = 0;
}
}
private void rdoSet_CheckedChanged(object sender, EventArgs e)
{
if (rdoSet.Checked)
{
btnID = 1;
this.pnlInput.Controls.Clear();
this.pnlInput.Controls.Add(this.pnlSet);
this.cmbMessage.Items.Clear();
this.cmbMessage.Items.AddRange(setMessage);
cmbFlower.SelectedIndex = 0;
cmbMessage.SelectedIndex = 0;
}
}
private async void rdoconnect_CheckedChanged(object sender, EventArgs e)
{
if (rdoconnect.Checked)
{
try
{
int id = System.Threading.Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("btnconnect_Click1 ThreadID : " + id);
Console.Write("btnconnect_Click1\n");
serial.openserial("COM11");
btnSend.Enabled = true;
while (true)
{
await Task.Run(() => serial.readserial(out read, out data));
Console.Write("btnconnect_Click2\n");
id = System.Threading.Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("btnconnect_Click2 ThreadID : " + id);
txbLog.AppendText(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " readserial len: " + read + " data: " +
BitConverter.ToString(data) + "\r\n");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void rdodisconnect_CheckedChanged(object sender, EventArgs e)
{
if (rdodisconnect.Checked)
{
serial.closeserial();
btnSend.Enabled = false;
}
}
private void ExecMessage()
{
switch (cmbMessage.SelectedIndex)
{
case 0:
txbDist.Enabled = true;
cmbArea.Enabled = true;
break;
case 1:
txbDist.Enabled = false;
cmbArea.Enabled = true;
break;
case 2:
txbDist.Enabled = true;
cmbArea.Enabled = false;
break;
default:
break;
}
}
private void SetMessage()
{
switch (cmbMessage.SelectedIndex)
{
case 0:
txbCount.Enabled = true;
txbTemp.Enabled = true;
cmbFlower.Enabled = true;
break;
case 1:
txbCount.Enabled = false;
txbTemp.Enabled = true;
cmbFlower.Enabled = false;
break;
case 2:
txbCount.Enabled = true;
txbTemp.Enabled = false;
cmbFlower.Enabled = true;
break;
default:
break;
}
}
private string ExecSendData()
{
string outdata = null;
switch (cmbMessage.SelectedIndex)
{
case 0:
outdata = txbDist.Text+cmbArea.SelectedItem.ToString();
break;
case 1:
outdata = cmbArea.SelectedItem.ToString();
break;
case 2:
outdata = txbDist.Text;
break;
default:
break;
}
return outdata;
}
private string SetSendData()
{
string outdata = null;
switch (cmbMessage.SelectedIndex)
{
case 0:
outdata = txbCount.Text + txbTemp.Text +cmbFlower.SelectedItem.ToString();
break;
case 1:
outdata = txbTemp.Text;
break;
case 2:
outdata = txbCount.Text +cmbFlower.SelectedItem.ToString();
break;
default:
break;
}
return outdata;
}
シリアル転送クラスの作成
シリアル転送クラスは「C#Taskクラスを使ってシリアル通信の非同期処理」を参照し、クラス化「クラス名:Serial」して送信メソッドを追加します。また、「com0comによるシリアル接続の試験環境の構築」に示す仮想シリアルポートを使用して、シリアル転送の試験を行います。
番号4のシリアル転送プログラムコードの28行目でシリアル転送クラスをインスタンス化し、接続ボタンを押すと92行目から122行目でシリアル回線をオープン(openserialメソッド)してデータを読み込み(readserialメソッド)ます。データの読み込みはTask命令により非同期動作としました。送信ボタンを押すと、31行目から47行目でラジオボタンの選択状況に応じてデータを入力し、メッセージに編集して送信(writeserialメソッド)します。なおメッセージの編集については、番号5の44行目から82行目に示す ExecSendDataメソッドやSetSendDataメソッドが呼び出されます。
シリアル転送プログラムの実行
作成したシリアル転送プログラムを起動し、設定コマンドボタンを押して、コンポボックスで「set-1」を選択し、接続ボタンを押すと次のように設定できるコントロール、この場合「温度」のみが設定可能となります。
TeraTermアプリを起動し、シリアルで「COM12」に接続します。温度に「11」を設定して送信ボタンを押すと、TeraTermの画面に「xxxx 11」(xxxx:日時)が表示されます。TeraTermアプリから「TomoSoft」を入力すると、画面下のテキストボックスに次のように表示されます。仕様上、9バイトごとバイナリで16進数表示します。



