您的位置:首页 > 其它

.net应用程序和TW3的ADS之间通讯

2016-02-02 15:38 567 查看
Beckhoff ADS (Automation DeviceSpecification)提供一个应用程序之间互相通信的接口,在TW3系统中,TwinCAT PLC,TwinCAT NC等被设计成虚拟的自动化设备,类似于实际的物理设备与设备之间通过基于TCP协议的路由来交换信息,参考下面的图示



ADS设备最重要的两个属性就是端口号和AdsAmsNetId

• AdsPortNr 指定通信的虚拟设备(ADS server),比如PLC,NC

• AdsAmsNetId 指定ADS路由器,是TCP IP地址的扩展

既然是基于TCP协议,那对照TCP/IP七层协议如下



接着看下ADS协议/报文的数据结构



在AMS Header中存放着上面提到的AMS NET ID和PORT



还有从PLC Read和Write变量时存放的变量地址和偏移量



ADS 设备之间的通讯有多种方式,不同方式有不同的特点。

1.一般的ADS 通讯(General ADS Services)
- 异步方式(Asynchronous)

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

- 通知方式(Notification)

ADS 客户端向ADS 服务器发送ADS 请求,ADS 服务器以Call-back 函数的方式不断向客户端发送响应,直到客户端取消该请求。PLC变量不更新,就不会向客户端相应,类似于OPC通信的订阅。

这两种通讯方式的效率高,但需求复杂的客户端程序。

优点:不会造成系统堵塞

缺点:不能确保每次请求都有返回

2.特殊的ADS 通讯(Specific ADS Services)

TwinCATADS 设备和Windows应用程序(例如VB、VC 应用程序等)之间的通讯除了可以采用一般的ADS 通讯方式外,还可以采用特殊的通讯方式,即同步通讯方式。

- 同步方式(Synchronous)

ADS 客户端向ADS 服务器发送ADS 请求,在通讯过程中客户端程序停止执行,直到获得ADS 服务器返回的响应。

这种通讯方式不需求复杂的客户端程序,但其轮循的通讯方式给系统带来比较大的负载,因此通讯效率较低。

优点:能即时返回结果

缺点:如果通讯故障会造成系统堵塞

TwinCAT ADS访问变量有两种方式:

1.地址方式

一个PLC变量的地址由两部分组成:GroupIndex和OffsetIndex:

GroupIndex一般用于区别寄存器类型,在TwinCAT ADS设备中为常量,具体内容可以参考Information System(后附常用的GroupIndex值)。

OffsetIndex为变量的偏移地址,在PLC中为该变量的地址。

2.变量名方式

在TwinCAT ADS设备中每个变量都有一个句柄(Handle)。

适用变量名访问变量首先需要得到该变量的句柄。(不同的高级语言方式略有不同)



测试中使用的异步通讯方式,通过变量名方式,代码如下

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using TwinCAT.Ads;

