SimaticNet是西门子全集成自动化系统中的一个重要组成部分,它为完善的工业自动化控制系统的通讯提供部件和网络,同时提供多个OPCServer,为数据的外部访问提供接口,本文主要以OPC.SimaticNET为例说明。
90年代OPC基金会开发了一系列的通讯接口比如 Data Access (DA), Alarm & Events (A&E), Historical Data Access (HDA) and Data eXchange (DX),统称传统OPC。今天主要使用的OPC DA通讯方式,这个在1995年左右还是很流行的方法,最近几年OPC Foundation又开发了新的 OPC Unified Architecture (UA) 标准,更好的适应了工业4.0。关于传统OPC和OPC UA的区别,后面会单独来说。
许多OPC服务器,包括OPC.SimaticNet,是在COM平台开发的,从而对于基于.NET框架下的C#语言,作为客户端程序语言访问OPCServer,需要解决两个平台间无缝迁移的问题。OPC基金会对会员提供了OpcRcw动态链接库,OPC NET COM 包装器和OPC NET API,将OPC复杂的规范封状成简单易用的C#类 ,可以比较容易地实现数据访问。
OPC主要包含两种接口:CUSTOM标准接口和OLE自动化标准接口,自定义接口是服务商必须提供的,而自动化接口则是可选的。
自定义接口是一组COM接口,主要用于采用C++语言的应用程序开发;
自动化接口是一组OLE接口,主要用于采用VB,DELPHI,Excel等基于脚本编程语言的应用程序开发。本文是使用C#通过自动化接口来实现的,也是最简单的方式。
首先必须了解的是OPC服务器的对象模型:
程序中涉及到的重要方法和属性比较多,解释下几个容易搞混的:
OPCItem 对象的属性ServerHandle,只读属性,服务器提供给Item的句柄,通过此句柄,Client可以定位到此Item,来对此Item进行后续的操作,比如移动删除;
OPCItem 对象的属性ClientHandle,可读可写属性,客户端分配给Item的句柄,这个句柄可以手动设置,也可由.NET随机选取的,不需要我们来设置,并且每次运行时,这
个句柄都不同,类似于TCP scoket通讯中的Client端分配的端口号。Server端必须指定端口号,Client端随机生成,每次都不一样。
OPCGroup 对象的属性的IsSubscribed,可读可写属性,Group的IsSubscribed为True,此Group才能开始接受服务器的数据属性,此Group才能被订阅。
OPCGroup 对象的事件DataChange (TransactionID As Long, NumItems As Long, ClientHandles() As Long,ItemValues() As Variant, Qualities() As Long, TimeStamps() As Date)需要注意的是NumItems参数是每次事件触发时Group中实际发生数据变化的Item的数量,而不是整个Group里的Items.
OPCGroup 对象的属性UpdateRate,可读可写属性,规定了数据刷新的周期,单位milliseconds.注意的是,不是设定多少ms,实际就是多少,比如给定53ms,OPC server会就近选择50ms.有区间划分的。
源程序如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using OPCAutomation;
namespace OpcClient
{
public partial class OpcClient : Form
{
public OpcClient()
{
InitializeComponent();
}
#region 私有变量
private String strHostIP;
private String strHostName;
private Boolean opc_connected;
private OPCServer LocalServer;
private OPCGroups myGroups;
private OPCGroup myGroup;
private OPCGroup myGroup1;
private OPCItems myItems;
private OPCItems myItems1;
private OPCItem myItem;
int itmHandleClient = 0; /// 客户端句柄
int itmHandleServer = 0; /// 服务端句柄
//**wfx
private OPCItem [] myItemArray;
private OPCItem[] myItemArray1;
#endregion
#region 私有方法
/// <summary>
/// 连接OPC服务器
/// </summary>
/// <param name="remoteServerIP">OPCServerIP</param>
/// <param name="remoteServerName">OPCServer名称</param>
private bool ConnectRemoteServer(string remoteServerIP, string re