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

TwinCAT3应用——与高级语言(C#)ADS通讯

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

TC3与C#ADS通讯进行数据读写

本文章旨在说明TwinCAT3的ADS通讯应用,利用C#对倍福中常见数据类型(INT、BOOL、STRING、WSTRING、ARRAY、STRUCT等)变量进行读写操作。

1. TwinCAT ADS技术

(备注:此部分大家可以前往倍福虚拟学堂进行学习: https://tr.beckhoff.com.cn/course/view.php?id=150
TwinCAT3应用——与高级语言(C#)ADS通讯

  • ADS即(Automation Device Specification)自动化设备规范;
  • TwinCAT系统各模块均作为独立的设备;
  • 每个任务均存在一个服务模块,服务端或客户端;
  • 由Message Router统一交换数据。
  • TwinCAT3应用——与高级语言(C#)ADS通讯

2. ADS通讯方式及命令参数

2.1 ADS通讯方式TwinCAT3应用——与高级语言(C#)ADS通讯

  • 异步通讯变量上限:550
  • 批量读取变量上限:500
  • 异步和批量读取需要通道支持

2.2 ADS命令参数

TwinCAT3应用——与高级语言(C#)ADS通讯

3. 程序实现

本例中采用按变量名方式进行读写调试。其具体实现方法如下:

step1 建立工程

建立c#的项目工程、Twincat3 项目工程,在c#中实现对Twincat3 中的变量进行读写。

step2 添加引用

在C#工程中引入 TwinCAT.Ads.dll 库文件(此文件可到BECKHOFF官网下载)
TwinCAT3应用——与高级语言(C#)ADS通讯

step3 前期工作

1)TC3变量创建

TYPE StructAxis :
STRUCT
 Triggle:         BOOL:=TRUE;
 Velocity:        LREAL:=200;
 //Acceleration:    LREAL;
 //Jrek:            LREAL;
 //Deceleration:    LREAL;
 //Done:            BOOL;
 //Busy:            BOOL;
 //Error:           BOOL;
 //ErrorID:         LREAL;
END_STRUCT
END_TYPE

PROGRAM MAIN
VAR
 //c# test
 Date_Int:         INT:=10;          
 Date_Bool:        BOOL:=FALSE;
 Date_Array:       ARRAY[0..2] OF INT;
 Date_Struct:      StructAxis;
 Date_string:      STRING:='Made by dashuaixiaoping';
 {attribute 'TcEncoding':='UTF-8'}
 Date_wstring:     STRING:=wsLiteral_TO_UTF8("大帅小平制作"); 
END_VAR

2)设计Form
本例添加以下控件进行调试:(比较粗糙,请忽略)
TwinCAT3应用——与高级语言(C#)ADS通讯
3)创建Adsclient

public TcAdsClient _client = new TcAdsClient();

4)连接PLC

_client.Connect(851);

5)创建句柄

 public int _myIntHand = 0;
 public int _myBoolHand = 0;
 public int _myStringHand = 0;
 public int _myWstringHand = 0;
 public int _myArrayHand = 0;
 public int _myStructHand = 0;

 _myIntHand = _client.CreateVariableHandle("MAIN.Date_Int");
 _myBoolHand = _client.CreateVariableHandle("MAIN.Date_Bool");
 _myStringHand = _client.CreateVariableHandle("Main.Date_string");
 _myWstringHand = _client.CreateVariableHandle("Main.Date_wstring");
 _myArrayHand = _client.CreateVariableHandle("Main.Date_Array");
 _myStructHand = _client.CreateVariableHandle("Main.Date_Struct");

6)释放句柄

 //释放句柄
 _client.DeleteVariableHandle(_myIntHand);
 _client.DeleteVariableHandle(_myBoolHand);
_client.DeleteVariableHandle(_myStringHand);
_client.DeleteVariableHandle(_myWstringHand);
 _client.DeleteVariableHandle(_myArrayHand);
_client.DeleteVariableHandle(_myStructHand);

step4 开始读写

TwinCAT.Ads.dll 库文件中提供了多个重载读写方法,本例中主要运用演示了:

public object ReadAny(int variableHandle, Type type, int[] args);
public int Read(int variableHandle, AdsStream dataStream, int offset, int length);
public void WriteAny(int variableHandle, object value, int[] args);
public void Write(int variableHandle, AdsStream dataStream, int offset, int length);

1)ReadAny/WriteAny 实现

//读
//INT
rInttb.Text = _client.ReadAny(_myIntHand, typeof(short)).ToString();
//BOOL
rBooltb.Text = _client.ReadAny(_myBoolHand, typeof(bool)).ToString();
//STRING
rStrtb.Text = _client.ReadAny(_myStringHand, typeof(string), new int[] { 16 }).ToString();
//WSTRING
Encoding defaultEncoding = Encoding.Default;           //当前系统默认用"BGK"进行解码与编码
Encoding utf8Encoding = Encoding.UTF8;
string temStr = _client.ReadAny(_myWstringHand, typeof(string), new int[] { 32 }).ToString();
byte[] tempBytes = defaultEncoding.GetBytes(temStr);    //解码:将字符、符号等转换为二进制机器语言
string str = utf8Encoding.GetString(tempBytes);         //编码:将二进制机器语言转换为字符、符号等
rWstrtb.Text = str;
//ARRAY
short[] tempArr = (short[])_client.ReadAny(_myArrayHand, typeof(short[]), new int[] { 4 });
rArr0tb.Text = tempArr[0].ToString();
rArr1tb.Text = tempArr[1].ToString();
rArr2tb.Text = tempArr[2].ToString();
//STRUCT
 Mystruct struct1 = (Mystruct)_client.ReadAny(_myStructHand, typeof(Mystruct));
rStruct1tb.Text = struct1.Triggle.ToString();
rStruct2tb.Text = struct1.Velocity.ToString();

//写
//INT
_client.WriteAny(_myIntHand, Convert.ToInt16(wInttb.Text));
//BOOL
_client.WriteAny(_myBoolHand, Convert.ToBoolean(wBooltb.Text));
//STRING
_client.WriteAny(_myStringHand, wStrtb.Text, new int[] { 16 });
//WSTRING
string str = wWstrtb.Text;
byte[] tempBytes = Encoding.UTF8.GetBytes(str);
_client.WriteAny(_myWstringHand,tempBytes);
//ARRAY
short[] temArr = new short[3];
temArr[0] = short.Parse(wArr0tb.Text);
temArr[1] = short.Parse(wArr1tb.Text);
temArr[2] = short.Parse(wArr2tb.Text);
_client.WriteAny(_myArrayHand, temArr);
//STRUCT
Mystruct tempStruct = new Mystruct();
tempStruct.Triggle = Boolean.Parse(wStruct1tb.Text);
tempStruct.Velocity = double.Parse(wStruct2tb.Text);
_client.WriteAny(_myStructHand, tempStruct);

2)Read/Write 实现
这里用到stream类,其具体含义与用法可参考博文:C# 温故而知新:Stream篇(—)

