Notes on the documentation

BECKHOFF Automation: Foreword

文档编制说明

本说明书仅供那些在控制与自动化工程领域中熟悉国家标准并受过培训专业人士使用。

在安装和调试这些组件时，操作人员须遵循下列说明和解释。

相关负责人员必须确保所述产品的应用或使用满足所有安全要求，包括相关法律、法规、准则和标准。

免责声明

此文档需注意的是：尽管本文档经过精心编制，但所述的产品正处在不断地更新换代中。

因此，我们并不能保证文档中所描述的产品性能数据、标准或其它特性与实际产品相一致。

倘若，本文档出现一些技术或编辑上的错误，我们时刻保留对文档信息作出修改的权力，且恕不另行通知。

对于所提供产品，如果在本文档中已对数据、图表以及说明进行了修改，我们将不再特别加以声明。

商标

Beckhoff®, TwinCAT®, EtherCAT®商标以及EtherCAT®, TwinSAFE® 和XFC® 的安全商标已为Beckhoff 自动化有限公司的注册商标并已由Beckhoff 自动化有限公司授权。

未经许可，任何在公共场合以盈利为目的使用本公司的商标，本公司将依法追究。

申请中的专利

TwinCAT技术已被注册，包含但不局限于如下专利申请和专利：

在许多其他国家有关EP0851348, US6167425的相关申请和注册。

版权

© Beckhoff 自动化有限公司

未经许可，任何人不得复制、分发和使用本手册以及内容传递。
 违者将追究损害赔偿，倘若批准一个专利、使用模式或设计，本公司保留所有权利。


Übersicht

TwinCAT ADS Samples - Visual Basic

概述

集成于 Visual Basic

Description
Linking into Visual Basic
Installation of ADS-OCX
Length of VB-variables

TwinCAT ADS-OCX

Description | Source code
Sample 1: Accessing an array in the PLC | ADS-OCX Sample01.exe
Sample 2: Transmitting structures to the PLC | ADS-OCX Sample02.exe
Sample 3: Event driven reading | ADS-OCX Sample03.exe
Sample 4: Read PLC variable declaration | ADS-OCX Sample04.exe
Sample 5: Detect/alter status in the TwinCAT Router and the PLC | ADS-OCX Sample05.exe
Sample 6: Send/receive messages via the TwinCAT Router | ADS-OCX Sample06.exe
Sample 7: Delete a handle of a PLC variable | ADS-OCX Sample07.zip
Sample 9: Event driven reading (with converting notification data) | ADS-OCX Sample09.exe

TwinCAT ADS-DLL for Windows NT/2000/XP/CE

Description | Source code
Sample 8: Reading and Writing of PLC Variables | TcAdsDll_ComSample01.exe
Sample 9: Event driven reading | TcAdsDll_ComSample02.exe

TwinCAT ADS-Script-DLL

Description | Source code
Sample 10: Reading and Writing of PLC Variables | ADS-Script-DLL Sample02.exe


ADS Return Codes

TwinCAT Diagnostics: ADS Return Codes

ADS 返回代码

错误代码: 0x000..., 0x500..., 0x700..., 0x1000..., 0x274C...

全局错误代码

Hex | Dec | Description
0x0 | 0 | 无错误
0x1 | 1 | 内部错误
0x2 | 2 | 无 Rtime
0x3 | 3 | 分配锁定内存错误
0x4 | 4 | 插入邮箱错误
0x5 | 5 | HMSG接收错误
0x6 | 6 | 目标端口没有找到
0x7 | 7 | 目标机器没有找到
0x8 | 8 | 未知命令 ID
0x9 | 9 | 不良task ID
0xA | 10 | 无 IO
0xB | 11 | 未知 AMS 命令
0xC | 12 | Win 32 错误
0xD | 13 | 端口没有连接
0xE | 14 | 无效 AMS长度
0xF | 15 | 无效 AMS Net ID
0x10 | 16 | 低安装级别
0x11 | 17 | 无可用调试
0x12 | 18 | 端口不可用
0x13 | 19 | 端口已经使用
0x14 | 20 | AMS Sync Win32 错误
0x15 | 21 | AMS Sync 超时
0x16 | 22 | AMS Sync AMS 错误
0x17 | 23 | AMS Sync 无索引图
0x18 | 24 | 无效 AMS 端口
0x19 | 25 | 无内存
0x1A | 26 | TCP 发送错误
0x1B | 27 | 主机不可达

路由器错误代码

Hex | Dec | Error type | Description
0x500 | 1280 | internal | ROUTERERR_NOLOCKEDMEMORY 无锁定内存可分配.
0x501 | 1281 | internal | ROUTERERR_RESIZEMEMORY 路由器内存大小不可变.
0x502 | 1282 | internal | ROUTERERR_MAILBOXFULL AmsPort 邮箱已达到可接入上限. 本次信息发送被取消.
0x503 | 1283 | internal | ROUTERERR_DEBUGBOXFULL Ams调试器端口邮箱已达到可接入上限. 调试监视器不显示发送的信息.
0x504 | 1284 | internal | ROUTERERR_UNKNOWNPORTTYPE
0x505 | 1285 | internal | ROUTERERR_NOTINITIALIZED 路由器没有初始化.
0x506 | 1286 | internal | ROUTERERR_PORTALREADYINUSE 目标端口号已被占用.
0x507 | 1287 | internal | ROUTERERR_NOTREGISTERED Ams 端口号没有注册.
0x508 | 1288 | internal | ROUTERERR_NOMOREQUEUES Ams 端口数量已达上限.
0x509 | 1289 | internal | ROUTERERR_INVALIDPORT
0x50A | 1290 | internal | ROUTERERR_NOTACTIVATED TwinCAT 路由器没有激活.

一般 ADS 错误代码

Hex | Dec | Description
0x700 | 1792 | 错误类<设备错误>
0x701 | 1793 | 服务器不支持该服务
0x702 | 1794 | 无效组索引
0x703 | 1795 | 无效偏移量索引
0x704 | 1796 | 无读写权限
0x705 | 1797 | 参数大小不正确
0x706 | 1798 | 无效参数值
0x707 | 1799 | 设备未准备就绪
0x708 | 1800 | 设备忙
0x709 | 1801 | 无效内容 (Windows)
0x70A | 1802 | 内存溢出
0x70B | 1803 | 无效参数值
0x70C | 1804 | 没有找到 (文件, ...)
0x70D | 1805 | 命令或文件语法错误
0x70E | 1806 | 对象不匹配
0x70F | 1807 | 对象已存在
0x710 | 1808 | 变量未找到
0x711 | 1809 | 变量版本无效
0x712 | 1810 | 服务器处于无效状态
0x713 | 1811 | Ads传输模式不支持
0x714 | 1812 | 通知句柄无效
0x715 | 1813 | 通知客户端没有注册
0x716 | 1814 | 无更多通知句柄
0x717 | 1815 | 监视大小过大
0x718 | 1816 | 设备未初始化
0x719 | 1817 | 设备超时
0x71A | 1818 | 查询接口失败
0x71B | 1819 | 请求错误接口
0x71C | 1820 | 类 ID 无效
0x71D | 1821 | 对象 ID 无效
0x71E | 1822 | 请求未处理
0x71F | 1823 | 请求终止
0x720 | 1824 | 警告信号
0x721 | 1825 | 无效数组索引
0x722 | 1826 | 变量未激活 -> 释放句柄,重试
0x723 | 1827 | 拒绝访问
0x740 | 1856 | 错误类 <客户端错误>
0x741 | 1857 | 无效服务参数
0x742 | 1858 | 轮询列表为空
0x743 | 1859 | 变量连接已用
0x744 | 1860 | 调用 ID 已用
0x745 | 1861 | 超时
0x746 | 1862 | win32子系统错误
0x748 | 1864 | ads端口未打开
0x750 | 1872 | ads sync 内部错误
0x751 | 1873 | hash 表溢出
0x752 | 1874 | 键在hash中未找到
0x753 | 1875 | 缓存中无更多变量
0x754 | 1876 | 收到无效响应
0x755 | 1877 | sync 端口锁定

RTime 错误代码

