中国工控网 - 中国工控网,专业工控信息服务平台 !

商业资讯: 企业新闻 | 人物专访 | 企业关注 | 专家观点 | 技术文摘 | 解决方案 | 展会新闻 | 工控知识 | 工控百科 | 产品推荐

你现在的位置: 首页 > 商业资讯 > 工控知识 > 运用VC#编程通过OPC方式实现PC机与西门子PLC通讯-

运用VC#编程通过OPC方式实现PC机与西门子PLC通讯-

信息来源:gkong.biz  时间:2009-06-24  浏览次数:161

  运用VC#编程通过OPC方式实现PC机与西门子 PLC通讯 - 异步 篇 在上一次发表的运用VC#编程通过OPC方式实现PC机与西门子PLC通讯主要讲的是同步通讯,本文将主要讲解如何编程实现异步通讯
  运用VC#编程通过OPC方式实现PC机与西门子PLC通讯-异步篇
  在上一次发表的<运用VC#编程通过OPC方式实现PC机与西门子PLC通讯>主要讲的是同步通讯,本文将主要讲解如何编程实现异步通讯,通过讲解你也将会知道同步通讯与异步通讯的区别,以及在什么情况下使用异步通讯。
  1、 配置OPC服务器
  对于服务器的配置与同步通讯的配置一样,这里不需再讲解,若有不清楚的,可以参阅之前发布的<运用VC#编程通过OPC方式实现PC机与西门子PLC通讯>
  2、 OPC编程
  变量组、项的命名规则与同步通讯的一样,这里不再描叙,下面主要就开发一个异步通讯类 来讲解如何编程。
  <1>、引用
  在VC#开发环境中添加对库以及库的引用,该库属于.NET库,不属于COM库,西门子虽然编写了类库,以提供对.NET平台的支持,但这些类库仍然难于编程,里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,以简化以后的编程,首先在类的开头使用命名空间:
   ;
   ;
   ;
   ;
  <2>、编程
  异步编程的原理就是在OPC服务器那边检测当前活动的变量组,一但检测到某一个变量,譬如变量Q0.0从1变成0,就会执行一个回调函数,以实现针对变量发生变化时需要实现的动作,在这里可以采用委托来实现该功能。
  1、 在命名空间的内部、类 声明之前添加委托的申明:
  // 定义用于返回发生变化的项的值和其对应的客户句柄
   void ([] ,int[] );
  2、 该类继承于西门子提供的库接口
   :
  在类的开头部分声明变量:
  
  {
   int ;
   ;
  }
   int _ID = 0x407; //本地语言
   Guid ;
   ="";
   int = 0; //客户组号
   int ; // for the
   ; //用于把组收集到一起
   int =0; //Item号
  3、编写构造函数,接收委托参数已确定当数据发生变化时需要执行的方法入口点:
  //创建服务器
  // 服务器类型的枚举
  // 提供用于在数据发生变化时需要执行的函数入口
   ( , )
  {
  ()
  {
  case __:
  ="";;
  case _:
  ="";;
  case __DP:
  ="";;
  case __PD:
  ="";;
  case _:
  ="";;
  }
  =new (11);
  =;
  }
  4、创建服务器
  // 创建一个OPC 接口
  // 返回错误信息
  //若为true,创建成功,否则创建失败
   bool Open(out )
  {
  ="";bool =true;
  Type ;
  //获取 OPC COM 接口
   = ();
   = ();
  try
  {
  //创建接口
   =()();
  ="";
  }
   ( err) //捕捉失败信息
  {
  ="错误信息:"+;=;
  }
   ;
  }
  5、 编写添加的函数
  ///
  /// 添加组
  ///
  /// 组名
  /// /创建时,组是否被激活
  /// //组的刷新频率,以ms为单位
  /// 返回错误信息
  /// 若为true,添加成功,否则添加失败
   bool ( ,int ,int ,out )
  {
  ="";bool =true;
  int = 0x407; //本地语言为英语
  int ;
   = 0;
  // 处理非托管COM内存
   ;
   = ;
   = (,);
  try
  {
  (, //组名
  , //创建时,组是否被激活
  , //组的刷新频率,以ms为单位
  , //客户号
  , //这里不使用
  (),
  , //本地语言
  out , //移去组时,用到的组ID号
  out , //返回组中的变量改变时的最短通知时间间隔
  ref ,
  out 1); //指向要求的接口
  =+1;
   grp=new ();
  =;=1;
  (,grp);//储存组信息
  // 对异步操作设置回调,初始化接口
   = ()1;
  Guid iid = ();
  (ref iid,out );
  (this,out );
  }
   ( err) //捕捉失败信息
  {
  ="错误信息:"+;=;
  }
  {
  if () ();
  }
   ;
  }
  编写激活、或者取消激活组的函数
  在同步编程中对于组的激活或者取消激活没有实质的意义,但在异步通讯编程中却异常重要,这是因为OPC服务器只对当前处于活动状态的组中的变量进行监控,同时这也是很有必要的,因为我们可以把不同界面中的变量编程不同的组,即同一界面中的变量规成一个组,而在某一时刻提供给用户的只有一个界面,让该界面中用到的组处于活动状态,这样执行委托调用时只会执行于该界面中有关的变量检测,而如果让所有的组处于活动状态,则当前没有显示给用户的界面用到的变量若发生变化也会触发对委托函数的调用,这根本是没有必要的,同时会大大降低程序的性能,请严格控制组的激活。
  ///
  /// 激活或者取消激活组
  ///
  /// 指定组名
  /// true为激活,为取消激活
  /// 若有错误,返回错误信息
  /// 若为true,添加成功,否则添加失败
   bool ( ,bool ,out )
  {
  ="";bool =true;
  //通过名称获取组
   grp=(()[]);
   =()grp;
  //初始化传递参数
   = ; //由客户指定的Item更新间隔时间
  int = 0; //由服务器返回的能够更新的最短时间间隔
   = ; //客户组
   = ;
   = ;
   = ;
  // 激活或者取消激活组
  int = 0;
   = (,);
  if()
   = 1;
  else
   = 0;
  try
  {
  (,out ,(),,,,);
  }
  ( err)
  {
  ="错误信息:"+;=;
  }
  {
  ();
  }
   ;
  }
  7、 向指定的组中添加变量的函数
  ///
  /// 向指定的组添加一系列项
  ///
  /// 指定组名
  /// 完整的item名数组
  /// 由服务器返回读写数据时需要使用的item号
  /// 无错误,返回true,否则返回
   bool ( ,[] ,int[] )
  {
  bool =true;
  [] =new [];
  for(int i=0;i{
  =+1; //客户项自动加1
  [i] = ""; // 可选的通道路径,对于 Net不需要。
  [i] = [i]; // , see
  [i] = 1; // item is
  [i] = ; // ,在中会用到
  [i] = 0; // blob size
  [i] = ; // to blob
  [i] = 4; //数据类型
  }
  //初始化输出参数
   = ;
   = ;
  try
  {
  // 添加项到组
   grp=(()[]);
  (()grp)(,,out ,out );
  int[] = new int[];
   pos = ;
  (, , 0,);
  for(int i=0;i
  {
  if ([i] == 0)
  {
   = ()(pos, ());
  [i] = ;
  pos = new (32() + (()));
  }
  else
  {
   ;
  ([0],0x407,out );
  =;
  ;
  }
  }
  (,,); //要求始终只有一个组被激活,才不会引起冲突。
  }
   ( err) // for in
  {
  =;
  //="错误信息:"++;
  }
  {
  // 释放非托管内存
  if( != )
  {
  ();
   = ;
  }
  if( != )
  {
  ();
   = ;
  }
  }
   ;
  }
  说明:使用该函数时,在类的开头,应该先声明整数数据,以用于保存由本函数返回的服务器对每一项分配的Item ID号:
  8、 下面编写的是一个最重要的重载函数,当检测到当前活动组中的某个变量发生变化时,就会调用委托。
  //数据变化时处理的问题
   void ( Int32 ,
  Int32 ,
  Int32 ,
  Int32 ,
  Int32 ,
  int[] ,
  [] ,
  [] ,
  [] ,
  int[] )
  {
  (,);
  }
  该函数的代码只有一句,即调用委托函数。
  以上编写的是需要实现监控的最重要的方法,当然不完善,还有许多方法和重载函数可以编写,这里就不详细介绍。
  9、 编写基本的测试程序,用于检测上面编写的异步类
  <1>、 重新创建一个工程,添加对上面编写的异步类的引用,并在类的开头部分添加变量声明:
  //声明委托
   S7 dt;
  //声明服务器
  S7 ;
  <2>、初始化服务器数据
  dt=new S7();
   =new (S7_,dt);
   err;
  (out err);
  ("",1,300,out err);
  ("",m1,nt1);
  ("1",1,300,out err);
  ("1",m2,nt2);
  nt[0]=nt1[0];nt[1]=nt1[1];
  <3>、添加两个单选按钮,用于选择某个组,并编写相应的程序
   err,err1;
  if(==null) ;
  if(1)
  { nt[0]=nt1[0];nt[1]=nt1[1];
  ("",true,out err);
  ("1",,out err1);
  }
  else
  {
  nt[0]=nt2[0];nt[1]=nt2[1];
  ("1",true,out err);
  ("",,out err1);
  }
  <4>、添加文本框、按钮等,并编写委托执行函数:
   void ([] obj,int[] )
  {
  for(int j=0;j{
  if([j]==nt[0])
  1=obj[j]();
  if([j]==nt[1])
  4=obj[j]();
  }
  }
  其中参数obj用于返回当前发生变化的变量的结果值,而返回当前发生变化的变量的ID号,其与添加变量时服务器返回的ID号对应。以上就是一个基本的测试函数,其相对同步编程来说,应该还简单一些。
  3、 同步编程与异步编程的使用场合
  一般来讲,同步编程需要使用定时器来循环检测变量,而异步编程则不需要,当服务器检测到数据发生变化时,可以直接调用传入的函数,从这方面来讲,使用异步编程更简单一些,但同步编程使用外部的定时器控制,编程则会更加灵活,一般只监控变量时可以使用异步编程,而当需要写入数据时可以使用同步编程,但这也不是绝对的,我曾编写了一个标准监控程序,没有使用异步编程。
  4、 关于开发监控界面的说明
  毫无疑问,我们应该开发一系列控件,用于简化界面的设计,否则工作量会异常大。设计一个标准模块,用于第一次运行监控软件时添加变量,并可以设定当前已经组态的界面中的各控件元素与之关联,这样在以后再运行该软件时,不需要再设定,就可以直接连接变量,并进行相应的变化。否则若在编程时编写代码进行关联,其工作量将会异常大。

    ——本信息真实性未经中国工控网证实,仅供您参考