//读
AdsStream rStream = new AdsStream(100);
AdsBinaryReader reader = new AdsBinaryReader(rStream);
//INT
 _client.Read(_myIntHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);  //初始化流的当前位置( 等效于stream.Position = 0;)
rInttb.Text = reader.ReadInt16().ToString(); //读取两位数,同时position后移两位(ReadInt16的作用)
//BOOL
_client.Read(_myBoolHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
rBooltb.Text = reader.ReadBoolean().ToString();
//ARRAY
_client.Read(_myArrayHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
rArr0tb.Text = reader.ReadInt16().ToString();
rArr1tb.Text = reader.ReadInt16().ToString();
rArr2tb.Text = reader.ReadInt16().ToString();
//STRUCT
_client.Read(_myStructHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
rStruct1tb.Text = reader.ReadBoolean().ToString();
//因为struct中每个成员的数据类型不一样,
//系统默认将读到的每个成员以成员中长度最长的位数存储在stream中
//如此处的结构体中成员长度最长的数据类型为Double(对于倍福中的LREAL),占8位
//所以需将stream向后移动8位
rStream.Seek(8, System.IO.SeekOrigin.Begin);
rStruct2tb.Text = reader.ReadDouble().ToString();
//STRING
_client.Read(_myStringHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
rStrtb.Text = reader.ReadPlcString(81, Encoding.Default);
 //WSTRING
_client.Read(_myWstringHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
//在倍福那边将WSTRING定义成UTF8格式的STRING,C#中以byte[]形式读取,然后以UTF8编码
rWstrtb.Text = Encoding.UTF8.GetString(reader.ReadBytes(81));

//写
AdsStream wStream = new AdsStream(100);
AdsBinaryWriter writer = new AdsBinaryWriter(wStream);
//INT
 wStream.Seek(0, System.IO.SeekOrigin.Begin);
writer.Write(short.Parse(wInttb.Text));
_client.Write(_myIntHand, wStream, 0, 2);
//BOOL
wStream.Seek(0, System.IO.SeekOrigin.Begin);
writer.Write(Boolean.Parse(wBooltb.Text));
 _client.Write(_myBoolHand, wStream, 0, 1);
//ARRAY
wStream.Seek(0, System.IO.SeekOrigin.Begin);
writer.Write(Int16.Parse(wArr0tb.Text));
writer.Write(Int16.Parse(wArr1tb.Text));
writer.Write(Int16.Parse(wArr2tb.Text));
_client.Write(_myArrayHand, wStream, 0, 6);
//STRUCT
wStream.Seek(0, System.IO.SeekOrigin.Begin);
writer.Write(Boolean.Parse(wStruct1tb.Text));
byte[] tempBytes = new byte[] { 0, 0, 0, 0, 0, 0, 0 };  //占位,使得BOOL和Double都占位8个
writer.Write(tempBytes);
writer.Write(Double.Parse(wStruct2tb.Text));
_client.Write(_myStructHand, wStream, 0, 16);
//STRING
wStream.Seek(0, System.IO.SeekOrigin.Begin);
byte[] tempBytes1 = Encoding.Default.GetBytes(wStrtb.Text);
writer.Write(tempBytes1);
_client.Write(_myStringHand, wStream, 0, 81);
//WSTRING
wStream.Seek(0, System.IO.SeekOrigin.Begin);
byte[] tempBytes2 = Encoding.UTF8.GetBytes(wWstrtb.Text);
writer.Write(tempBytes2);
_client.Write(_myWstringHand, wStream, 0, 81);

4. 注意事项

  • WSTRING的读写
    WSTRING是TC3中的变量,主要用于存放中文字符,其详细介绍请参考博文:
    BECKHOFF TwinCAT3 常见问题2

    值得注意的是,如果在C#中直接将WSTING通过string字符串的形式进行读写,会存在乱码问题,此问题的具体解决方案如下:

    step1:
    在TC3中将带中文的字符的变量命名为UTF-8编码规则下的STRING型;如:

     {attribute 'TcEncoding':='UTF-8'}
     Date_wstring:     STRING:=wsLiteral_TO_UTF8("大帅小平制作");

{attribute ‘TcEncoding’:=‘UTF-8’} 的用法参照:https://infosys.beckhoff.com/english.php?content=…/content/1033/tc3_plc_intro/5873680907.html&id=6249601214176997827
如需深入了解Unicode/UTF-8/UTF-16等解码编码问题可参考博文:
从字节理解Unicode(UTF8/UTF16)

step2:
在C#中接收string,然后用UTF-8进行编码,如下:

//Write方法
AdsStream rStream = new AdsStream(100);
AdsBinaryReader reader = new AdsBinaryReader(rStream);
_client.Read(_myWstringHand, rStream);
rStream.Seek(0, System.IO.SeekOrigin.Begin);
//在倍福那边将WSTRING定义成UTF8格式的STRING,C#中以byte[]形式读取,然后以UTF8编码
rWstrtb.Text = Encoding.UTF8.GetString(reader.ReadBytes(81));

关于此处,本人也尝试用WriteAny进行实现,但是总会遇到编码解码不完全,或者写入TC3时覆盖不完全等问题,所以弃用了WriteAny方法,如有读者利用WriteAny测试成功,还请留言指点(抱拳)。

  • TC3与C#变量类型对应关系
    TwinCAT3应用——与高级语言(C#)ADS通讯

5. 完整工程项目文件

链接:https://pan.baidu.com/s/12ly_fzMBRk7I5DQUUQTV6Q
提取码:aihx

后记:笔者才疏学浅,如有错误,望读者指正。