MELSEC MX Component を VB.NET で使う (2016/07/22 修正)

■VB.NET (Visual Studio 2015) で MX Componet を使ってみました。
 ・FX5UCPU の Ethernet ポート直結接続です。
 ・起動と同時に通信を開始し、オープン中は、100 ミリ秒周期でデバイスの値を取得します。
 ・通信異常時は、1 秒周期で再接続を試みます。

 ※VS 2015 の場合、メニュー - [プロジェクト] - 「既存の項目の追加」で、
  C:\MELSEC\Act\Include フォルダの ActDefine.vb を追加すると、
  UNIT_FXVETHER_DIRECT, CPU_FX5UCPU ... 等の定義が、使用できるようになります。

 ※変換関数は、MX Component の サンプル SampleTypeConv を参考にしています。


Imports System.Text     'Encodingクラスを含む名前空間

Module Module1
    '' **************************************
    '' 参考:MX Component の SampleTypeConv
    '' **************************************

    '' ASCIIコードページにエンコードするインスタンス生成
    Dim objAsciiCodePageEncoding As Encoding = Encoding.Default

    '' シーケンサ取得データを16ビット整数に
    Public Function IntArrayToShort(ByVal sharrBufferForDeviceValue() As Integer) As Short
        Dim byarrBufferByte(1 * 2 - 1) As Byte      ' 編集用バイトバッファの宣言
        Dim byarrTemp() As Byte                     ' シーケンサ用データを一時的に受ける配列
        Dim i As Integer                            ' ループ用カウンタ

        '' シーケンサ用データバッファからバイト配列に変換
        byarrTemp = BitConverter.GetBytes(sharrBufferForDeviceValue(0))
        byarrBufferByte(0) = byarrTemp(0)
        byarrBufferByte(1) = byarrTemp(1)
        Return BitConverter.ToInt16(byarrBufferByte, 0)
    End Function

    '' シーケンサ取得データを32ビット整数に
    Public Function IntArrayToInt(ByVal sharrBufferForDeviceValue() As Integer) As Integer
        Dim byarrBufferByte(2 * 2 - 1) As Byte      ' 編集用バイトバッファの宣言
        Dim byarrTemp() As Byte                     ' シーケンサ用データを一時的に受ける配列
        Dim i As Integer                            ' ループ用カウンタ

        '' シーケンサ用データバッファからバイト配列に変換
        For i = 0 To 1
            byarrTemp = BitConverter.GetBytes(sharrBufferForDeviceValue(i))
            byarrBufferByte(i * 2) = byarrTemp(0)
            byarrBufferByte(i * 2 + 1) = byarrTemp(1)
        Next i
        Return BitConverter.ToInt32(byarrBufferByte, 0)
    End Function

    '' シーケンサ取得データを32ビット実数(単精度)に
    Public Function IntArrayToSingle(ByVal sharrBufferForDeviceValue() As Integer) As Single
        Dim byarrBufferByte(2 * 2 - 1) As Byte      ' 編集用バイトバッファの宣言
        Dim byarrTemp() As Byte                     ' シーケンサ用データを一時的に受ける配列
        Dim i As Integer                            ' ループ用カウンタ

        '' シーケンサ用データバッファからバイト配列に変換
        For i = 0 To 1
            byarrTemp = BitConverter.GetBytes(sharrBufferForDeviceValue(i))
            byarrBufferByte(i * 2) = byarrTemp(0)
            byarrBufferByte(i * 2 + 1) = byarrTemp(1)
        Next i
        Return BitConverter.ToSingle(byarrBufferByte, 0)
    End Function

    '' シーケンサ取得データを64ビット実数(倍精度)に
    Public Function IntArrayToDouble(ByVal sharrBufferForDeviceValue() As Integer) As Double
        Dim byarrBufferByte(4 * 2 - 1) As Byte      ' 編集用バイトバッファの宣言
        Dim byarrTemp() As Byte                     ' シーケンサ用データを一時的に受ける配列
        Dim i As Integer                            ' ループ用カウンタ

        '' シーケンサ用データバッファからバイト配列に変換
        For i = 0 To 3
            byarrTemp = BitConverter.GetBytes(sharrBufferForDeviceValue(i))
            byarrBufferByte(i * 2) = byarrTemp(0)
            byarrBufferByte(i * 2 + 1) = byarrTemp(1)
        Next i
        Return BitConverter.ToDouble(byarrBufferByte, 0)
    End Function

    '' 16ビット整数をシーケンサ転送用データに
    Public Sub ShortToIntArray(ByVal shtValue As Short, ByRef sharrBufferForDeviceValue() As Integer)
        Dim byarrBufferByte() As Byte
        '' 入力されたデータをSingle型数値に変換し、バイト配列に代入(データ量は2バイト固定)
        byarrBufferByte = BitConverter.GetBytes(shtValue)

        '' バイト配列からシーケンサ用データバッファに代入
        sharrBufferForDeviceValue(0) = BitConverter.ToInt16(byarrBufferByte, 0)
    End Sub

    '' 32ビット整数をシーケンサ転送用データに
    Public Sub IntToIntArray(ByVal intValue As Integer, ByRef sharrBufferForDeviceValue() As Integer)
        Dim byarrBufferByte() As Byte
        Dim i As Integer

        '' 入力されたデータをSingle型数値に変換し、バイト配列に代入(データ量は4バイト固定)
        byarrBufferByte = BitConverter.GetBytes(intValue)
        For i = 0 To 1
            '' バイト配列からシーケンサ用データバッファに代入
            sharrBufferForDeviceValue(i) = BitConverter.ToInt16(byarrBufferByte, i * 2)
        Next i
    End Sub

    '' 32ビット実数(単精度)をシーケンサ転送データに
    Public Sub SingleToIntArray(ByVal sngValue As Single, ByRef sharrBufferForDeviceValue() As Integer)
        Dim byarrBufferByte() As Byte
        Dim i As Integer

        '' 入力されたデータをSingle型数値に変換し、バイト配列に代入(データ量は4バイト固定)
        byarrBufferByte = BitConverter.GetBytes(sngValue)

        '' バイト配列からシーケンサ用データバッファに代入
        For i = 0 To 1
            sharrBufferForDeviceValue(i) = BitConverter.ToInt16(byarrBufferByte, i * 2)
        Next i
    End Sub

    '' 64ビット実数(倍精度)をシーケンサ転送データに
    Public Sub DoubleToIntArray(ByVal dblValue As Double, ByRef sharrBufferForDeviceValue() As Integer)
        Dim byarrBufferByte() As Byte
        Dim i As Integer

        '' 入力されたデータをSingle型数値に変換し、バイト配列に代入(データ量は8バイト固定)
        byarrBufferByte = BitConverter.GetBytes(dblValue)

        '' バイト配列からシーケンサ用データバッファに代入
        For i = 0 To 3
            sharrBufferForDeviceValue(i) = BitConverter.ToInt16(byarrBufferByte, i * 2)
        Next i
    End Sub

    '' シーケンサ取得データを文字列に
    Function ShortArrayToString(ByVal sharrBufferForDeviceValue() As Short, ByVal txtlen As Integer) As String
        Dim byarrBufferByte(txtlen - 1) As Byte          '編集用バイトバッファの宣言
        Dim byarrTemp() As Byte                          'シーケンサ用データを一時的に受ける配列
        Dim i As Integer                                 'ループ用カウンタ

        '' シーケンサ用データバッファからバイト配列に変換
        For i = 0 To txtlen / 2 - 1
            byarrTemp = BitConverter.GetBytes(sharrBufferForDeviceValue(i))
            byarrBufferByte(i * 2) = byarrTemp(0)
            byarrBufferByte(i * 2 + 1) = byarrTemp(1)
        Next i

        '' コードページバイト配列からUnicodeに変換して、読出し用テキストボックスに格納
        Return objAsciiCodePageEncoding.GetString(byarrBufferByte)
    End Function

    '' 文字列をシーケンサ転送データに
    Sub StringToShortArray(ByVal s As String, ByVal txtlen As Integer, ByRef sharrBufferForDeviceValue() As Short)
        Dim byarrBufferByte() As Byte                             ' 編集用バイトバッファの宣言
        Dim iLengthOfBuffer As Integer                            ' シーケンサ用バッファに書き込む文字数
        Dim i As Integer                                          ' ループ用カウンタ

        '' 文字列からコードページのByte配列に変換
        byarrBufferByte = objAsciiCodePageEncoding.GetBytes(s)

        '' Byte配列からシーケンサ用データバッファに変換する文字数の設定
        iLengthOfBuffer = Math.Min(byarrBufferByte.Length, txtlen)

        '' Byte配列からシーケンサ用データバッファに変換
        ''  Step 2               :2つのByte配列データをShort配列データに代入するため
        ''  iLengthOfBuffer - 2  :文字数が奇数量の場合、最後のBitConverter実行時に領域外を参照しないため
        For i = 0 To iLengthOfBuffer - 2 Step 2
            sharrBufferForDeviceValue(i / 2) = BitConverter.ToInt16(byarrBufferByte, i)
        Next i

        '' 文字数が奇数量だった場合の最後の文字の処理
        If (iLengthOfBuffer Mod 2) = 1 Then
            sharrBufferForDeviceValue(Fix(iLengthOfBuffer / 2)) = byarrBufferByte(iLengthOfBuffer - 1)
        End If
    End Sub


