欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

倍福控制器(Beckhoff Twincat 3)的ADS通讯相关知识及测试

程序员文章站 2022-06-09 15:08:55
...

ADS简介

YapethsDY 2020/08/27 PM

  • 基于ADS的twincat系统架构

倍福控制器(Beckhoff Twincat 3)的ADS通讯相关知识及测试

在Beckhoff TwinCAT 系统中,各个软件模块(如TwinCAT PLC、TwinCAT NC、Windows 应用程序等)的工作模式类似于硬件设备,它们能够独立工作,各个软件模块之间的信息交换通过TwinCAT ADS 而完成。因此各个ADS 设备之间都能够交换数据和信息。(获取与官方ppt)

  • ADS通讯协议概述

ADS通讯协议位于网络通讯协议的应用层

倍福控制器(Beckhoff Twincat 3)的ADS通讯相关知识及测试

  • ADS通讯协议的设备标识

每台ADS设备都有各自不同的AdsAmsNetID和AdsPort端口号,也是其最重要的两个属性。

•  AdsPort  指定通信的虚拟设备(ADS server),各不相同且固定不变,而ADS客户端应用程序的port则是可变的。创建plc程序的时候默认端口为851。
•  AdsAmsNetId  指定ADS路由器,是TCP IP地址的扩展。一台PC的IP为“192.168.10.10”时,AdsAmsNetId就是“192.168.10.10.1.1”。

  • ADS通讯方式

  1. 异步方式(Asynchronous)

ADS 客户端向ADS 服务器发送ADS 请求,同时客户端继续自己的工作。ADS 服务器处理请求后,把响应以Call-back 函数方式发给客户端。

      2.通知方式(Notification)

ADS 客户端向ADS 服务器发送ADS 请求,ADS 服务器以Call-back 函数的方式不断向客户端发送响应,直到客户端取消该请求。
这两种通讯方式的效率高,但需求复杂的客户端程序。
优点:不会造成系统堵塞。
缺点:不能确保每次请求都有返回。

       3.同步方式(Synchronous)

ADS 客户端向ADS 服务器发送ADS 请求,在通讯过程中客户端程序停止执行,直到获得ADS 服务器返回的响应。
这种通讯方式不需求复杂的客户端程序,但其轮循的通讯方式给系统带来比较大的负载,因此通讯效率较低。
优点:能即时返回结果。
缺点:如果通讯故障会造成系统堵塞。

  • ADS访问变量的方式

1.地址方式

一个PLC变量的地址由两部分组成:GroupIndex和OffsetIndex,前者一般区别与寄存器类型,一般为常量。后者为变量的偏移地址,在PLC中为该变量的地址。

2.变量名方式

在TwinCAT ADS设备中每个变量都有一个句柄(Handle),适用变量名访问变量首先需要得到该变量的句柄。


测试环境:

  • Twincat 3开发平台
  • windows 7 环境
  • VS 2015 c#开发IDE

首先明确下.net环境下的调用流程,ADS组件库的加载不做赘述

倍福控制器(Beckhoff Twincat 3)的ADS通讯相关知识及测试


分两种方式来进行测试


 

 

/******************************************************************************
* ProjectName:  AdsProject
* Description:  ADS通讯类,主要完成以下功能
*               1.与ADS设备连接,断开
*               2.同步通讯(读/写变量)的方法
* ClassName:    ADSInteractionHMI
* CLRVersion:   .NET Framework 4.5及以上
* Author:       YapethsDY
* NameSpace:    ADSInteractionHMI
* MachineName:  ThinkPad T460
* CreateTime:   2020/08/27 晚
* UpdatedTime:  Null
* others:       未来待实现通过订阅方式获取变量值变更的方法
*               那样看起来不需要浪费多余的资源
* Others        
* Copyright(C)  All rights reserved
*******************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using TwinCAT.Ads;

namespace ADSInteractionHMI
{
    class ADSOperation
    {
        private TcAdsClient ADSClient = new TcAdsClient() ;

        //ADS通讯打开
        public int ADSOpen(string[] adsTagsNameArray)
        {
            int iRet = -1;
            int Index = 0;
            ADSClient = new TcAdsClient();
            try
            {
                ADSClient.Connect(ADS.AMSNetId, ADS.AMSNetPort);
                for (Index = 0; Index < ADS.ADSTagsCount; Index++)
                {
                    MessageBox.Show("句柄创建成功");
                    //创建变量存储指定的句柄
                    ADS.adsHandleArray[Index] = 
                    ADSClient.CreateVariableHandle(adsTagsNameArray[Index]);
                }
                iRet = 0;
            }
            catch (System.Exception ex)
            {
                //异常日志记录
                LogHelper.ErrorLog(null, ex);
                iRet = -1;
            }
            return iRet;
        }
        //ADS读取
        public int ADSRead(int Handle, ref object Value, Type type)
        {
            object tmp = 0;
            int iRet = -1;
            try
            {
                tmp = ADSClient.ReadAny(Handle, type);
                iRet = 0;
            }
            catch (System.Exception ex)
            {
                iRet = -1;
                Debug.WriteLine(ex.Message);
                Debug.WriteLine("ADSClient.ReadAny异常");      
                MessageBox.Show(ex.Message);
            }
            if (0 == iRet)
            {
                Value = tmp;
            }
            return iRet;
        }
        //ADS写入
        public int ADSWrite(int Handle, object Value)
        {
            int iRet = -1;
            try
            {
                ADSClient.WriteAny(Handle, Value);
                iRet = 0;
            }
            catch (System.Exception ex)
            {
                iRet = -1;
                Debug.WriteLine(ex.Message);
                Debug.WriteLine("ADSClient.WriteAny异常");
                MessageBox.Show(ex.Message);
            }
            return iRet;
        }
        //ADS关闭
        public int ADSClose()
        {
            int Index = 0;
            try
            {
                //删除创建的句柄
                for (Index = 0; Index < ADS.ADSTagsCount; Index++)
                {
                    ADSClient.DeleteVariableHandle(ADS.adsHandleArray[Index]);
                }
            }
            catch (System.Exception ex)
            {
                //异常日志记录
                LogHelper.ErrorLog(null, ex);
            }
            finally
            {
                //资源释放
               ADSClient.Dispose();
            }
            return 0;
        }
    }
}

//**********************************************************************************
class ADS{
      public static bool ADSok = false;     //ADS连接/断开状态
      public const string AMSNetId = "192.168.10.10.1.1"; //本地AMSNetID 
      public const int AMSNetPort = 851;     //建的PLC工程默认端口号
      public const int ADSTagsCount = 10;   //相关联的变量总数
      public static ADSOperation ADSOperation = new ADSOperation();//ADS通讯客户端对象明
      public static ADSCommunication ADSCommunication = new ADSCommunication();//ADS通讯
      public static string[] adsTagsNameArray = new string[ADSTagsCount];//ADS变量的名称
      public static int[] adsHandleArray = new int[ADSTagsCount]//所需读取的ADS变量句柄数组
      public static LogRecord tcLogRecord = new LogRecord();//日志记录对象声明  
}                                                                  
//后续还需要初始化变量名与句柄的对应关系  比较冗余就不写了

这种写法虽然说简单,但资源浪费严重,需要在主线程或着界面刷新线程中添加定时器,有的不需要在很短周期内读取的变量也变得优先级很高,显得很浪费,明天会增加事件触发或者json注册的方式来读取,当然了还是依靠变量名来操作。

happy ending!