Hex | Dec | Error type | Description
0x1000 | 4096 | internal | RTERR_INTERNAL TwinCAT 实时核 system致命内部错误.
0x1001 | 4097 | internal | RTERR_BADTIMERPERIODS 定时器值无效
0x1002 | 4098 | internal | RTERR_INVALIDTASKPTR 任务指针为无效值ZERO
0x1003 | 4099 | internal | RTERR_INVALIDSTACKPTR 任务堆栈指针为无效值ZERO.
0x1004 | 4100 | internal | RTERR_PRIOEXISTS 目标任务优先级已被占用.
0x1005 | 4101 | internal | RTERR_NOMORETCB TCB (Task Control Block) 已达上限64.
0x1006 | 4102 | internal | RTERR_NOMORESEMAS 信号灯已达上限64.
0x1007 | 4103 | internal | RTERR_NOMOREQUEUES 队列已达上限64.
0x1008 | 4104 | internal | TwinCAT 保留使用.
0x1009 | 4105 | internal | TwinCAT 保留使用.
0x100A | 4106 | internal | TwinCAT 保留使用
0x100B | 4107 | internal | TwinCAT 保留使用.
0x100C | 4108 | internal | TwinCAT 保留使用.
0x100D | 4109 | internal | RTERR_EXTIRQALREADYDEF An external synchronisation interrupt is already applied.
0x100E | 4110 | internal | RTERR_EXTIRQNOTDEF No external synchronsiation interrupt applied.
0x100F | 4111 | internal | RTERR_EXTIRQINSTALLFAILED The apply of the external synchronisation interrupt failed.
0x1010 | 4112 | internal | RTERR_IRQLNOTLESSOREQUAL Call of a service function in the wrong context.

Winsock 错误代码

Hex | Dec | Description
0x274c | 10060 | socket 操作目标主机不可达
0x274d | 10061 | 因响应超时而连接失败, 或因目标主机没有响应而建立连接失败.
0x2751 | 10065 | 目标机器拒绝连接
 |  | 更多 Winsock 错误代码: Win32 错误代码


ADS Return Codes

TwinCAT ADS-VB-Variablelength

VB变量长度

VB变量类型 | 变量长度( byte)
Boolean | 2
Integer | 2
Long | 4
Single | 4
Double | 8
String | 字符数 * 2
Byte | 1

数组长度

数组长度（字节）= 数组中元素个数 * 变量类型长度.

示例

对于读写长整型数组来说,长度为20 byte(元素数5 * 4 byte)

包含25个字符的字符串长度为50 byte(字符数25 * 2 byte).


Installing the ADS OCX

ADS-OCX: Setup

安装 ADS OCX

通过 Regsvr32 来安装 ADS OCX.必须说明要注册的文件路径.

打开运行核(Start -->Run...); 键入 Regsvr32 <path to adsocx.ocx>\adsocx.ocx; Enter

不需要重启电脑.


Accessing an array in the PLC

TwinCAT ADS-OCX: Examples

访问PLC中的数组

任务

在Visual Basic 通过read命令读取PLC中的数组.

描述

PLC中有一个包含100个integer 型 (2 bytes)元素的数组.该数组值为从3500 到 3599.
首先,在 Visual Basic程序 load 事件函数中获取PLC变量的句柄.程序结束时,应在 unload 事件函数中释放它.

用户单击窗体上的按钮时,通过 AdsSyncRead[Datatype]VarReq() 方法将PLC中的整个数组读取到 Visual Basic 变量 Data.

Data 变量必须与PLC中相应变量具有相同的结构;即100个 integer 型 (2 bytes)元素.其所声明的长度为200,因为其所请求的数据长度为200 bytes (100 个2 bytes 元素).

在接下来的一个FOR循环中,将该PLC数组数据填充至一个listbox控件.

Visual Basic 源程序

Dim hVar As Long
Dim Data(100) As Integer

'--- Is called at the start ---
Private Sub Form_Load()
  '--- Exception freigeben --- AdsOcx1.EnableErrorHandling = True
  Call AdsOcx1.AdsCreateVarHandle("Main.PLCVar", hVar)
End Sub

'--- Is called at the end ---
Private Sub Form_Unload(Cancel As Integer)
  Call AdsOcx1.AdsDeleteVarHandle(hVar)
End Sub

'--- Is called by the user ---
Private Sub cmd_read_Click()
  Dim intIndex As Integer
  '--- Read complete array ---
  Call AdsOcx1.AdsSyncReadIntegerVarReq(hVar, 200, Data(0))
  '--- Display array elements in form ---
  lstArray.Clear
  For intIndex = 0 To 99
    lstArray.AddItem (CStr(intIndex) & Chr(vbKeyTab) & _ CStr(Data(intIndex)))
  Next
End Sub

PLC 源程序

PROGRAM MAIN
VAR
  PLCVar : ARRAY [0..99] OF INT;
  Index: BYTE;
END_VAR

FOR Index := 0 TO 99 DO
  PLCVar[Index] := 3500 + INDEX;
END_FOR

本例源程序 'Access an array in the PLC'


Transmitting structures to the PLC

TwinCAT ADS-OCX: Examples

传输 structure 类型数据到 PLC 中

任务

Visual Basic 将 structure 类型数据写进 PLC.structure 中的元素具有不同的数据类型.

描述

为了在Windows NT/2000环境下使CPU更快捷地访问变量,Visual Basic (或其他编程语言)在主存中依据某种规律排列它们.这意味着在一个structure中存在内存泄露.因为Visual Basic 和 IEC1131-3 具有不同的排列规则,必须使用中间变量来匹配它们.

不幸的是,在Visual Basic中不能定义一个通用的排列规则.然而有两个Visual Basic函数: VarPtr() 和 LenB()可以解析 structure的内存排列.

VarPtr() 返回一个变量的地址,而 LenB() 则返回一个变量(或整个数组)所占用的长度(bytes).下例描述了在一个窗体上显示一个结构的内存映射.其信息可用于判断 structure 的"内存碎片"位置.在本例中,其将被VarDummyX 变量填充.

提示: VarPtr() 函数只支持 Visual Basic 5或更高.

下面的草图为内存分配图:

一个矩形代表一个被该变量在此处占用的字节.一个叉号表示一个未被任何变量使用且已分配的字节.本例中,叉号被虚拟变量填充.

Visual Basic程序中的 Structure

Type VBStruct
     VarInteger   As Integer
    VarDummy1    As Integer
    VarLong      As Long
    VarByte      As Byte
    VarDummy2    As Byte
    VarDummy3    As Byte
    VarDummy4    As Byte
    VarDouble    As Double
    VarSingle    As Single
End Type

PLC 程序中的 Structure

Visual Basic 程序中的structure排列调整后,PLC程序中的structure必需做相应的调整.

TYPE PLCStruct
STRUCT
    PLCVarInteger : INT;
    PLCVarDummy1  : INT;
    PLCVarLong    : DINT;
    PLCVarByte    : SINT;
    PLCVarDummy2  : SINT;
    PLCVarDummy3  : SINT;
    PLCVarDummy4  : SINT;
    PLCVarDouble  : LREAL;
    PLCVarSingle  : REAL;
END_STRUCT
END_TYPE

Visual Basic 程序

Dim hVar As Long
Dim VBVar As VBStruct

'--- Is called at the start ---
Private Sub Form_Load()
     '--- Enable exception --- AdsOcx1.EnableErrorHandling = True
    Call AdsOcx1.AdsCreateVarHandle("Main.PLCVar", hVar)
    '--- Display addresses of the variables ---
    lblInteger.Caption = VarPtr(VBVar.VarInteger)
    lblLong.Caption = VarPtr(VBVar.VarLong)
    lblByte.Caption = VarPtr(VBVar.VarByte)
    lblDouble.Caption = VarPtr(VBVar.VarDouble)
    lblSingle.Caption = VarPtr(VBVar.VarSingle)
    '--- Display length of the structure ---
    lblVarLength.Caption = LenB(VBVar)
End Sub

'--- Is called at the end ---
Private Sub Form_Unload(Cancel As Integer)
    Call AdsOcx1.AdsDeleteVarHandle(hVar)
End Sub

'--- Is called by the user ---
Private Sub cmd_write_Click()
    Dim intIndex As Integer
    '--- Fill structure ---
    VBVar.VarInteger = CInt(txtInteger.Text)
    VBVar.VarLong = CLng(txtLong.Text)
    VBVar.VarByte = CByte(txtByte.Text)
    VBVar.VarDouble = CDbl(txtDouble.Text)
    VBVar.VarSingle = CSng(txtSingle.Text)
    '--- Write structure to PLC ---
    Call AdsOcx1.AdsSyncWriteIntegerVarReq(hVar, LenB(VBVar), VBVar.VarInteger)
End Sub

PLC 程序

PROGRAM MAIN
VAR
  PLCVar : PLCStruct;
END_VAR

本例源程序 'Transmitting structures to the PLC'.

优化