End Module

Public Class Form1

    Public GB_OpenFlag As Boolean
    Public GB_ReadCnt As Integer
    Public GB_OpenCnt As Integer
    Public GB_TmBusyFlag As Boolean

    '' PLCオープン
    Public Sub PlcOpen()
        Dim iRet As Integer
        '' FX5UCPU Ehernetポート 直結
        With AxActProgType1
            .ActUnitType = UNIT_FXVETHER_DIRECT     ''&H2002    '' ユニットタイプ
            .ActCpuType = CPU_FX5UCPU               ''&H210     '' CPUタイプ
            .ActProtocolType = PROTOCOL_UDPIP       ''&H8       '' 通信プロトコルタイプ
            .ActHostAddress = "255.255.255.255"
            .ActDestinationPortNumber = &H15B8
            .ActTimeOut = 500
            iRet = .Open()
            '' TextBox1.Text = String.Format("{0:X8}", iRet)
            '' すでにオープンされている
            If iRet = &HF0000003 Then
                .Close()
                iRet = .Open()
            End If
            GB_OpenFlag = iRet = 0
        End With
    End Sub

    '' クローズ
    Public Sub PlcClose()
        With AxActProgType1
            .Close()
            GB_OpenFlag = False
        End With
    End Sub

    '' インターバルタイマー
    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Dim iRet As Integer
        Dim iData(4) As Integer

        Dim txtlen As Integer = 20
        Dim shtData(txtlen / 2) As Short

        If Not GB_TmBusyFlag Then
            GB_TmBusyFlag = True
            If Not GB_OpenFlag Then
                GB_OpenCnt = GB_OpenCnt + 1
                TextBox1.Text = GB_OpenCnt
                PlcOpen()
                If GB_OpenFlag Then
                    '' 100 ミリ秒でデバイス読込
                    Timer1.Interval = 100
                End If
            End If
            If GB_OpenFlag Then
                GB_ReadCnt = GB_ReadCnt + 1
                TextBox2.Text = GB_ReadCnt
                With AxActProgType1

                    iRet = .ReadDeviceBlock("D0", 4, iData(0))
                    If iRet <> 0 Then
                        GB_OpenFlag = False
                        '' 2 秒周期で再接続
                        Timer1.Interval = 2000
                    Else
                        '' D0 を 16ビット整数
                        TextBox11.Text = CStr(IntArrayToShort(iData))

                        '' D1, D0 を 32ビット整数
                        TextBox12.Text = CStr(IntArrayToInt(iData))

                        '' D1, D0 を 32ビット実数(単精度)
                        TextBox13.Text = CStr(IntArrayToSingle(iData))

                        '' D3 ~ D0 を 64ビット実数(倍精度)
                        TextBox14.Text = CStr(IntArrayToDouble(iData))

                        '' 文字列(最大20文字)
                        .ReadDeviceBlock2("D0", txtlen / 2, shtData(0))
                        TextBox16.Text = ShortArrayToString(shtData, txtlen)
                    End If
                End With
            End If
            GB_TmBusyFlag = False
        End If
    End Sub

    Private Sub Form1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
        Timer1.Interval = 100
        Timer1.Enabled = True
    End Sub

    '' 16 ビット整数
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim iData(1) As Integer
        Try
            If GB_OpenFlag Then
                'iData(0) = CShort(TextBox10.Text)
                'AxActProgType1.WriteDeviceBlock("D0", 1, iData(0))

                ShortToIntArray(CShort(TextBox10.Text), iData)
                AxActProgType1.WriteDeviceBlock("D0", 1, iData(0))
            End If
        Catch ex As Exception
            ''
        End Try
    End Sub

    '' 32 ビット整数
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim iData(2) As Integer
        Try
            If GB_OpenFlag Then
                IntToIntArray(CInt(TextBox9.Text), iData)
                AxActProgType1.WriteDeviceBlock("D0", 2, iData(0))
            End If
        Catch ex As Exception
            ''
        End Try
    End Sub

    '' 32ビット実数(単精度)
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Dim iData(2) As Integer
        Try
            If GB_OpenFlag Then
                SingleToIntArray(CSng(TextBox8.Text), iData)
                AxActProgType1.WriteDeviceBlock("D0", 2, iData(0))
            End If
        Catch ex As Exception
            '' 
        End Try
    End Sub


    '' 64 ビット実数(倍精度)
    Private Sub Button4_Click_1(sender As Object, e As EventArgs) Handles Button4.Click
        Dim iData(4) As Integer
        Try
            If GB_OpenFlag Then
                DoubleToIntArray(CDbl(TextBox7.Text), iData)
                AxActProgType1.WriteDeviceBlock("D0", 4, iData(0))
            End If
        Catch ex As Exception
            ''
        End Try
    End Sub

    '' 文字列
    Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
        Dim txtlen As Integer = 20	'最大文字列数
        Dim shtData(txtlen / 2 - 1) As Short    'シーケンサ用バッファの宣言

        StringToShortArray(TextBox15.Text, txtlen, shtData)
        If GB_OpenFlag Then
            AxActProgType1.WriteDeviceBlock2("D0", txtlen / 2, shtDatae(0))
        End If
    End Sub

End Class