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

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通讯_第1张图片

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

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

2.1 ADS通讯方式TwinCAT3应用——与高级语言(C#)ADS通讯_第3张图片

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

2.2 ADS命令参数

TwinCAT3应用——与高级语言(C#)ADS通讯_第4张图片

3. 程序实现

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

step1 建立工程

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

step2 添加引用

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

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通讯_第6张图片
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通讯_第7张图片

5. 完整工程项目文件

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

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

你可能感兴趣的:(TwinCAT3,c#,乱码)