在VB应用程序中合理地安排VBStruct 的成员变量可以避免增加更多的虚拟bytes.请遵循以下规则:

  • 根据所占内存大小来安排VB structure 的成员变量:首先排列最大的数据类型.
  • 最后的位置可以填满(但不必须)4 bytes.

在 Visual Basic 中优化structure的声明

Type VBStruct
    VarDouble As Double 	' 8 bytes
    VarSingle As Single	'+4 bytes
    VarLong As Long		'+4 byte
    VarInteger As Integer	'+2 bytes
    VarByte As Byte		'+1 byte
                '+1 hidden padding byte in memory
                '=20 bytes (LenB result)
End Type

在PLC中优化 structure 的声明

TYPE PLCStruct
STRUCT
    PLCVarDouble : LREAL;
    PLCVarSingle : REAL;
    PLCVarLong : DINT;
    PLCVarInteger : INT;
    PLCVarByte : SINT;
END_STRUCT
END_TYPE

优化后的 VB structure 以 Double 开始,可根据VB 程序做适当的调整:

'--- Is called by the user ---
Private Sub cmd_write_Click()
...
    '--- Write structure to PLC ---
    call AdsOcx1.AdsSyncWriteDoubleVarReq(hVar, Len(VBVar), VBVar.VarDouble)
End Sub

采用Len函数获得可变数据长度,而不用LenB函数.如果使用 LenB , 并未将数据写进PLC,因为 LenB 返回的长度为 20 bytes (包括了VB内存中的填充 bytes), 而PLC中的structure 长度为19 bytes.

Len vs. LenB

  • 用户定义类型时, Len 返回要写到文件中的数据大小.
  • 用户定义类型时, LenB 返回数据所占内存的大小,其包括元素间的任何填充.


Event driven reading

TwinCAT ADS-OCX: Examples

事件驱动读取

任务

PLC中声明了7个全局变量.其中每个变量均具有不同的数据类型.应以最有效的方式读取变量值,而且在Visual Basic窗体中显示其值和相应时间片.

描述

在窗体的 load 事件中调用AdsReadVarConnectEx() 方法创建到每一个PLC变量的连接.该连接的句柄存储在一个全局数组中.

传入AdsReadVarConnectEx()方法的第二个参数代表着数据交换的类型.此处选择ADSTRANS_SERVERONCHA.这意味着只有在PLC中变量值发生改变才会产生数据交换(请参考 ADSOCXTRANSMODE 数据类型).第三个参数代表着PLC每隔100 ms检查相应变量值是否发生改变.

当PLC变量值发生改变,调用AdsReadConnectUpdateEx()事件.时间片、句柄、值和要显示其值的控件的引用均以参数形式传入.

在unload事件中通过AdsDisconnectEx() 方法释放连接.这一步至关重要,因为通过AdsReadVarConnectEx() 建立的每一个连接均占用资源.

应当选择合适的循环时间,因为过于频繁的读写操作会使系统负担加大以至于用户界面变得很慢.

Visual Basic 程序

Option Explicit

Dim hConnect(0 to 6) As Long

Private Sub Form_Load()
    Dim nErr As Long

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarBoolean", ADSTRANS_SERVERONCHA, 100, hConnect(0), lblBoolean)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarBoolean: " & nErr)

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarInteger", ADSTRANS_SERVERONCHA, 100, hConnect(1), lblInteger)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarInteger: " & nErr)

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarLong", ADSTRANS_SERVERONCHA, 100, hConnect(2), lblLong)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarLong: " & nErr)

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarDouble", ADSTRANS_SERVERONCHA, 100, hConnect(3), lblDouble)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarDouble: " & nErr)

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarSingle", ADSTRANS_SERVERONCHA, 100, hConnect(4), lblSingle)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarSingle: " & nErr)

    nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarString", ADSTRANS_SERVERONCHA, 100, hConnect(5), lblString)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarString: " & nErr)

   nErr = AdsOcx1.AdsReadVarConnectEx(".PLCVarByte", ADSTRANS_SERVERONCHA, 100, hConnect(6), lblByte)
    If (nErr > 0) Then Call MsgBox("Error AdsReadVarConnectEx -> .PLCVarByte: " & nErr)
End Sub

Private Sub AdsOcx1_AdsReadConnectUpdateEx(ByVal dateTime As Date,
                                           ByVal nMs As Long,
                                           ByVal hConnect As Long,
                                           ByVal data As Variant,
                                           Optional ByVal hUser As Variant)
    hUser.Caption = ("Date Time: " & dateTime & "," & nMs & "  Data: " & data)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Dim nIndex As Long
    For nIndex = 0 To 6
        Call AdsOcx1.AdsDisconnectEx(hConnect(nIndex))
    Next
End Sub

PLC 程序

VAR_GLOBAL
    PLCVarBoolean	: BOOL;
    PLCVarInteger	: INT;
    PLCVarLong	: DINT;
    PLCVarDouble	: LREAL;
    PLCVarSingle	: REAL;
    PLCVarString	: STRING(10);
    PLCVarByte	: BYTE;
 END_VAR

PROGRAM MAIN
VAR
    ;
END_VAR

本例源程序'Event driven reading' .


Read PLC variable declaration

TwinCAT ADS-OCX: Examples

读取PLC中声明的变量

任务

所有的变量信息都从PLC中读取 (上载变量).

描述

点击窗体上的按钮时调用cmdReadSymbols_Click() 事件函数.AdsReadSymbolInfo() 方法提供变量(名称)的数量和其数据长度.首次调用AdsEnumSymbols() 方法是参数bNext 必须被置为FALSE.这就使首个变量的信息可读.对于每次调用的子序列,bNext 设置为 TRUE.FOR循环中,PLC中有多少个变量就调用多少次AdsEnumSymbols() .

Visual Basic 程序

Option Explicit

'--- Is called at the start of the program ---
Private Sub Form_Load()
    '--- Enable exception ---
    AdsOcx1.EnableErrorHandling = True
    '--- Clear display list and boxes ---
    lstSymbols.Clear
    lblSymbols.Caption = "Symbols: "
End Sub

'--- Is called by the user ---
Private Sub cmdReadSymbols_Click()
    Dim nSymbolsAvailable As Long
    Dim cbBufSizeNeeded As Long
    Dim strSymbolName As String
    Dim strComment As String
    Dim nSymbolType As Long
    Dim cbSymbolSize As Long
    Dim nIndexOffset As Long
    Dim nIndexGroup As Long
    Dim intIndex As Long

    '--- Clear display list ---
    lstSymbols.Clear

    Call AdsOcx1.AdsReadSymbolInfo(nSymbolsAvailable, cbBufSizeNeeded)
    lblSymbols.Caption = "Symbols: " & nSymbolsAvailable

    '--- Load and display the first symbol ---
    Call AdsOcx1.AdsEnumSymbols(strSymbolName, nSymbolType, cbSymbolSize, _
                                strComment, nIndexGroup, nIndexOffset, False)
    lstSymbols.AddItem ("0" & vbTab & Format(strSymbolName, "!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") & vbTab & _
                        GetSymbolType(nSymbolType) & vbTab & cbSymbolSize & vbTab & strComment & vbTab & _
                        "0x" & Hex(nIndexGroup) & vbTab & "0x" & Hex(nIndexOffset))

    '--- Load and display the remaining symbols ---
    For intIndex = 1 To nSymbolsAvailable - 1
        Call AdsOcx1.AdsEnumSymbols(strSymbolName, nSymbolType, cbSymbolSize, _
                                    strComment, nIndexGroup, nIndexOffset, True)
        lstSymbols.AddItem (intIndex & vbTab & Format(strSymbolName, "!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") & vbTab & _
                            GetSymbolType(nSymbolType) & vbTab & cbSymbolSize & vbTab & strComment & vbTab & _
                            "0x" & Hex(nIndexGroup) & vbTab & "0x" & Hex(nIndexOffset))
    Next intIndex
End Sub

Private Function GetSymbolType(VarType As ADSDATATYPEID) As String
    Select Case VarType
        Case ADST_BIT:     GetSymbolType = "BIT"
        Case ADST_INT8:    GetSymbolType = "INT8"
        Case ADST_INT16:   GetSymbolType = "INT16"
        Case ADST_INT32:   GetSymbolType = "INT32"
        Case ADST_INT64:   GetSymbolType = "INT64"
        Case ADST_UINT8:   GetSymbolType = "UINT8"
        Case ADST_UINT16:  GetSymbolType = "UINT16"
        Case ADST_UINT32:  GetSymbolType = "UINT32"
        Case ADST_UINT64:  GetSymbolType = "UINT64"
        Case ADST_REAL32:  GetSymbolType = "REAL32"
        Case ADST_REAL64:  GetSymbolType = "REAL64"
        Case ADST_REAL80:  GetSymbolType = "REAL80"
        Case ADST_BIGTYPE: GetSymbolType = "BIGTYPE"
        Case ADST_VOID:    GetSymbolType = "VOID"
    End Select
