-
指定された Decimal Double Long の値を、指定された桁で切り上げる関数です。
指定された値が正の場合は、有効桁数の10の累乗で割ったものを Math.Ceiling 関数で最小の整数値を求めて、更に累乗値を掛けてやります。 指定された値が負の場合は、有効桁数の10の累乗で割ったものを Math.Floor 関数で最小の整数値を求めて、更に累乗値を掛けてやります
テストプログラムの動作としては、Double値が「123456」で有効桁数が「3」の場合、「123456」を先ず「1000」で割って「123.456」となり Math.Ceiling 関数を通すことで「124.000」となり結果「124000」が関数戻り値となります。指定した精度の桁数に数値を切り上げ
''' ------------------------------------------------------------------------ '''''' 指定した精度の数値の切り上げ ''' ''' <param name="Val">丸め対象の倍精度浮動小数点数</param> ''' <param name="intUnit">戻り値の有効桁数の精度</param> '''切り上げられた数値。 ''' ------------------------------------------------------------------------ Public Function RoundUp(ByVal Val As Double, ByVal intUnit As Integer) As Double Dim dblPow As Double = System.Math.Pow(10, intUnit) If Val > 0 Then 'Ceiling:指定した倍精度浮動小数点数以上の数のうち、最小の整数値を返します Return System.Math.Ceiling(Val / dblPow) * dblPow Else 'Floor:指定した倍精度浮動小数点数以下の数のうち、最大の整数を返します Return System.Math.Floor(Val / dblPow) * dblPow End If End Function Public Function RoundUp(ByVal Val As Decimal, ByVal intUnit As Integer) As Decimal Dim decPow As Decimal = System.Math.Pow(10, intUnit) If Val > 0 Then 'Ceiling:指定した 10 進数以上の数のうち、最小の整数値を返します Return System.Math.Ceiling(Val / decPow) * decPow Else 'Floor:指定した 10 進数以下の数のうち、最大の整数を返します Return System.Math.Floor(Val / decPow) * decPow End If End Function Public Function RoundUp(ByVal Val As Long, ByVal intUnit As Integer) As Long Dim decPow As Decimal = System.Math.Pow(10, intUnit) If Val > 0 Then Return System.Math.Ceiling(Val / decPow) * decPow Else Return System.Math.Floor(Val / decPow) * decPow End If End Function '***** '仮にテストプログラム '***** Private Sub BtnRoundUp_Click(sender As Object, e As EventArgs) Handles BtnRoundUp.Click Try Dim decData As Decimal = 123456 Dim dblData As Double = 123456 Dim lngData As Long = 123456 Dim str As String = "" str &= "Decimal:" & decData.ToString & "-->" & RoundUp(decData, 3).ToString & vbCrLf str &= "Double :" & dblData.ToString & "-->" & RoundUp(dblData, 2).ToString & vbCrLf str &= "Long :" & lngData.ToString & "-->" & RoundUp(decData, 1).ToString & vbCrLf decData = -decData dblData = -dblData lngData = -lngData str &= "Decimal:" & decData.ToString & "-->" & RoundUp(decData, 3).ToString & vbCrLf str &= "Double :" & dblData.ToString & "-->" & RoundUp(dblData, 2).ToString & vbCrLf str &= "Long :" & lngData.ToString & "-->" & RoundUp(decData, 1).ToString & vbCrLf MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub
PR -
指定されたコピー元のバイト配列から、指定されたコピー先バイト配列に、右詰でコピーを行う関数です。
関数としては、コピー元の配列数がコピー先の配列数より多い場合は、コピー先のコピー開始位置を右にずらして配列数分までをコピーします。
また、コピー元の配列数がコピー先の配列数より少ない場合、コピー先の残りに指定のバイト値を埋めます。右詰でバイト配列のコピーを行う
''' ----------------------------------------------------------------------------- '''''' 右詰でバイト配列のコピーを行う。 ''' ''' <param name="srcArr">コピー元バイト配列</param> ''' <param name="desArr">コピー先バイト配列</param> ''' <param name="fillByte">指定バイトデータ(規定値:0x00)</param> '''コピー元のバイト数が、コピー先より小さい場合に、先頭から余った領域にバイトデータを埋める。 ''' ----------------------------------------------------------------------------- Public Sub CopyByteArrRight(ByVal srcArr As Byte(), ByVal desArr As Byte(), Optional ByVal fillByte As Byte = &H0) Dim idx As Integer If srcArr.Length = 0 Then '元の配列長が0の場合、指定バイトデータを埋める For idx = 0 To desArr.Length - 1 desArr(idx) = fillByte Next Else 'コピー先に収まるバイト数を計算 Dim copyBytes As Integer = srcArr.Length 'コピー元の長さ If srcArr.Length > desArr.Length Then 'コピー元の長さがコピー先の長さより大きい場合、コピー先の長さをコピー数にする copyBytes = desArr.Length End If 'コピー元の長さで、コピー先の開始位置を右にずらして、左からコピー Array.Copy(srcArr, 0, desArr, desArr.Length - copyBytes, copyBytes) 'コピー元のバイト数が、コピー先のバイト数より小さい場合、指定バイトデータを埋める If copyBytes < desArr.Length Then For idx = 0 To (desArr.Length - copyBytes) - 1 desArr(idx) = fillByte Next End If End If End Sub '***** '仮にテストプログラム '***** Private Sub CopyArrRight_Click(sender As Object, e As EventArgs) Handles CopyArrRight.Click Try '配列宣言 Dim byteArr(10) As Byte 'コピー元 Dim byteArr2(12) As Byte 'コピー先 'コピー元配列を「&H55」で埋める FillByteArr(byteArr, &H55) ' 'コピー元からコピー先へコピーする CopyByteArrRight(byteArr, byteArr2, &HAA) '結果の表示 Dim str As String = "" For i = 0 To byteArr2.Length - 1 str &= String.Format("byteArr2[{0}] = {1}", i, byteArr2(i).ToString("X")) & vbCrLf Next MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub関連する記事
⇒配列の使い方について(Dim, Redim)
⇒配列の使い方の注意点について(コピー, Clone)
⇒配列の範囲指定によるコピー(Array.Copy, Skip, Take)
-
指定されたコピー元のバイト配列から、指定されたコピー先バイト配列に、左詰でコピーを行う関数です。
関数としては、コピー元の配列数がコピー先の配列数より多い場合は、コピー先の配列数分までをコピーします。
また、コピー元の配列数がコピー先の配列数より少ない場合、コピー先の残りに指定のバイト値を埋めます。左詰でバイト配列のコピーを行う
''' ----------------------------------------------------------------------------- '''''' 左詰でバイト配列のコピーを行う。 ''' ''' <param name="srcArr">コピー元バイト配列</param> ''' <param name="desArr">コピー先バイト配列</param> ''' <param name="fillByte">指定バイトデータ(規定値:0x00)</param> '''コピー元のバイト数が、コピー先より小さい場合に、余った領域に埋めるバイトデータを埋める。 ''' ----------------------------------------------------------------------------- Public Sub CopyByteArrLeft(ByVal srcArr As Byte(), ByVal desArr As Byte(), Optional ByVal fillByte As Byte = &H0) Dim idx As Integer If srcArr.Length = 0 Then 'コピー元の配列長が0の場合、指定バイトデータを埋める For idx = 0 To desArr.Length - 1 desArr(idx) = fillByte Next Else 'コピー先に収まるバイト数を計算 Dim copyBytes As Integer = srcArr.Length 'コピー元の長さ If srcArr.Length > desArr.Length Then 'コピー元の長さがコピー先の長さより大きい場合、コピー先の長さをコピー数にする copyBytes = desArr.Length End If '左からコピー Array.Copy(srcArr, desArr, copyBytes) 'コピー元のバイト数が、コピー先のバイト数より小さい場合、後ろに指定バイトデータ埋める If copyBytes < desArr.Length Then For idx = copyBytes To desArr.Length - 1 desArr(idx) = fillByte Next End If End If End Sub '***** '仮にテストプログラム '***** Private Sub CopyArrLeft_Click(sender As Object, e As EventArgs) Handles CopyArrLeft.Click Try '配列宣言 Dim byteArr(10) As Byte 'コピー元 Dim byteArr2(12) As Byte 'コピー先 'コピー元配列を「&H55」で埋める FillByteArr(byteArr, &H55) ' 'コピー元からコピー先へコピーする CopyByteArrLeft(byteArr, byteArr2, &HAA) '結果の表示 Dim str As String = "" For i = 0 To byteArr2.Length - 1 str &= String.Format("byteArr2[{0}] = {1}", i, byteArr2(i).ToString("X")) & vbCrLf Next MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub関連する記事
⇒配列の使い方について(Dim, Redim)
⇒配列の使い方の注意点について(コピー, Clone)
⇒配列の範囲指定によるコピー(Array.Copy, Skip, Take)
-
最近では通信と言えば、BlueTooth, USBやネットでの通信が当たり前で、 シリアルポート(RS232C)を使って外部装置と通信することも少なくなりました。 しかし稀に外部装置との接続をシリアル通信(調歩同期)で行うこともありますので 今回はシリアルポートを使う方法を説明します。
SerialPortコントロールを使うには、フォーム上にツールボックスからSerialPortを選択し貼り付けます。 以下の図は、SerialPortコントロールを貼り付けた後の様子です。
MSDNの説明によれば以下の様になっています。(代表的なものを示しています。青色は今回使用しているものです。)SerialPort コントロール・について
<プロパティ> ・PortName :通信用のポートを取得または設定します。 このポートには、使用可能なすべての COM ポートが含まれますが、 これに限定されるわけではありません。 ・BaudRate :シリアル ボー レートを取得または設定します。 ・BytesToRead :受信バッファー内のデータのバイト数を取得します。 ・BytesToWrite:送信バッファー内のデータのバイト数を取得します。 ・CtsHolding :Clear To Send ラインの状態を取得します。 ・DsrHolding :DSR (Data Set Ready) シグナルの状態を取得します。 ・IsOpen :SerialPort オブジェクトの開いている状態または 閉じた状態を示す値を取得します。 ・Parity :パリティ チェック プロトコルを取得または設定します。 ・DtrEnable :シリアル通信中に、DTR (Data Terminal Ready)シグナルを 有効にする値を取得または設定します。 ・DataBits :バイトごとのデータ ビットの標準の長さを取得または設定します。 ・StopBits :バイトごとのストップ ビットの標準の数を取得または設定します。 ・ReadBufferSize:SerialPortの入力バッファーのサイズを取得または設定します。 ・ReadTimeout :読み取り操作が完了していないときに、 タイムアウトになるまでのミリ秒数を取得または設定します。 <メソッド> ・Close() :ポート接続を閉じ、IsOpenプロパティを false に設定し、 内部 Stream オブジェクトを破棄します。 ・Open() :新しいシリアル ポート接続を開きます。 ・Read(Byte(), Int32, Int32): SerialPort の入力バッファーから複数のバイトを読み取り、 読み取ったバイトを指定したオフセットでバイト配列に書き込みます。 ・ReadByte() :SerialPort の入力バッファーから、同期で 1 バイトを読み取ります。 ・Write(Byte(), Int32, Int32): バッファーのデータを使用して、指定したバイト数をシリアル ポートに書き込みます。 <イベント> ・DataReceived:SerialPort オブジェクトによって表される ポートを介してデータが受信されたことを示します。
今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、 そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。
com0com というドライバで、PC上に仮想シリアルポートを2個作成し、 その2個をループバックできる様に仮想的に接続できるという優れものです。 以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。 (尚、もう一台のPCなどがあれば必要ありませんが)
⇒http://sourceforge.net/projects/com0com/
このドライバをインストールすると、私のPCではCOM7、COM8が追加で生成されました。
インストール後のPCの状況を以下の図に示します。
COM7側をテスト用送信プログラムとするのですが、 通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。
⇒http://nonsoft.la.coocan.jp/Download/Rs232cTool/index.html
この方はいろいろなフリーソフトを作成されている様です。使えるソフトが満載です。
インストール後の「RS232Cテストツール」を動作させている様子を以下に示します。
では実際のプログラムを以下に示しますので、説明します。
フォームにSerialPort、TextBox、Buttonを各1個ずつ貼り付けます。
フォームロードイベントでは、シリアルポートの設定を行い、ポートのオープン処理を行います。 ポートの設定は、ポート名「COM8」、通信速度「9600BPS」、パリティ「無し」、データビット数「8」、 ストップビット「1Bit」です。
SerialPortの受信イベントにより、受信バッファー内のデータのバイト数分のバイト配列を宣言し SerialPortのReadコマンドで受信バイトを全て読込みます。
読込んだバイト配列を文字列に変換し、TextBoxに設定します。
プログラム的には出来ている様な感じがしますが、これをデバッグモードで実行してみます。SerialPortコントロールの使い方(最初)
Public Class frmSerial '''''' フォームロードイベント ''' Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load With SerialPort1 'シリアルポートの設定 .PortName = "COM8" 'ポート名 .BaudRate = 9600 '通信速度指定 .Parity = IO.Ports.Parity.None 'パリティ指定 .DataBits = 8 'ビット数指定 .StopBits = IO.Ports.StopBits.One 'ストップビット指定 'シリアルポートのオープン .Open() End With End Sub '''''' フォームクローズイベント ''' Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'シリアルポートのクローズ SerialPort1.Close() End Sub '''''' シリアルポート受信イベント ''' Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived 'データ受信 Dim arrByte As Byte() = New Byte(SerialPort1.BytesToRead - 1) {} '受信バッファー内のデータのバイト数分 'SerialPort の入力バッファーからバイト数を読み取り SerialPort1.Read(arrByte, 0, arrByte.GetLength(0)) '受信バイト配列を文字列変換 Dim str As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(arrByte) 'テキストBOXに文字列表示関数 DisplayText(str) End Sub '''''' テキストBOXに文字列表示関数 ''' ''' <param name="strDisp">表示文字列</param> Private Sub DisplayText(ByVal strDisp As String) 'テキストBOXに文字列を追加 Me.TextBox1.Text &= strDisp End Sub '''''' [Clear]ボタン処理 ''' Private Sub btnClear_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnClear.Click Me.TextBox1.Text = "" End Sub End Classこのプログラムをデバッグモードで実行させて待機状態にして置き、 先ほどの「RS232Cテストツール」を実行し、送信TEXTにデータを設定し、送信します。 送信ボタン押下した時に、今回のプログラムのTextBoxにデータが表示されるはずですが 実際は以下の様なエラーが発生しました。
スレッド間のアクセスが起きてエラーが発生した様です。
そこで調べたところ、MSDNの説明によれば、
DataReceived イベントは、データが SerialPort オブジェクトから受信されたときにセカンダリ スレッドで発生します。 メインの Form または Control で要素を変更する必要がある場合は、 Invoke を使用して変更要求をポストバックします。これにより、適切なスレッドで処理が実行されるようになります。
とあります。結局、DataReceivedイベントは別スレッドなので、フォームのコントロールなどにアクセスする場合は Invoke等を使用しなさいとのことの様です。
プログラムを以下の様に書き換えました。SerialPortコントロールの使い方(正常処理)
Public Class frmSerial '''''' フォームロードイベント ''' Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load With SerialPort1 'シリアルポートの設定 .PortName = "COM8" 'ポート名 .BaudRate = 9600 '通信速度指定 .Parity = IO.Ports.Parity.None 'パリティ指定 .DataBits = 8 'ビット数指定 .StopBits = IO.Ports.StopBits.One 'ストップビット指定 'シリアルポートのオープン .Open() End With End Sub '''''' フォームクローズイベント ''' Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'シリアルポートのクローズ SerialPort1.Close() End Sub '''''' シリアルポート受信イベント ''' Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived 'データ受信 Dim arrByte As Byte() = New Byte(SerialPort1.BytesToRead - 1) {} '受信バッファー内のデータのバイト数分 'SerialPort の入力バッファーからバイト数を読み取り SerialPort1.Read(arrByte, 0, arrByte.GetLength(0)) 'デリゲート生成 Dim dlg As New DisplayTextDelegate(AddressOf DisplayText) '受信バイト配列を文字列変換 Dim str As String = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetString(arrByte) 'デリゲート関数をコールする Me.Invoke(dlg, New Object() {str}) End Sub 'Invokeメソッドで使用するデリゲート宣言 Delegate Sub DisplayTextDelegate(ByVal strDisp As String) '''''' テキストBOXに文字列表示関数 ''' ''' <param name="strDisp">表示文字列</param> Private Sub DisplayText(ByVal strDisp As String) 'テキストBOXに文字列を追加 Me.TextBox1.Text &= strDisp End Sub '''''' [Clear]ボタン処理 ''' Private Sub btnClear_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnClear.Click Me.TextBox1.Text = "" End Sub End Class
USB ⇒ RS232C 変換ケーブル
シリアル通信に関連してですが、 最近のPC、特にノートPCの場合ではRS232Cのコネクタがついていることは殆んどありません。
そのため、以下の様なUSBを使ったRS232Cの変換ケーブルが使えそうです。
⇒iBUFFALO USBシリアルケーブル(USBtypeA to D-sub9ピン)1.0m ブラックスケルトン BSUSRC0610BS
⇒サンワサプライ USB-RS232Cコンバータ USB-CVRS9
⇒Plugable USB‐9ピンRS232シリアルアダプター (Prolific社製 PL2303HX Rev Dチップセット採用)
関連する記事
⇒SerialPortコントロールの使い方その2
⇒SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)
⇒SerialPortコントロールの使い方その4(データ受信時にイベントを発生させる)
⇒SerialPortコントロールの使い方その5(データ受信時及び、信号変化時にイベントを発生させる)
⇒SerialPortコントロールの使い方その6(ハンドシェィクによるデータ送受信)
おすすめ本
-
フォーム上にある同じ種類のコントロールを配列に入れて、 昔のVisualBasicの様にコントロール配列的に処理したいことはよくあります。 そこで今回は、フォーム上に設置した5個の TextBox と Button を配列に入れて処理を行う方法を示します。
以下の画像は、今回のフォームを実行した場合の表示で、Button4 を押下した時の様子です。
以下のソースは、フォームロード時に「コントロール配列」に TextBox1~5 及び Button1~5 を設定し 各ボタンのクリックイベントの処理関数を AddHandler で設定しています。
ボタンのクリックイベントではイベント発生が在ったオブジェクト sender がどのボタンに該当するのかの 配列の指標を取得します。 その指標を使って、TextBox と Button のプロパティにアクセスし、そのデータを表示しています。コントロールを配列で処理する方法
Public Class frmCtrlIdx 'TextBoxコントロールの配列 Private ArrTextBox() As TextBox 'Buttonコントロールの配列 Private ArrButton() As Button '''''' フォームロードイベント ''' Private Sub frmCtrlIdx_Load(sender As Object, e As EventArgs) Handles Me.Load 'コントロール配列に各コントロールを設定 ArrTextBox = {Me.TextBox1, Me.TextBox2, Me.TextBox3, Me.TextBox4, Me.TextBox5} ArrButton = {Me.Button1, Me.Button2, Me.Button3, Me.Button4, Me.Button5} 'ボタンコントロールのクリックイベント処理を定義 For Each btn As Button In ArrButton AddHandler btn.Click, AddressOf ButtonClick Next End Sub '''''' ボタンクリックイベント ''' Private Sub ButtonClick(ByVal sender As Object, ByVal e As System.EventArgs) For i As Integer = 0 To UBound(ArrButton) 'ボタンがどのボタンに該当するかチェック(指標を取得する) If sender Is ArrButton(i) Then 'テキストボックスの入力値を取得 Dim strTextBox As String = ArrTextBox(i).Text 'ボタンのCaptionを取得 Dim strTitle As String = DirectCast(sender, Button).Text '表示 MsgBox(strTextBox, MsgBoxStyle.OkOnly, strTitle) End If Next End Sub '''''' フォームClosedイベント ''' Private Sub frmCtrlIdx_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed 'コントロール配列の廃棄 Erase ArrButton Erase ArrTextBox End Sub End Class上の方法では、クリックイベントで sender がどのボタンに該当するかをコントロールを配列から探していますが、 ボタンのコントロール名の最後の数字と、テキストボックスのコントロール名の最後の数字が同じものにしていますので、 テキストボックスのコントロール名をフォーム上から探すことで同じことができます。
フォーム上に存在するコントロールを名前で探すには ControlCollection.Find メソッドを使用します。
以下にクリックイベントの部分のソースを示します。フォーム上のコントロールを名前で探す方法
'''
''' ボタンクリックイベント ''' Private Sub ButtonClick(ByVal sender As Object, ByVal e As System.EventArgs) 'ボタンのオブジェクト名から指標文字列を取得(Nameが[ButtonNN]の形式が前提) Dim strTitleIdx As String = DirectCast(sender, Button).Name.Replace("Button", "") '指標が数値?? If IsNumeric(strTitleIdx) = True Then 'テキストボックスのオブジェクト名 Dim strTextBoxName As String = "TextBox" & strTitleIdx 'フォーム上のテキストボックスを探す(結果が配列で返ってくる) Dim arrCtrl() As Control = Me.Controls.Find(strTextBoxName, False) If arrCtrl.Length > 0 Then 'テキストボックスの入力値を取得 Dim strTextBox As String = DirectCast(arrCtrl(0), TextBox).Text 'ボタンのCaptionを取得 Dim strTitle As String = DirectCast(sender, Button).Text '表示 MsgBox(strTextBox, MsgBoxStyle.OkOnly, strTitle) End If End If End Sub関連する記事
⇒コレクション「List」をコントロール配列の様に使う方法
⇒フォーム上のコントロールのイベント処理の一括関連付け:[AddHandler,DirectCast]
⇒コントロールの同じイベント処理に複数の関連付けをテスト:[AddHandler,DirectCast]
⇒配列の使い方について(Dim, Redim)
⇒配列の使い方の注意点について(コピー, Clone)
⇒配列の範囲指定によるコピー(Array.Copy, Skip, Take)