网站标志
导航菜单
当前日期时间
当前时间:
购物车
购物车中有 0 件商品 去结算 我的订单
商品搜索
商品搜索:
文章正文
运用VC#编程通过OPC方式实现PC机与西门子PLC通讯(1)
作者:管理员    发布于:2015-08-16 22:26:50    文字:【】【】【

 1、 配置OPC服务器
   对于服务器的配置与同步通讯的配置一样,这里不需再讲解,若有不清楚的,可以参阅之前发布的<运用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 返回错误信息
 //若为true,创建成功,否则创建失败
 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的函数
 ///
/// 添加组
 ///
/// 组名
 /// /创建时,组是否被激活
 /// //组的刷新频率,以ms为单位
 /// 返回错误信息
 /// 若为true,添加成功,否则添加失败
 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服务器只对当前处于活动状态的组中的变量进行监控,同时这也是很有必要的,因为我们可以把不同界面中的变量编程不同的组,即同一界面中的变量规成一个组,而在某一时刻提供给用户的只有一个界面,让该界面中用到的组处于活动状态,这样执行委托调用时只会执行于该界面中有关的变量检测,而如果让所有的组处于活动状态,则当前没有显示给用户的界面用到的变量若发生变化也会触发对委托函数的调用,这根本是没有必要的,同时会大大降低程序的性能,请严格控制组的激活。
 ///
/// 激活或者取消激活组
 ///
/// 指定组名
 /// true为激活,false为取消激活
 /// 若有错误,返回错误信息
 /// 若为true,添加成功,否则添加失败
 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、 向指定的组中添加变量的函数
 ///
/// 向指定的组添加一系列项
 ///
/// 指定组名
 /// 完整的item名数组
 /// 由服务器返回读写数据时需要使用的item号
 /// 无错误,返回true,否则返回false
 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
 ItemDefArray[i].szAccessPath = ""; // 可选的通道路径,对于Simatiic Net不需要。
 ItemDefArray[i].szItemID = itemsName[i]; // ItemID, see above
 ItemDefArray[i].bActive = 1; // item is active
 ItemDefArray[i].hClient = hClientItem; // client handle ,在OnDataChange中会用到
 ItemDefArray[i].dwBlobSize = 0; // blob size
 ItemDefArray[i].pBlob = IntPtr.Zero; // pointer to blob
ItemDefArray[i].vtRequestedDataType = 4; //DWord数据类型
 }
 //初始化输出参数
 IntPtr pResults = IntPtr.Zero;
 IntPtr pErrors = IntPtr.Zero;
 try
{
 // 添加项到组
 object grp=((groupStru)hashGroup[groupName]).groupObj;
 ((IOPCItemMgt)grp).AddItems(itemsName.Length,ItemDefArray,out pResults,out pErrors);
 
int[] errors = new int[itemsName.Length];
IntPtr pos = pResults;
 Marshal.Copy(pErrors, errors, 0,itemsName.Length);
 for(int i=0;i {
 if (errors[i] == 0)
 {
 OPCITEMRESULT result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));
 itemsID[i] = result.hServer;
pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMRESULT)));
 }
 else
 {
 String pstrError;
 pIOPCServer.GetErrorString(errors[0],0x407,out pstrError);
success=false;
 break;
 }
 }
 SetItenClient(groupName,itemsID,itemsID); //要求始终只有一个组被激活,才不会引起冲突。
 }
 catch (System.Exception err) // catch for error in adding items.
 {
 success=false;
 //error="错误信息:"+error+err.Message;
 }
 finally
 {
 // 释放非托管内存
 if(pResults != IntPtr.Zero)
 {
 Marshal.FreeCoTaskMem(pResults);
 pResults = IntPtr.Zero;
 }
 if(pErrors != IntPtr.Zero)
 {
 Marshal.FreeCoTaskMem(pErrors);
 pErrors = IntPtr.Zero;
}
 }
 return success;
 }
   

浏览 (61) | 评论 (0) | 评分(0) | 支持(0) | 反对(0) | 发布人:管理员
将本文加入收藏夹
新闻详情
脚注栏目
|
脚注信息
机电工程网(C) 2015-2020 All Rights Reserved.    联系我们