End Function

PLC 程序

PROGRAM MAIN
VAR
    REAL32_1 AT %MB0 : REAL;  (* 1 *)
    REAL32_2 AT %MB4 : REAL;  (* 2 *)
    REAL32_3 AT %MB8 : REAL;  (* 3 *)
    REAL32_4 AT %MB12: REAL;  (* 4 *)
    REAL32_5 AT %MB16: REAL;  (* 5 *)

    REAL64_1 AT %MB20 : LREAL;  (* 6 *)
    REAL64_2 AT %MB28 : LREAL;  (* 7 *)
    REAL64_3 AT %MB36 : LREAL;  (* 8 *)
    REAL64_4 AT %MB44 : LREAL;  (* 9 *)
    REAL64_5 AT %MB52 : LREAL;  (* 10 *)

    INT32_1 AT %MB60 : DINT;  (* 11 *)
    INT32_2 AT %MB64 : DINT;  (* 12 *)
    INT32_3 AT %MB68 : DINT;  (* 13 *)
    INT32_4 AT %MB72 : DINT;  (* 14 *)
    INT32_5 AT %MB76 : DINT;  (* 15 *)

    UINT32_1 AT %MB80 : UDINT;  (* 16 *)
    UINT32_2 AT %MB84 : UDINT;  (* 17 *)
    UINT32_3 AT %MB88 : UDINT;  (* 18 *)
    UINT32_4 AT %MB92 : UDINT;  (* 19 *)
    UINT32_5 AT %MB96 : UDINT;  (* 20 *)

    INT16_1 AT %MB100 : INT;  (* 21 *)
    INT16_2 AT %MB102 : INT;  (* 22 *)
    INT16_3 AT %MB104 : INT;  (* 23 *)
    INT16_4 AT %MB106 : INT;  (* 24 *)
    INT16_5 AT %MB108 : INT;  (* 25 *)

    UINT16_1 AT %MB110 : UINT;  (* 26 *)
    UINT16_2 AT %MB112 : UINT;  (* 27 *)
    UINT16_3 AT %MB114 : UINT;  (* 28 *)
    UINT16_4 AT %MB116 : UINT;  (* 29 *)
    UINT16_5 AT %MB118 : UINT;  (* 30 *)

    INT8_1 AT %MB120 : SINT;  (* 31 *)
    INT8_2 AT %MB121 : SINT;  (* 32 *)
    INT8_3 AT %MB122 : SINT;  (* 33 *)
    INT8_4 AT %MB123 : SINT;  (* 34 *)
    INT8_5 AT %MB124 : SINT;  (* 35 *)

    UINT8_1 AT %MB125 : USINT;  (* 36 *)
    UINT8_2 AT %MB126 : USINT;  (* 37 *)
    UINT8_3 AT %MB128 : USINT;  (* 38 *)
    UINT8_4 AT %MB129 : USINT;  (* 39 *)
    UINT8_5 AT %MB130 : USINT;  (* 40 *)

    BOOL_1 AT %MX131.0 : BOOL;  (* 41 *)
    BOOL_2 AT %MX131.1 : BOOL;  (* 42 *)
    BOOL_3 AT %MX131.2 : BOOL;  (* 43 *)
    BOOL_4 AT %MX131.3 : BOOL;  (* 44 *)
    BOOL_5 AT %MX131.4 : BOOL;  (* 45 *)

    ARRAY_1 : ARRAY[1 .. 10] OF SINT; (* 46 *)
    ARRAY_2 : ARRAY[1 .. 10] OF INT;  (* 47 *)
    ARRAY_3 : ARRAY[1 .. 10] OF DINT; (* 48 *)
    ARRAY_4 : ARRAY[1 .. 10] OF LREAL;(* 49 *)
    ARRAY_5 : ARRAY[1 .. 10] OF BOOL; (* 50 *)
END_VAR

本例源程序 'Read PLC Variable Declarations' .


Detect/alter status of the router and the PLC

TwinCAT ADS-OCX: Examples

检测/改变TwinCAT路由器和PLC的状态

任务

ADS-OCX提供了跟踪TwinCAT路由器和ADS设备状态变化的方法.例如: AdsRouterRemove(), AdsRouterShutdown(), AdsRouterStart(), AdsServerStateChanged() 和 AdsServerSymChanged() .

描述

如果TwinCAT 路由器停止,触发AdsRouterShutdown() 事件,受影响的程序便会作出相应的反应.当TwinCAT 路由器启动,触发AdsRouterStart() 事件,这时便可用AdsReadVarConnectEx()方法重新建立到ADS变量的连接.如果在Windows NT/2000控制面板中将TwinCAT 路由器从操作系统中完全卸载,便会触发 AdsRouterRemove()事件.

ADS设备和TwinCAT路由器的状态改变可以被跟踪.这对PLC尤为重要.不管PLC处于启动或停止,均可触发AdsServerStateChanged() 事件.AdsServerStateChanged() 事件会报告变量表的改变.例如,PLC程序重新汇编并传入PLC时会触发上述事件.

不但可以检测ADS设备的状态,还可以改变之.AdsSyncWriteControlReq() 方法使这成为可能.PLC 可以控制ADS 状态STOP 和 RUN. 用户可使用下例中的检查框按钮在这两种状态间转换.

TwinCAT路由器状态的每次改变都会在列表框内有相应的显示.

提示:如果检测到变量表中的一个变化,可能是 AdsReadVarConnectEx()所连接的变量被删除或重命名.当AdsServerSymChanged() 事件发生时, 所有连接和句柄都被删除并重建.

Visual Basic 程序

Option Explicit

'--- Is called at the start ---
Private Sub Form_Load()
    Call lstEvent.Clear
    AdsOcx1.EnableErrorHandling = True
End Sub

'--- Is called when the status of the ADS device changes ---
Private Sub AdsOcx1_AdsServerStateChanged(ByVal nAdsState As ADSOCXLib.ADSSTATE, ByVal nDeviceState As Long)
    Select Case nAdsState
        Case ADSSTATE_INVALID:      lstEvent.AddItem ("PLC invalid")
        Case ADSSTATE_IDLE:         lstEvent.AddItem ("PLC idle")
        Case ADSSTATE_RESET:        lstEvent.AddItem ("PLC reset")
        Case ADSSTATE_INIT:         lstEvent.AddItem ("PLC init")
        Case ADSSTATE_START:        lstEvent.AddItem ("PLC start")
        Case ADSSTATE_RUN:          lstEvent.AddItem ("PLC run")
                                    chkRunStop.Value = 1
        Case ADSSTATE_STOP:         lstEvent.AddItem ("PLC stop")
                                    chkRunStop.Value = 0
        Case ADSSTATE_SAVECFG:      lstEvent.AddItem ("PLC savecfg")
        Case ADSSTATE_LOADCFG:      lstEvent.AddItem ("PLC loadcfg")
        Case ADSSTATE_POWERFAILURE: lstEvent.AddItem ("PLC powerfailure")
        Case ADSSTATE_POWERGOOD:    lstEvent.AddItem ("PLC powergood")
        Case ADSSTATE_ERROR:        lstEvent.AddItem ("PLC error")
    End Select
End Sub

'--- Is called when the symbol table changes ---
Private Sub AdsOcx1_AdsServerSymChanged()
    lstEvent.AddItem ("PLC symbol changed")
End Sub

'--- Is called when the TwinCAT Router is removed ---
Private Sub AdsOcx1_AdsRouterRemove()
    lstEvent.AddItem ("TwinCAT-Router remove")
End Sub

'--- Is called when the TwinCAT Router is stopped ---
Private Sub AdsOcx1_AdsRouterShutdown()
    lstEvent.AddItem ("TwinCAT-Router shutdown")
End Sub

'--- Is called when the TwinCAT Router is started ---
Private Sub AdsOcx1_AdsRouterStart()
    lstEvent.AddItem ("TwinCAT-Router start")
End Sub

'--- Is called by the user ---
Private Sub chkRunStop_Click()
    Dim nState As ADSOCXLib.ADSSTATE
    Dim nRet As Integer
    nState = IIf(chkRunStop.Value = 0, ADSSTATE_STOP, ADSSTATE_RUN)
    Call AdsOcx1.AdsSyncWriteControlReq(nState, 0&, 0&, nRet)
