パソコンで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進数表示します。