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

ADS简介

YapethsDY 2020/08/27 PM

  • 基于ADS的twincat系统架构

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

  • ADS通讯协议概述

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组件库的加载不做赘述


分两种方式来进行测试


/******************************************************************************
* 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!

猜你喜欢

转载自blog.csdn.net/Ding86341631/article/details/108259212