End Sub

本例源程序 'Detect/alter status in the TwinCAT Router'.


Send/receive messages via the router

TwinCAT ADS-OCX: Examples

通过 TwinCAT 路由器发送/接收信息

任务

ADS设备可以通过TwinCAT路由器发送信息到其他ADS设备,也可接收并处理信息.这等于可能写信息到Windows NT/2000事件监视器中.

下面的 Visual Basic 程序从PLC接收信息并在屏幕上显示.同样可能从Visual Basic 程序写信息到Windows NT/2000事件监视器中.

描述

为了能够接受信息,必须采用AdsEnableLogNotification() 方法定义一个滤波器.这里给出了要接受信息的ADS设备端口号范围.附加的第二个参数描述了信息类型 (错误, 信息或警告).采用OR操作符可合成不止一个信息类型.

当ADS设备发送一个信息且满足滤波器条件时,触发 AdsLogNotification() 事件. source, type, time 和 message 本身均可从参数获悉.如果要使用ADS-OCX发送信息,使用 AdsLogFmtString() 方法.第一个参数为信息类型(错误, 信息或警告). 数值信息最多可包括4个占位符. 例如,可传输只有run-time可识别的值.采用ADS-OCX发送的信息均被自动地写进Windows NT/2000 事件监视器.

Visual Basic 程序可调用Windows NT/2000事件查看器.这样就可检查事件监视器中所有的信息.事件监视器和事件查看器是Windows NT/2000套件的标准组件.更多信息请参阅Windows NT/2000文档.

PLC程序每隔5秒周期地发送信息.该信息被写进Windows NT/2000 事件监视器.PLC文档中有PLC函数ADSLOGDINT()的描述.

注意:所有通过TwinCAT路由器发送的信息均可在System Manager中在线实时地查看.此操作要求View 菜单中的logger output 必须处于激活状态.

Windows NT/2000提供了可以查看保存在事件监视器中的信息的相关API函数.但Visual Basic并未提供任何组件定位事件监视器中保存的信息.

