PLC 结构体定义
TYPE S_Cmd :
STRUCT
done: BOOL;
iAttr: INT;
END_STRUCT
END_TYPE
PLC 变量声明
lrTest: LREAL:=1.3;
iTest: INT:=2;
sTestId: STRING:= 'hello';
sCmd: S_Cmd;
arrTests: ARRAY[1..32] OF INT:=[1,2,3,4,28(3)];
fbTrigITests: TON;
fbTrigLrTests: TON;
PLC 任务主程序(为了触发Notification,这里做了些定时自增操作)
fbTrigITests(IN:= NOT fbTrigITests.Q, PT:= T#1S, Q=> , ET=> );
IF fbTrigITests.Q THEN
iTest := iTest + 1;
END_IF
fbTrigLrTests(IN:= NOT fbTrigLrTests.Q, PT:= T#3S, Q=> , ET=> );
IF fbTrigLrTests.Q THEN
lrTest := lrTest+ 1;
END_IF
上位机C#主程序
using System;
using System.Collections;
using System.IO;
using TwinCAT.Ads;
namespace TestA
{
class Program
{
static ArrayList notifyHdls = new ArrayList();
static AdsStream dataStream = new AdsStream(16);
static BinaryReader binRead = new BinaryReader(dataStream, System.Text.Encoding.ASCII);
static void Main(string[] args)
{
TcAdsClient client = new TcAdsClient();
client.Connect("192.168.0.112.1.1", 851); //建立ADS通信连接
string varName = "MAIN.iTest";
string varNameLr = "MAIN.lrTest";
//根据变量名读取
var val = (Int16)client.ReadSymbol(varName, typeof(Int16), true);
Console.WriteLine($"MAIN.iTest: {val}");
//根据变量句柄读取
var varHdl = client.CreateVariableHandle(varName);
val = (Int16)client.ReadAny(varHdl, typeof(Int16));
var varInfo = client.ReadSymbolInfo(varName); //获取变量信息
Console.WriteLine($"group: {varInfo.IndexGroup},offset: {varInfo.IndexOffset}");
//根据变量Group+Offset通过流读取
var adsStream = new AdsStream(sizeof(Int16));
client.Read((UInt32)varInfo.IndexGroup, (UInt32)varInfo.IndexOffset, adsStream);
BinaryReader binReader = new BinaryReader(adsStream);
Console.WriteLine($"-MAIN.iTest: {binReader.ReadInt16()}");
//根据变量名读取字符串
string sVal = client.ReadSymbol("MAIN.sTestId", typeof(string), true).ToString();
Console.WriteLine($"MAIN.sTestId:{sVal}");
//根据变量名读取数组
Int16[] arrTests = (Int16[])client.ReadSymbol("MAIN.arrTests", typeof(Int16[]), true);
Console.WriteLine($"MAIN.arrTests:{string.Join("-", arrTests)}");
//根据变量名读取结构体
Cmd c = (Cmd)client.ReadSymbol("MAIN.sCmd", typeof(Cmd), true);
Console.WriteLine($"MAIN.sCmd: {c.done},{c.iLastCaller},{c.udExeCnt}");
//根据变量名写入
client.WriteSymbol(varName, 100, true);
//根据变量句柄写入
client.WriteAny(varHdl, (Int16)200);
//根据句柄通过流写入
var adsStreamW = new AdsStream(sizeof(Int16));
BinaryWriter binWriter = new BinaryWriter(adsStreamW);
binWriter.Write((Int16)3);
client.Write(varHdl, adsStreamW);
//根据变量名写入字符串
client.WriteSymbol("MAIN.sTestId", "ok", true);
//根据变量名写入数组,会覆盖整个数组
Int16[] arrTestsW = new Int16[] { 9, 8, 7 };
client.WriteSymbol("MAIN.arrTests", arrTestsW, true);
//根据变量名写入结构体
Cmd cw = new Cmd();
cw.done = true;
cw.iLastCaller = 100;
client.WriteSymbol("MAIN.sCmd", cw, true);
//事件驱动读取
notifyHdls.Add(client.AddDeviceNotification(varName, dataStream, AdsTransMode.OnChange, 500, 0, varName));
notifyHdls.Add(client.AddDeviceNotification(varNameLr, dataStream, AdsTransMode.OnChange, 500, 0, varNameLr));
client.AdsNotification += new AdsNotificationEventHandler(AdsNotificationHandler);
//多线程版本
//notifyHdls.Add(client.AddDeviceNotificationEx(varName, AdsTransMode.OnChange, 500, 0, varName, typeof(Int16)));
//notifyHdls.Add(client.AddDeviceNotificationEx(varNameLr, AdsTransMode.OnChange, 500, 0, varNameLr, typeof(Double)));
//client.AdsNotificationEx += new AdsNotificationExEventHandler(AdsNotificationExHandler);
Console.ReadLine();
foreach(int hdl in notifyHdls)
{
client.DeleteDeviceNotification(hdl);
}
client.DeleteVariableHandle(varHdl);
notifyHdls.Clear();
client.Close();
}
static void AdsNotificationHandler(object sender, AdsNotificationEventArgs e)
{
DateTime time = DateTime.FromFileTime(e.TimeStamp);
e.DataStream.Position = e.Offset;
if (e.NotificationHandle == (int)notifyHdls[0])
Console.WriteLine(binRead.ReadInt16().ToString() + e.UserData);
else if (e.NotificationHandle == (int)notifyHdls[1])
Console.WriteLine(binRead.ReadDouble().ToString() + e.UserData);
else
;
}
static void AdsNotificationExHandler(object sender, AdsNotificationExEventArgs e)
{
string userData = (string)e.UserData;
Type type = e.Value.GetType();
Console.WriteLine($"TransId: {e.UserData}, {e.Value}");
switch (userData) {
case "MAIN.iTest":
//todo
break;
case "MAIN.lrTest":
//todo
break;
default:
break;
}
}
}
}
对应PLC中S_Cmd,上位机中需要定义如下结构体:
namespace TestA
{
public struct Cmd
{
public bool done;
public short iAttr;
}
}