对于服务器的配置与同步通讯的配置一样,这里不需再讲解,若有不清楚的,可以参阅之前发布的<运用VC#编程通过OPC方式实现PC机与西门子plc通讯> 2、 OPC编程 变量组、项的命名规则与同步通讯的一样,这里不再描叙,下面主要就开发一个异步通讯类 AsynServer来讲解如何编程。 <1>、引用 在VC#开发环境中添加对OpcRcw.Da库以及OpcRcw.Comn库的引用,该库属于.NET库,不属于COM库,西门子虽然编写了类库,以提供对.NET平台的支持,但这些类库仍然难于编程,里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,以简化以后的编程,首先在类的开头使用命名空间: using OpcRcw.Comn; using OpcRcw.Da; using System.Runtime.InteropServices; using System.Collections; <2>、编程 异步编程的原理就是在OPC服务器那边检测当前活动的变量组,一但检测到某一个变量,譬如变量Q0.0从1变成0,就会执行一个回调函数,以实现针对变量发生变化时需要实现的动作,在这里可以采用委托来实现该功能。 1、 在命名空间的内部、类 AsynServer声明之前添加委托的申明: // 定义用于返回发生变化的项的值和其对应的客户句柄 public delegate void DataChange(object[] values,int[] itemsID); 2、 该类继承于西门子提供的库接口IOPCDataCallback public class AsynServer:IOPCDataCallback 在类的开头部分声明变量: struct groupStru { public int groupID; public object groupObj; } internal const int LOCALE_ID = 0x407; //本地语言 private Guid iidRequiredInterface; private string serverType=""; private int hClientGroup = 0; //客户组号 private int nSvrGroupID; // server group handle for the added group private Hashtable hashGroup; //用于把组收集到一起 private int hClientItem=0; //Item号 3、编写构造函数,接收委托参数已确定当数据发生变化时需要执行的方法入口点: //创建服务器 //svrType 服务器类型的枚举 //dataChange 提供用于在数据发生变化时需要执行的函数入口 public AsynServer(ServerType svrType,DataChange dataChange) { switch(svrType) { case ServerType.OPC_Simatichmi_PTPR serverType="OPC.SimaticHMI.PTPro";break; case ServerType.OPC_SimaticNET: serverType="OPC.SimaticNET";break; case ServerType.OPC_SimaticNET_DP: serverType="OPC.SimaticNET.DP";break; case ServerType.OPC_SimaticNET_PD: serverType="OPC.SimaticNET.PD";break; case ServerType.OPCServer_WINCC: serverType="OPCServer.WinCC";break; } hashGroup=new Hashtable(11); dtChange=dataChange; } 4、创建服务器 // 创建一个OPC Server接口 //error 返回错误信息 // public bool Open(out string error) { error="";bool success=true; Type svrComponenttyp ; //获取 OPC Server COM 接口 iidRequiredInterface = typeof(IOPCItemMgt).GUID; svrComponenttyp = System.Type.GetTypeFromProgID(serverType); try { //创建接口 pIOPCServer =(IOPCServer)System.Activator.CreateInstance(svrComponenttyp); error=""; } catch (System.Exception err) //捕捉失败信息 { error="错误信息:"+err.Message;success=false; } return success; } 5、 编写添加Group的函数 /// /// 添加组 /// /// /// /// /// /// public bool AddGroup(string groupName,int bActive,int updateRate,out string error) { error="";bool success=true; int dwLCID = 0x407; //本地语言为英语 int pRevUpdateRate; float deadband = 0; // 处理非托管COM内存 GCHandle hDeadband; IntPtr pTimeBias = IntPtr.Zero; hDeadband = GCHandle.Alloc(deadband,GCHandleType.Pinned); try { pIOPCServer.AddGroup(groupName, //组名 bActive, //创建时,组是否被激活 updateRate, //组的刷新频率,以ms为单位 hClientGroup, //客户号 pTimeBias, //这里不使用 (IntPtr)hDeadband, dwLCID, //本地语言 out nSvrGroupID, //移去组时,用到的组ID号 out pRevUpdateRate, //返回组中的变量改变时的最短通知时间间隔 ref iidRequiredInterface, out pobjGroup1); //指向要求的接口 hClientGroup=hClientGroup+1; groupStru grp=new groupStru(); grp.groupID=nSvrGroupID;grp.groupObj=pobjGroup1; this.hashGroup.Add(groupName,grp);//储存组信息 // 对异步操作设置回调,初始化接口 pIConnectionPointContainer = (IConnectionPointContainer)pobjGroup1; Guid iid = typeof(IOPCDataCallback).GUID; pIConnectionPointContainer.FindConnectionPoint(ref iid,out pIConnectionPoint); pIConnectionPoint.Advise(this,out dwCookie); } catch (System.Exception err) //捕捉失败信息 { error="错误信息:"+err.Message;success=false; } finally { if (hDeadband.IsAllocated) hDeadband.Free(); } return success; } 6、 编写激活、或者取消激活组的函数 在同步编程中对于组的激活或者取消激活没有实质的意义,但在异步通讯编程中却异常重要,这是因为OPC服务器只对当前处于活动状态的组中的变量进行监控,同时这也是很有必要的,因为我们可以把不同界面中的变量编程不同的组,即同一界面中的变量规成一个组,而在某一时刻提供给用户的只有一个界面,让该界面中用到的组处于活动状态,这样执行委托调用时只会执行于该界面中有关的变量检测,而如果让所有的组处于活动状态,则当前没有显示给用户的界面用到的变量若发生变化也会触发对委托函数的调用,这根本是没有必要的,同时会大大降低程序的性能,请严格控制组的激活。 /// /// 激活或者取消激活组 /// /// /// /// /// public bool AciveGroup(string groupName,bool toActive,out string error) { error="";bool success=true; //通过名称获取组 object grp=((groupStru)hashGroup[groupName]).groupObj; IOPCGroupStateMgt groupStateMgt=(IOPCGroupStateMgt)grp; //初始化传递参数 IntPtr pRequestedUpdateRate = IntPtr.Zero; //由客户指定的Item更新间隔时间 int nRevUpdateRate = 0; //由服务器返回的能够更新的最短时间间隔 IntPtr hClientGroup = IntPtr.Zero; //客户组 IntPtr pTimeBias = IntPtr.Zero; IntPtr pDeadband = IntPtr.Zero; IntPtr PLCID = IntPtr.Zero; // 激活或者取消激活组 int nActive = 0; GCHandle hActive = GCHandle.Alloc(nActive,GCHandleType.Pinned); if(toActive) hActive.Target = 1; else hActive.Target = 0; try { groupStateMgt.SetState(pRequestedUpdateRate,out nRevUpdateRate,hActive.AddrOfPinnedObject(),pTimeBias,pDeadband,pLCID,hClientGroup); } catch(System.Exception err) { error="错误信息:"+err.Message;success=false; } finally { hActive.Free(); } return success; } 7、 向指定的组中添加变量的函数 /// /// 向指定的组添加一系列项 /// /// /// /// /// public bool AddItems(string groupName,string[] itemsName,int[] itemsID) { bool success=true; OPCITEMDEF[] ItemDefArray=new OPCITEMDEF[itemsName.Length]; for(int i=0;i { hClientItem=hClientItem+1; //客户项自动加1 最新评论热门文章
关闭
站长推荐
|