在6/98发行由 Steingräberverlag (http://www.basicpro.de)出版的basicopro 杂志第56页, 有一篇文章说明了 Visual Basic中可用的相关API函数.

应用程序

在开发和调试应用程序时这项技术非常有用.例如,一个PLC程序或一个 Visual Basic 程序可以设置某个应用程序内部状态(System Manager中) 并保存( 在Windows NT/2000 中). 这种方式跟踪应用程序不必安装开发环境(例如一台计算机).

必须确保短时间内无大量信息传送,因为这会导致整个系统性能的下降.

提示:如果想要在程序中记录信息(例如一台机器出现故障) ,此时应使用 TwinCAT Event Logger. 它比Windows NT/2000 Event Logger强大的多, 而且其能更好地满足自动化技术的要求.

Visual Basic 程序

Option Explicit

'--- Is called at the start of the program ---
Private Sub Form_Load()
    cboMessageType.ListIndex = 0
    AdsOcx1.EnableErrorHandling = True
    '--- Trap messages ---
    Call AdsOcx1.AdsEnableLogNotification(1, 65535, ADSLOG_MSGTYPE_HINT Or ADSLOG_MSGTYPE_ERROR Or ADSLOG_MSGTYPE_WARN)
End Sub

'--- Is called by ADS-OCX when a message arrives ---
Private Sub AdsOcx1_AdsLogNotification(ByVal dateTime As Date, ByVal nMs As Long, _
                                       ByVal dwMsgCtrl As Long, ByVal nServerPort As Long, _
                                       ByVal szDeviceName As String, ByVal szLogMsg As String)
    '--- Display message ---
    lstMessages.AddItem Format(DateValue(dateTime), "!@@@@@@@@@@") & _
                        Format(TimeValue(dateTime), "!@@@@@@@@@@@@@@@") & _
                        Format(nMs, "000   ") & _
                        Format(szDeviceName, "!@@@@@@@@@@") & _
                        Format(nServerPort, "00000     ") & _
                        szLogMsg
End Sub

'--- Submit message ---
Private Sub cmdSend_Click()
    Dim Para1 As Long
    Dim Para2 As Double
    Dim Para3 As Integer
    Dim Para4 As Integer
    '--- Set parameters ---
    Para1 = CLng(txt1Para.Text)
    Para2 = CDbl(txt2Para.Text)
    Para3 = CInt(txt3Para.Text)
    Para4 = CInt(txt4Para.Text)
    '--- Submit message ---
    Call AdsOcx1.AdsLogFmtString(cboMessageType.ItemData(cboMessageType.ListIndex), _
                                 txtMessage.Text, Para1, Para2, Para3, Para4)
End Sub

'--- Display Windows NT/2000 Event Viewer ---
Private Sub cmdEventViewer_Click()
    Call Shell("eventvwr.exe", vbNormalFocus)
End Sub

'--- Delete list ---
Private Sub cmdClearList_Click()
    Call lstMessages.Clear
End Sub

PLC 程序

PROGRAM MAIN
VAR
    PLCVarInteger AT %MW0  : INT;
    TP_1  : TP;
    TOGGEL  : BOOL;
    AdsLogResult  : DINT;
END_VAR

TOGGEL := NOT TOGGEL;
TP_1( IN := TOGGEL, PT := t#5s);
IF (TP_1.Q = 0) THEN
    IF (TOGGEL = 0) THEN
        PLCVarInteger := PLCVarInteger + 1;
        AdsLogResult := ADSLOGDINT(ADSLOG_MSGTYPE_HINT OR ADSLOG_MSGTYPE_LOG , 'Message from the PLC: %d', PLCVarInteger);
    END_IF
END_IF

本例源程序 'Send/receive messages via the TwinCAT Router'.


Beckhoff Information System - English

TwinCAT TcAdsDLL: Examples

删除PLC变量的句柄

本例显示了怎样删除PLC变量的句柄

Dim handle As Long

'--- Is called at the start ---
Private Sub Form_Load()
    txtHandle.Text = handle
End Sub

' --- Is called when "Get Handle" is pressed ---
Private Sub btnGetHandle_Click()
    Call AdsOcx1.AdsCreateVarHandle("MAIN.PLCVar", handle)
    txtHandle.Text = handle
End Sub

' --- Is called when "Release Handle" is pressed ---
Private Sub btnReleaseHandle_Click()
    Call AdsOcx1.AdsDeleteVarHandle(handle)
    handle = 0
    txtHandle.Text = handle
End Sub

本例源程序 'Delete a handle of a PLC variable'


Event-driven reading (with conversion to another type)

TwinCAT ADS-OCX: Examples

事件驱动读取 (转换通知数据)

TwinCAT 2.8 Build > 743或更高版本

窗体上任务

PLC中有四个不同类型的变量.要采用最有效的方式读取这些变量,并将其值显示在Visual Basic.可用一检查框转换两种不同的连接模式(ADSTRANS_SERVERCYCLE 或ADSTRANS_SERVERONCHA).用两个按钮来建立或断开到PLC变量的连接.

PLC变量为结构化的数据类型. 例如,PLC以数据块的形式向AdsOcx客户端发送. 然而AdsOcx只能解析特定的数据类型(包括变体类型),并将其作为VB事件例程的参数.首先,用户可以利用 AdsReadVarConvertConnect 方法来规定VB事件例程中的变体类型变量. 通过AdsOcx将事件数据复制到变体变量中,并传给VB事件例程. 一个变体数组可以装载PLC中的一个复杂structure.每个变体元素可装载的数据量要依据每个元素的类型. 必须考虑一些例外情况, 比如字符串(字符串长度必须事先适当地设定)和 boolean变量 (2-byte VB boolean由1 byte的数据组成).

下面为要在窗体上显示的PLC变量:

  • 枚举类型的值被读取到一个长整型变量并在标签中显示.
  • 结构化数据类型的值(structure 包括4个booleans) 被读取到一个长整型变量中并在一个检查框中显示.如果PLC中的布尔变量值为TRUE,则检查框被选中.
  • 在一个列表框中显示字符串数组.
  • 将结构化数据类型的值读取到一个变体数组中,并在另一个列表框中显示之.

本例源程序 complete example sources .

PLC 程序

VAR_GLOBAL
    eColors 		: E_Colors := cWhite;
    st4Switches 	: ST_4Switches;
    arr3Strings 	: ARRAY[1..3] OF STRING :=1('First'), 1('Second'),1('Third');
    stBigStruct 	: ST_BigStruct;
END_VAR

在线实时显示 PLC 数据:

定义枚举类型数据:

TYPE E_Colors :
(
    cUnknown,
    cWhite := 1,
    cBlue := 2,
    cRed := 3,
    cBlack
);
END_TYPE

定义具有4个boolean类型变量的 structure:

TYPE ST_4Switches :
STRUCT
    bLevel1 : BOOL;
    bLevel2 : BOOL;
    bLevel3 : BOOL;
    bLevel4 : BOOL;
END_STRUCT
END_TYPE

定义结构化的数据类型:

TYPE ST_BigStruct :
STRUCT
    single : REAL;
    long : DINT;
    boolean : BOOL;
    stSub1 : ST_Sub1;
    counter : DINT;
    datetime : DT := DT#2003-01-20-15:12:44;
END_STRUCT
END_TYPE

其中包含2个子结构:

TYPE ST_Sub1 :
STRUCT
    bFirst : BOOL;
    bSecond : BOOL;
    bThird : BOOL;
    stSub2 : ST_Sub2;
END_STRUCT
END_TYPE
TYPE ST_Sub2 :
STRUCT
    integer : INT;
    double : LREAL;
    string20 : STRING(20) := 'Unknown';
END_STRUCT
END_TYPE

VB 应用程序

Option Explicit
Dim adsErr As Long
Dim hConnect_EnumVar As Long
Dim hConnect_4Switches As Long
Dim hConnect_StringArray As Long
Dim hConnect_BigStruct As Long

加载窗体时建立到第一个PLC runtime 系统的连接:

Private Sub Form_Load()
    AdsOcx1.AdsAmsServerNetId = AdsOcx1.AdsAmsClientNetId
    AdsOcx1.AdsAmsServerPort = 801
    AdsOcx1.EnableErrorHandling = True
End Sub

鼠标单击 AdsReadVarConvertConnect 按钮时建立到PLC 变量的连接. 程序执行成功后, AdsReadVarConvertConnect 方法返回一个句柄. 引用此句柄标识该连接并最后将其移除.

1. PLC中的枚举类型只占用2bytes 的内存. 这2bytes在该事件函数中被读取到一个长整型变量(four bytes) 中. 采用VB integer 数据类型会更有效.

2. structure 类型转换的4 boolean 值占据4个独立的PLC内存bytes. 它们在该事件函数中被读取到一个长整型变量中.

3. 数组中的字符串总共占用243 bytes 的PLC内存(定义字符串长度 + null 结束标志1 byte )*3. 为了能分隔每个字符串,每个VB字符串长度必须与PLC中字符串的长度相对应. 如果字符串长度为零, 将无事件数据复制到字符串变量.

4. structure 变量被读取到一个一维变体数组中.每个数组元素可为不同的数据类型. 但是,建立连接之前, 每个数组元素必须初始化为合适的类型.

Private Sub cmdConnect_Click()
    Dim adsTransMode As ADSOCXTRANSMODE
    adsTransMode = IIf(chkTransMode.Value = vbChecked, ADSTRANS_SERVERCYCLE, ADSTRANS_SERVERONCHA)

    'Connects to enum var
    Dim convertedEnumVar As Long
    adsErr = AdsOcx1.AdsReadVarConvertConnect(".eColors", adsTransMode, 300, hConnect_EnumVar, convertedEnumVar, lblEnum)

    'Connects to struct with 4 boolean variables
    Dim converted4Switches As Long
    adsErr = AdsOcx1.AdsReadVarConvertConnect(".st4Switches", adsTransMode, 300, hConnect_4Switches, converted4Switches, chk4Switches)

    'Connects to array of strings
    Dim convertedStringArray(1 To 3) As String
    Dim i As Integer
    For i = LBound(convertedStringArray) To UBound(convertedStringArray)
        convertedStringArray(i) = String(81, "#")
    Next i
    adsErr = AdsOcx1.AdsReadVarConvertConnect(".arr3Strings", adsTransMode, 300, hConnect_StringArray, convertedStringArray, lstStringArray)

    'Connects to struct variable
    Dim convertedBigStruct(1 To 11) As Variant
    convertedBigStruct(1) = CSng(0) 		'stBigStruct.single
    convertedBigStruct(2) = CLng(0)		'stBigStruct.long
    convertedBigStruct(3) = CBool(False)		'stBigStruct.boolean
    convertedBigStruct(4) = CBool(False)		'stBigStruct.stSub1.bFirst
    convertedBigStruct(5) = CBool(False)		'stBigStruct.stSub1.bSecond
    convertedBigStruct(6) = CBool(False)		'stBigStruct.stSub1.bThird
    convertedBigStruct(7) = CInt(0)		'stBigStruct.stSub1.stSub2.integer
    convertedBigStruct(8) = CDbl(0)		'stBigStruct.stSub1.stSub2.double
    convertedBigStruct(9) = CStr(String(21, "*"))	'stBigStruct.stSub1.stSub2.string20
    convertedBigStruct(10) = CLng(0)		'stBigStruct.counter
    convertedBigStruct(11) = CDate(0)		'stBigStruct.datetime
    adsErr = AdsOcx1.AdsReadVarConvertConnect(".stBigStruct", adsTransMode, 300, hConnect_BigStruct, convertedBigStruct, lstBigStruct)

    cmdConnect.Enabled = False
    cmdDisconnect.Enabled = True
End Sub

鼠标单击 AdsDisconnectEx 按钮会断开到PLC变量的连接:

Private Sub cmdDisconnect_Click()
    adsErr = AdsOcx1.AdsDisconnectEx(hConnect_EnumVar)
    adsErr = AdsOcx1.AdsDisconnectEx(hConnect_4Switches)
    adsErr = AdsOcx1.AdsDisconnectEx(hConnect_StringArray)
    adsErr = AdsOcx1.AdsDisconnectEx(hConnect_BigStruct)

    cmdConnect.Enabled = True
    cmdDisconnect.Enabled = False
End Sub

AdsReadConvertConnectUpdate 事件例程. 这个事件例程被周期地调用(选择 ADSTRANS_SERVERCYCLE)或只在PLC变量值改变时被调用(选择ADSTRANS_SERVERONCHA).hUser 参数可用于将事件数据分配给合适的控件(标签,检查框,列表框).

Private Sub AdsOcx1_AdsReadConvertConnectUpdate(ByVal dateTime As Date, ByVal nMs As Long, ByVal hConnect As Long, data As Variant, Optional hUser As Variant)
    Dim i As Integer
    If TypeOf hUser Is CheckBox Then

        chk4Switches.Value = IIf(data = 0, vbUnchecked, vbChecked)
        chk4Switches.Caption = IIf(data = 0, "ALL disbled", "At least ONE enabled")

    ElseIf TypeOf hUser Is ListBox Then

        If hUser Is lstStringArray Then
            Call lstStringArray.Clear
            For i = LBound(data) To UBound(data)
                Call lstStringArray.AddItem("Type: " & TypeName(data(i)) & " Value: " & data(i))
            Next i
        ElseIf hUser Is lstBigStruct Then
            Call lstBigStruct.Clear
            For i = LBound(data) To UBound(data)
                Call lstBigStruct.AddItem("Type: " & TypeName(data(i)) & " Value: " & data(i))
            Next i
        End If

    Else 'lblEnum
        Dim objLabel As Label
        Set objLabel = hUser
        objLabel.Caption = "eColors: " & data
    End If
End Sub

本例源程序 complete example sources .


Reading and writing of variables with Visual Basic

TwinCAT TcADSDLL: Examples

用Visual Basic读写PLC变量

本例程序演示了在Visual Basic中怎样利用TcAdsDll COM接口来读写PLC变量.

在Visual Basic程序的 load 事件函数中通过 CreateObject 创建 TcClient 对象. 采用 Connect 方法与PLC建立连接.将 "0.0.0.0.0.0" 传送到 NetID, 从而与本地计算机建立一个连接.

通过按下Read按钮将变量从PLC中读出.因此调用TcAdsSync 对象的Read 方法. 将要读取变量长度的byte 数组传送到这个方法.这个数组会被复制到相应的VB数据类型.如果其数据层次结构一致, 这时可使用RtlMoveMemory API 例程. 在写进PLC时,现将byte 数组用所要的数据填充,之后再调用 Write 方法.

Visual Basic 程序

Option Explicit
Dim WithEvents ads As TcAdsDll.TcAdsSync

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(lpDest As Any, lpSrc As Any, ByVal Length As Long)

Private Sub Form_Load()
  On Error GoTo errFunc
  Dim client As TcAdsDll.TcClient
  Dim netId As AmsNetId

  Set client = CreateObject("TcAdsDll.TcClient")
  netId.b(0) = 0
  netId.b(1) = 0
  netId.b(2) = 0
  netId.b(3) = 0
  netId.b(4) = 0
  netId.b(5) = 0
  Set ads = client.Connect(netId, 801)

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub cmdRead_Click()
  On Error Resume Next
  TextBoolean.Text = ReadBoolean(&H4020&, 0)
  TextInteger.Text = ReadInteger(&H4020&, 2)
  TextLong.Text = ReadLong(&H4020&, 4)
  TextSingle.Text = ReadSingle(&H4020&, 8)
  TextDouble.Text = ReadDouble(&H4020&, 12)
End Sub

Private Sub cmdWrite_Click()
On Error Resume Next
  Call WriteBoolean(&H4020&, 0, TextBoolean.Text)
  Call WriteInteger(&H4020&, 2, TextInteger.Text)
  Call WriteLong(&H4020&, 4, TextLong.Text)
  Call WriteSingle(&H4020&, 8, TextSingle.Text)
  Call WriteDouble(&H4020&, 12, TextDouble.Text)
End Sub

Private Sub WriteBoolean(indexGroup As Long, indexOffset As Long, val As Boolean)
  On Error GoTo errFunc
  Dim arrRead(0 To 0) As Byte
  Dim cbLen As Long

  cbLen = 1
  If (val = True) Then
    arrRead(0) = 1
  Else
    arrRead(0) = 0
  End If

  Call ads.Write(indexGroup, indexOffset, cbLen, arrRead(0))

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub WriteInteger(indexGroup As Long, indexOffset As Long, val As Integer)
  On Error GoTo errFunc
  Dim arrRead(0 To 1) As Byte
  Dim cbLen As Long

  cbLen = 2
  Call CopyMemory(arrRead(0), val, cbLen)
  Call ads.Write(indexGroup, indexOffset, cbLen, arrRead(0))

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub WriteLong(indexGroup As Long, indexOffset As Long, val As Long)
  On Error GoTo errFunc
  Dim arrRead(0 To 3) As Byte
  Dim cbLen As Long

  cbLen = 4
  Call CopyMemory(arrRead(0), val, cbLen)
  Call ads.Write(indexGroup, indexOffset, cbLen, arrRead(0))

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub WriteSingle(indexGroup As Long, indexOffset As Long, val As Single)
  On Error GoTo errFunc
  Dim arrRead(0 To 3) As Byte
  Dim cbLen As Long

  cbLen = 4
  Call CopyMemory(arrRead(0), val, cbLen)
  Call ads.Write(indexGroup, indexOffset, cbLen, arrRead(0))

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub WriteDouble(indexGroup As Long, indexOffset As Long, val As Double)
  On Error GoTo errFunc
  Dim arrRead(0 To 7) As Byte
  Dim cbLen As Long

  cbLen = 8
  Call CopyMemory(arrRead(0), val, cbLen)
  Call ads.Write(indexGroup, indexOffset, cbLen, arrRead(0))

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Function ReadBoolean(indexGroup As Long, indexOffset As Long) As Boolean
  On Error GoTo errFunc
  Dim arrRead(0 To 0) As Byte
  Dim cbRead As Long
  Dim cbLen As Long
  Dim val As Boolean

  cbLen = 1
  Call ads.Read(indexGroup, indexOffset, cbLen, cbRead, arrRead(0))

  If (arrRead(0) = 0) Then
    val = False
  Else
    val = True
  End If
  ReadBoolean = val

  Exit Function
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Function

Private Function ReadInteger(indexGroup As Long, indexOffset As Long) As Integer
  On Error GoTo errFunc
  Dim arrRead(0 To 1) As Byte
  Dim cbRead As Long
  Dim cbLen As Long
  Dim val As Integer

  cbLen = 2
  Call ads.Read(indexGroup, indexOffset, cbLen, cbRead, arrRead(0))
  Call CopyMemory(val, arrRead(0), cbLen)
  ReadInteger = val

  Exit Function
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Function

Private Function ReadLong(indexGroup As Long, indexOffset As Long) As Long
On Error GoTo errFunc
  Dim arrRead(0 To 3) As Byte
  Dim cbRead As Long
  Dim cbLen As Long
  Dim val As Long

  cbLen = 4
  Call ads.Read(indexGroup, indexOffset, cbLen, cbRead, arrRead(0))
  Call CopyMemory(val, arrRead(0), cbLen)
  ReadLong = val

  Exit Function
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Function

Private Function ReadSingle(indexGroup As Long, indexOffset As Long) As Single
On Error GoTo errFunc
  Dim arrRead(0 To 3) As Byte
  Dim cbRead As Long
  Dim cbLen As Long
  Dim val As Single

  cbLen = 4
  Call ads.Read(indexGroup, indexOffset, cbLen, cbRead, arrRead(0))
  Call CopyMemory(val, arrRead(0), cbLen)
  ReadSingle = val

  Exit Function
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Function

Private Function ReadDouble(indexGroup As Long, indexOffset As Long) As Double
  On Error GoTo errFunc
  Dim arrRead(0 To 7) As Byte
  Dim cbRead As Long
  Dim cbLen As Long
  Dim val As Double

  cbLen = 8
  Call ads.Read(indexGroup, indexOffset, cbLen, cbRead, arrRead(0))
  Call CopyMemory(val, arrRead(0), cbLen)
  ReadDouble = val

  Exit Function
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Function

PLC 程序

PROGRAM MAIN
VAR
  PLCBoolVar AT %MX0.0 : BOOL := TRUE;
  PLCIntVar AT %MB2 : INT := 75;
  PLCDIntVar AT %MB4 : DINT := 43234532;
  PLCRealVar AT %MB8 : REAL := 3.1415;
  PLCLRealVar AT %MB12 : LREAL := 3.141592654;
END_VAR

;

本例源程序 'Reading and writing of variables'.


Event-driven reading with Visual Basic

TwinCAT TcAdsDLL: Examples

在Visual Basic中采用事件驱动读取

在窗体的load 事件中,通过AddDeviceNotification 方法建立到PLC变量的连接.如果一个PLC变量改变,便会触发TcAdsSync 对象的DeviceNotification 事件.通过unload或单击Stop按钮事件中的 DelDeviceNotification 方法断开到PLC变量的连接.

Option Explicit
Dim WithEvents ads As TcAdsDll.TcAdsSync
Attribute ads.VB_VarHelpID = -1
Dim hValInteger As Long
Dim hValDouble As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
 (lpDest As Any, lpSrc As Any, ByVal Length As Long)

Private Sub Form_Load()
On Error GoTo errFunc
  Dim client As TcAdsDll.TcClient
  Dim netId As AmsNetId

  Set client = CreateObject("TcAdsDll.TcClient")
  netId.b(0) = 0
  netId.b(1) = 0
  netId.b(2) = 0
  netId.b(3) = 0
  netId.b(4) = 0
  netId.b(5) = 0
  Set ads = client.Connect(netId, 801)

  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub cmdStart_Click()
  Dim delay As Long
  Dim cycle As Long

  delay = 0
  cycle = 1000000 '100ms

  If hValInteger = 0 Then
    hValInteger = ads.AddDeviceNotification(&H4020&, 2, 2, ADSTRANS_SERVERONCHA, delay, cycle)
  End If
  If hValDouble = 0 Then
    hValDouble = ads.AddDeviceNotification(&H4020&, 12, 8, ADSTRANS_SERVERONCHA, delay, cycle)
  End If
  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

Private Sub cmdStop_Click()
  Call DeleteNotifications
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Call DeleteNotifications
End Sub

Private Sub ads_DeviceNotification(pTime As TcAdsDll.TimeStamp, ByVal hNotification As Long, ByVal cbLen As Long, pData As Byte)
  Dim integerVal As Integer
  Dim doubleVal As Double

  If (hNotification = hValInteger And cbLen = 2) Then
    Call CopyMemory(integerVal, pData, cbLen)
    TextInteger.Text = integerVal
  ElseIf (hNotification = hValDouble And cbLen = 8) Then
    Call CopyMemory(doubleVal, pData, cbLen)
    TextDouble.Text = doubleVal
  End If
End Sub

Private Sub DeleteNotifications()
On Error GoTo errFunc
  If hValInteger <> 0 Then
    Call ads.DelDeviceNotification(hValInteger)
    hValInteger = 0
  End If
  If hValDouble <> 0 Then
    Call ads.DelDeviceNotification(hValDouble)
    hValDouble = 0
  End If
  Exit Sub
  errFunc:
    MsgBox "Error: (0x" & Format(Hex(Err.Number), "00000000") & "), " & Err.Description
End Sub

PLC 程序

PROGRAM MAIN
VAR
  PLCBoolVar AT %MX0.0 : BOOL := TRUE;
  PLCIntVar AT %MB2 : INT := 75;
  PLCDIntVar AT %MB4 : DINT := 43234532;
  PLCRealVar AT %MB8 : REAL := 3.1415;
  PLCLRealVar AT %MB12 : LREAL := 3.141592654;
END_VAR

;

本例源程序 'Event driven reading' .


Visual Basic

ADS-Script DLL 并未利用Visual Basic的所有灵活性. ADS-OCX 更适于从Visual Basic中访问TwinCAT, 因为其更易匹配Visual Basic. ADS-Script DLL 的特殊价值在于其使用脚本语言(VBScript 或 JScript). 为了更清楚地演示ADS-Script DLL的灵活性,这里给出一个Visual Basic 程序.

首先加载PLC程序到PC中的本地PLC (run-time system 1). 然后启动Visual Basic程序:

除非提供一些其他说明,否则应先创建一个到本地PLC的通讯管道.如果输入别的AdsAmsNetId 和其他的端口号,便可创建到其他PC中不同run-time system 的连接.然而,上述PLC程序在其他 run-time system中必须处于激活状态.如果需要,远程计算机接入TwinCAT System Control 也是必须的.

如果选择读写PLC变量通过地址(IndexGroup和IndexOffset)或是变量名. 这是无意义的操作-这只不过演示了有这2种可能性.

在右上角可读写PLC的状态. 5对应着RUN 状态,而6对应着STOP.

Option Explicit

Public TcClientSync As TCSCRIPTLib.TcScriptSync
Public nAdsAmsPort As Integer
Public strAdsAmsNetId As String

Private Sub Form_Load()
Set TcClientSync = CreateObject("TcScript.TcScriptSync")
Call TcClientSync.ConnectTo("", 801)
End Sub

Private Sub Form_Terminate()
Set TcClientSync = Nothing
End Sub

Private Sub cmdConnect_Click()
nAdsAmsPort = CLng(txtAdsAmsPort.Text)
if (Len(txtAdsAmsNetId.Text) > 0) Then
strAdsAmsNetId = txtAdsAmsNetId.Text
Else
strAdsAmsNetId = ""
End If
Set TcClientSync = CreateObject("TcScript.TcScriptSync")
Call TcClientSync.ConnectTo(strAdsAmsNetId, nAdsAmsPort)
End Sub

Private Sub cmdRead_Click()
Dim VarBool As Variant
Dim VarInt8 As Byte
Dim VarInt16 As Integer
Dim VarInt32 As Long
Dim VarReal32 As Single
Dim VarReal64 As Double

VarBool = TcClientSync.ReadBool(&H4020, 0)
lblBoolean.Caption = VarBool

VarInt8 = TcClientSync.ReadInt8(&H4020, 2)
lblInt8.Caption = VarInt8

VarInt16 = TcClientSync.ReadInt16(&H4020, 4)
lblInt16.Caption = VarInt16

VarInt32 = TcClientSync.ReadInt32(&H4020, 6)
lblInt32.Caption = VarInt32

VarReal32 = TcClientSync.ReadReal32(&H4020, 10)
lblReal32.Caption = VarReal32

VarReal64 = TcClientSync.ReadReal64(&H4020, 14)
lblReal64.Caption = VarReal64
End Sub

Private Sub cmdReadByName_Click()
Dim VarBool As Variant
Dim VarInt8 As Byte
Dim VarInt16 As Integer
Dim VarInt32 As Long
Dim VarReal32 As Single
Dim VarReal64 As Double

VarBool = TcClientSync.ReadBoolVar(".PLCBoolVar")
lblBoolean.Caption = VarBool

VarInt8 = TcClientSync.ReadInt8Var(".PLCSIntVar")
lblInt8.Caption = VarInt8

VarInt16 = TcClientSync.ReadInt16Var(".PLCIntVar")
lblInt16.Caption = VarInt16

VarInt32 = TcClientSync.ReadInt32Var(".PLCDIntVar")
lblInt32.Caption = VarInt32

VarReal32 = TcClientSync.ReadReal32Var(".PLCRealVar")
lblReal32.Caption = VarReal32

VarReal64 = TcClientSync.ReadReal64Var(".PLCLRealVar")
lblReal64.Caption = VarReal64
End Sub

Private Sub cmdWrite_Click()
Dim VarBool As Boolean
Dim VarInt8 As Byte
Dim VarInt16 As Integer
Dim VarInt32 As Long
Dim VarReal32 As Single
Dim VarReal64 As Double

If (txtBoolean.Text <> "") Then
VarBool = CBool(txtBoolean.Text)
Call TcClientSync.WriteBool(&H4020,
0, VarBool)
txtBoolean.Text = VarBool
End If

If (txtInt8.Text <> "") Then
VarInt8 = CByte(txtInt8.Text)
Call
TcClientSync.WriteInt8(&H4020, 2, VarInt8)
txtInt8.Text = VarInt8
End If

If (txtInt16.Text <> "") Then
VarInt16 = CInt(txtInt16.Text)
Call
TcClientSync.WriteInt16(&H4020, 4, VarInt16)
txtInt16.Text = VarInt16
End If

If (txtInt32.Text <> "") Then
VarInt32 = CLng(txtInt32.Text)
Call
TcClientSync.WriteInt32(&H4020, 6, VarInt32)
txtInt32.Text = VarInt32
End If

If (txtReal32.Text <> "") Then
VarReal32 = CSng(txtReal32.Text)
Call
TcClientSync.WriteReal32(&H4020, 10, VarReal32)
txtReal32.Text = VarReal32
End If

If (txtReal64.Text <> "") Then
VarReal64 = CDbl(txtReal64.Text)
Call
TcClientSync.WriteReal64(&H4020, 14, VarReal64)
txtReal64.Text = VarReal64
End If
End Sub

Private Sub cmdWriteByName_Click()
Dim VarBool As Boolean
Dim VarInt8 As Byte
Dim VarInt16 As Integer
Dim VarInt32 As Long
Dim VarReal32 As Single
Dim VarReal64 As Double

If (txtBoolean.Text <> "") Then
VarBool = CBool(txtBoolean.Text)
Call
TcClientSync.WriteBoolVar(".PLCBoolVar", VarBool)
txtBoolean.Text = VarBool
End If

If (txtInt8.Text <> "") Then
VarInt8 = CByte(txtInt8.Text)
Call
TcClientSync.WriteInt8Var(".PLCSIntVar", VarInt8)
txtInt8.Text = VarInt8
End If

If (txtInt16.Text <> "") Then
VarInt16 = CInt(txtInt16.Text)
Call
TcClientSync.WriteInt16Var(".PLCIntVar", VarInt16)
txtInt16.Text = VarInt16
End If

If (txtInt32.Text <> "") Then
VarInt32 = CLng(txtInt32.Text)
Call
TcClientSync.WriteInt32Var(".PLCDIntVar", VarInt32)
txtInt32.Text = VarInt32
End If

If (txtReal32.Text <> "") Then
VarReal32 = CSng(txtReal32.Text)
Call
TcClientSync.WriteReal32Var(".PLCRealVar", VarReal32)
txtReal32.Text = VarReal32
End If

If (txtReal64.Text <> "") Then
VarReal64 = CDbl(txtReal64.Text)
Call
TcClientSync.WriteReal64Var(".PLCLRealVar", VarReal64)
txtReal64.Text = VarReal64
End If
End Sub

Private Sub cmdReadState_Click()
lblAdsState.Caption = TcClientSync.ReadAdsState()
End Sub

Private Sub cmdWriteState_Click()
If (txtAdsState.Text <> "") Then
If (txtAdsState.Text <>
TcClientSync.ReadAdsState()) Then
Call
TcClientSync.WriteAdsState(txtAdsState.Text)
End If
End If
End Sub

本例源程序 'Visual Basic'.


Übersicht

Beschreibung | Quelltexte
Beispiel 1: Lesen und Schreiben von Variablen mit Visual Basic | TcAdsDll ComSample01.exe
Beispiel 2: Ereignisgesteuertes Lesen mit Visual Basic | TcAdsDll ComSample02.exe