namespace TW3_ADS_test
{
public partial class Form1 : Form
{
private TcAdsClient tcAdsClient;                      //会有变量定义在类外面吗,比如说在CLASS Form1外面定义变量,共几个类用???
private int hander1, hander2,hd1,hd2,hd3,hd4;
private AdsStream iStream;
private AdsStream bStream;
private AdsStream sStream;
private AdsBinaryReader iBinaryReader;
private AdsBinaryReader bBinaryReader;
private AdsBinaryReader sBinaryReader;
private AdsBinaryWriter sBinaryWriter;

private bool[] b;
/// <summary>
/// Form1窗口构造函数,窗口初始化
/// </summary>
public Form1()
{
InitializeComponent();

iStream = new AdsStream(3 * 2);//流数据必须赋值大小,否则报错:无法在流的结尾之外进行读取
bStream = new AdsStream(3);
sStream = new AdsStream(7);
iBinaryReader = new AdsBinaryReader(iStream);
bBinaryReader = new AdsBinaryReader(bStream);
sBinaryReader = new AdsBinaryReader(sStream);
sBinaryWriter = new AdsBinaryWriter(sStream);
try
{
//// Connect to local PLC - Runtime 1 - TwinCAT2 Port=801, TwinCAT3 Port=851
tcAdsClient = new TcAdsClient();
tcAdsClient.Connect(851);
hander1 = tcAdsClient.CreateVariableHandle("MAIN.ivar");
hander2 = tcAdsClient.CreateVariableHandle("MAIN.bvar");
hd1 = tcAdsClient.CreateVariableHandle("MAIN.bvar[0]");
hd2 = tcAdsClient.CreateVariableHandle("MAIN.bvar[1]");
hd3 = tcAdsClient.CreateVariableHandle("MAIN.bvar[2]");
hd4 = tcAdsClient.CreateVariableHandle("MAIN.svar");

}
catch(Exception ex)
{
MessageBox.Show("ADS设备连接失败,请确认变量名和PLC运行状态:"+ex.Message );
}

}
/// <summary>
/// 圆形区域重绘
/// </summary>
private void lightstate()
{
Graphics g;
SolidBrush brush = new SolidBrush(Color.Green);
SolidBrush redbrush = new SolidBrush(Color .Red );
g = CreateGraphics();

if (b[0])
g.FillEllipse(brush, 80, 80, 40, 40);
else
g.FillEllipse(redbrush ,80,80,40,40);
if (b[1])
g.FillEllipse(brush, 220, 80, 40, 40);
else
g.FillEllipse(redbrush, 220, 80, 40, 40);
if (b[2])
g.FillEllipse(brush, 350, 80, 40, 40);
else
g.FillEllipse(redbrush, 350, 80, 40, 40);
}
/// <summary>
/// 初始化状态圆形区域
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Paint(object sender, EventArgs e)
{
Graphics g;
SolidBrush brush = new SolidBrush(Color.Red);
//Pen pen = new Pen( Color.Red,1); //画空心圆
g = CreateGraphics();
g.FillEllipse(brush,80,80,40,40);
g.FillEllipse(brush, 220, 80, 40, 40);
g.FillEllipse(brush, 350, 80, 40, 40);
//g.DrawEllipse (pen,400,80,40,40);
}
/// <summary>
/// 窗口Load事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
this .Paint +=new PaintEventHandler(Form1_Paint);//不是在VS里双击过来的事件,可以手动添加委托
timer1.Interval = 100;
button4.Enabled = true;
button5.Enabled = false;
listBox1.Items.Clear();
}

private void timer1_Tick(object sender, EventArgs e)
{
b = new bool[3];
try
{
tcAdsClient.Read(hander1, iStream);
tcAdsClient.Read(hander2, bStream);
tcAdsClient.Read( hd4 ,sStream );
iStream.Position = 0;                                               //否则报错:无法在流的结尾之外进行读取
textBox1.Text = iBinaryReader.ReadInt16().ToString();
textBox2.Text = iBinaryReader.ReadInt16().ToString();
textBox3.Text = iBinaryReader.ReadInt16().ToString();
bStream.Position = 0;
b[0] = bBinaryReader.ReadBoolean();
b[1] = bBinaryReader.ReadBoolean();
b[2] = bBinaryReader.ReadBoolean();
sStream.Position = 0;

//TwinCAT.Ads.NET version >= 1.0.0.10: new method
//listBox1.Items.Add( sBinaryReader .ReadPlcString(4) );

//TwinCAT.Ads.NET version < 1.0.0.10:old method
listBox1.Items.Add(tcAdsClient .ReadAny ( hd4 ,typeof ( string ),new int []{7}));
}
catch( Exception ex )
{
MessageBox.Show("读PLC数据错误"+ex.Message );
}
lightstate();
}
/// <summary>
/// 开始读数据从PLC
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
sBinaryWriter.WritePlcString( textBox1 .Text ,7 );
tcAdsClient.Write( hd4 ,sStream );
timer1.Enabled = true;
button4.Enabled = false;
button5.Enabled = true;
}
/// <summary>
/// 停止和PLC的ADS通信
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
button4.Enabled = true;
button5.Enabled = false;
}

private void button1_Click(object sender, EventArgs e)
{
if (b[0])
b[0] = false;
else
b[0] = true;
}

private void button2_Click(object sender, EventArgs e)
{

if (b[1])
b[1] = false;
else
b[1] = true;
tcAdsClient.WriteAny( hd2 ,b[1] );
}

private void button3_Click(object sender, EventArgs e)
{

if (b[2])
b[2] = false;
else
b[2] = true;
}

}
}


PLC代码:

PROGRAM MAIN

VAR
ivar:ARRAY[0..2]OF INT;
bvar:ARRAY[0..2]OF BOOL;
index:INT;
svar:STRING:='test';
END_VAR
FOR index:=0 TO 2 DO
ivar[index]:=500+index;
END_FOR
bvar[0]:=TRUE;
bvar[2]:=TRUE;


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: