倍福PLC——ADS上位机通讯
前言
工程中涉及与倍福plc的交互用到ads通讯,在此稍作研究总结。
一、ADS服务
本机没有安装倍福全家桶的需要安装一下这个TwinCAT System。
安装完成后需要配置一下服务中的端口。(具体操作等下次有机会再记录把)
二、使用ads函数进行数据通讯
1.通过句柄读写
先看一下两端的数据配置如下:
PLC端:
结构体定义:
此处顶部的{attribute ‘pack_mode’:= ‘1’}指示了内存排列的方式,与下文c#端设置结构体排列时有着对应的关系,请着重关注一下
上位机C#端:
看一下两边类型对照表:
重点是结构体定义:
很多人在这个地方喜欢把Struct用Class定义,在单独传输结构体数据时是没有问题的,但是当要传输一个结构体数组,用Class定义则会引起代码报错,此处注意!
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct myStruct
{
public void StructIni()
{
b = false;
bt = 0;
word = 0;
dword = 0;
sint = 0;
m_int = 0;
dint = 0;
lint = 0;
real = 0;
lreal = 0;
string_en = "";
intArr = new int[10];
intArr2 = new int[9];
}
[MarshalAs(UnmanagedType.I1)]
public bool b ;
[MarshalAs(UnmanagedType.U1)]
public byte bt ;
[MarshalAs(UnmanagedType.U2)]
public ushort word ;
[MarshalAs(UnmanagedType.U4)]
public uint dword ;
[MarshalAs(UnmanagedType.I1)]
public sbyte sint ;
[MarshalAs(UnmanagedType.I2)]
public short m_int;
[MarshalAs(UnmanagedType.I4)]
public int dint ;
[MarshalAs(UnmanagedType.I8)]
public long lint ;
[MarshalAs(UnmanagedType.R4)]
public float real;
[MarshalAs(UnmanagedType.R8)]
public double lreal ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string string_en; //plc端string长10
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] intArr ;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public int[] intArr2 ; //对应plc端是个二维数组
}
结构体数组:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct myStructArr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public myStruct[] arr ;
}
c#读取写入代码
定义类型与句柄结构方便操作:
public struct PLCPointInfo
{
public PLCType plcType;
public int Handel;
public int byteCount;
}
public enum PLCType
{
BOOL,
BYTE,
WORD,
DWORD,
SINT,
INT,
DINT,
LINT,
USINT,
UINT,
UDINT,
ULINT,
REAL,
LREAL,
STRING,
//STRING_CH, //本来是用网上转UTF8格式的来处理,但是我已发现的bug太多,在此不推荐
WSTRING,
STRUCT
}
首先需要获取对应数据的句柄:
TcAdsClient ads;
private void Form1_Load(object sender, EventArgs e) //连接
{
ads = new TcAdsClient();
ads.Connect("169.254.109.125.1.1", 851);
}
//存储数据结构的字典
Dictionary<string, PLCPointInfo> dic = new Dictionary<string, PLCPointInfo>();
private void button1_Click(object sender, EventArgs e) //获取数据句柄
{
dic.Clear();
foreach (PLCType item in Enum.GetValues(typeof(PLCType)))
{
dic.Add(item.ToString().ToLower(), new PLCPointInfo()
{
plcType = item,
Handel = ads.CreateVariableHandle("MAIN." item.ToString().ToLower() "1")
});
}
dic["string"] = new PLCPointInfo()
{
plcType = PLCType.STRING,
Handel = ads.CreateVariableHandle("MAIN." "string" "1"),
byteCount = 10
};
//dic["string_ch"] = new PLCPointInfo()
//{
// plcType = PLCType.STRING_CH,
// Handel = ads.CreateVariableHandle("MAIN." "string_ch" "1"),
// byteCount = 10
//};
dic["wstring"] = new PLCPointInfo()
{
plcType = PLCType.WSTRING,
Handel = ads.CreateVariableHandle("MAIN." "wstring" "1"),
byteCount = 10
};
}
读取PLC中的数据:
public object GetPLCValue(PLCPointInfo info, Type type = null)
{
switch (info.plcType)
{
case PLCType.BOOL:
return (ads.ReadAny(info.Handel, typeof(bool)));
case PLCType.BYTE:
case PLCType.USINT:
return (ads.ReadAny(info.Handel, typeof(byte)));
case PLCType.WORD:
case PLCType.UINT:
return (ads.ReadAny(info.Handel, typeof(ushort)));
case PLCType.DWORD:
case PLCType.UDINT:
return (ads.ReadAny(info.Handel, typeof(uint)));
case PLCType.SINT:
return (ads.ReadAny(info.Handel, typeof(sbyte)));
case PLCType.INT:
return (ads.ReadAny(info.Handel, typeof(short)));
case PLCType.DINT:
return (ads.ReadAny(info.Handel, typeof(int)));
case PLCType.LINT:
return (ads.ReadAny(info.Handel, typeof(long)));
case PLCType.ULINT:
return (ads.ReadAny(info.Handel, typeof(ulong)));
case PLCType.REAL:
return (ads.ReadAny(info.Handel, typeof(float)));
case PLCType.LREAL:
return (ads.ReadAny(info.Handel, typeof(double)));
case PLCType.STRING:
return (ads.ReadAny(info.Handel, typeof(string), new int[] { info.byteCount == 0 ? 255 : info.byteCount }));
//case PLCType.STRING_CH: //不建议使用UTF8编码传输
// object o = (ads.ReadAny(info.Handel, typeof(string), new int[] { info.byteCount == 0 ? 255 : info.byteCount 10 }));
// byte[] byteArr = Encoding.Default.GetBytes(o.ToString());
// return Encoding.UTF8.GetString(byteArr);
case PLCType.WSTRING: //wstring类型直接这样读取就行,plc端wstring编码是UTF16,所有直接转换成Unicode就行
object o2 = (ads.ReadAny(info.Handel, typeof(string), new int[] { info.byteCount == 0 ? 255 : info.byteCount }));
byte[] byteArr2 = Encoding.Default.GetBytes(o2.ToString());
return Encoding.Unicode.GetString(byteArr2);
case PLCType.STRUCT:
if (type == null)
{
throw new Exception("输入结构体C#端类型为null!");
}
return (ads.ReadAny(info.Handel, type));
default:
return null;
break;
}
}
//数据结果存放的字典
Dictionary<string, object> value = new Dictionary<string, object>();
private void button2_Click(object sender, EventArgs e)
{
value.Clear();
foreach (var item in dic)
{
if (item.Value.plcType == PLCType.STRUCT)
{
value.Add(item.Key, GetPLCValue(item.Value, typeof(myStructArr)));
}
else
{
value.Add(item.Key, GetPLCValue(item.Value));
}
}
}
结果:
数据写入PLC:
public void SetPLCValue(PLCPointInfo info,object value, Type type = null)
{
object input;
switch (info.plcType)
{
case PLCType.BOOL:
input = (bool)value;
ads.WriteAny(info.Handel, input);
break;
case PLCType.BYTE:
case PLCType.USINT:
input = Convert.ToByte(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.WORD:
case PLCType.UINT:
input = Convert.ToUInt16(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.DWORD:
case PLCType.UDINT:
input = Convert.ToUInt32(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.SINT:
input = Convert.ToSByte(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.INT:
input = Convert.ToInt16(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.DINT:
input = Convert.ToInt32(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.LINT:
input = Convert.ToInt64(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.ULINT:
input = Convert.ToUInt64(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.REAL:
input = Convert.ToSingle(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.LREAL:
input = Convert.ToDouble(value);
ads.WriteAny(info.Handel, input);
break;
case PLCType.STRING:
ads.WriteAnyString(info.Handel, value.ToString(), info.byteCount == 0? value.ToString().Length : info.byteCount, Encoding.Default);
break;
//case PLCType.STRING_CH:
//用UTF8以WriteAny发送过去的数据会存在最后一个字符乱码的情况(最后一个编码被改成63空格结束符)用stream流发送,不会乱码,但是在此读取时又会出现奇怪的乱码问题,原因以后有时间再研究一下
//更新一下,在通过这种往内存里写东西的时候,比如string(10)我写入(“嗡嗡嗡”)后再次写入(“哇哇”)则会呈现(“哇哇嗡”)的情况
//也就是写入内存小的,没用到的内存不会被清除,读取的时候就会有问题
//我在写入时新增结束符或把其他内存都付0也不行
//在读取时最后一位莫名其妙变成了63导致最后一个字符乱码,由于找不到倍福数据传输的结构,实在找不到原因了。若以后有新发现,会继续更新
// byte[] byteArr = Encoding.UTF8.GetBytes(value.ToString());
// byte[] newbyteArr;
// newbyteArr = new byte[byteArr.Length 1];
// byteArr.CopyTo(newbyteArr, 0);
// for (int i = byteArr.Length; i < byteArr.Length 1; i )
// {
// newbyteArr[i] = Convert.ToByte('\0');
// }
// //if (byteArr.Length < info.byteCount)
// //{
// // newbyteArr = new byte[info.byteCount];
// // byteArr.CopyTo(newbyteArr, 0);
// // for (int i = byteArr.Length; i < newbyteArr.Length; i )
// // {
// // newbyteArr[i] = Convert.ToByte('\0');
// // }
// //}
// //else if (byteArr.Length > info.byteCount)
// //{
// // throw new Exception("输入数据长度超过设定长度!");
// //}
// //else
// //{
// // newbyteArr = byteArr;
// //}
// //ads.WriteAnyString(info.Handel, Encoding.Default.GetString(newbyteArr), info.byteCount == 0 ? value.ToString().Length : info.byteCount, Encoding.Default);
// using (AdsStream rStream = new AdsStream(newbyteArr.Length))
// {
// rStream.Seek(0, System.IO.SeekOrigin.Begin);
// using (AdsBinaryWriter writer = new AdsBinaryWriter(rStream))
// {
// writer.Write(byteArr);
// ads.Write(info.Handel, rStream, 0, newbyteArr.Length);
// }
// }
// break;
case PLCType.WSTRING: //读取时选定Encoding.Unicode就行(不支持UTF8编码读取)
ads.WriteAnyString(info.Handel, value.ToString(), info.byteCount == 0 ? value.ToString().Length : info.byteCount, Encoding.Unicode);
break;
case PLCType.STRUCT:
if (type == null)
{
throw new Exception("输入结构体C#端类型为null!");
}
ads.WriteAny(info.Handel, value);
break;
default:
input = null;
break;
}
}
private void button3_Click(object sender, EventArgs e) //写入数据
{
foreach (var item in dic)
{
switch (item.Value.plcType)
{
case PLCType.BOOL:
SetPLCValue(item.Value, true);
break;
case PLCType.BYTE:
SetPLCValue(item.Value, 100);
break;
case PLCType.WORD:
SetPLCValue(item.Value, 300);
break;
case PLCType.DWORD:
SetPLCValue(item.Value, 2000);
break;
case PLCType.SINT:
SetPLCValue(item.Value, -100);
break;
case PLCType.INT:
SetPLCValue(item.Value, -2000);
break;
case PLCType.DINT:
SetPLCValue(item.Value, -20000);
break;
case PLCType.LINT:
SetPLCValue(item.Value, -2000000);
break;
case PLCType.USINT:
SetPLCValue(item.Value, 200);
break;
case PLCType.UINT:
SetPLCValue(item.Value, 20000);
break;
case PLCType.UDINT:
SetPLCValue(item.Value, 200000);
break;
case PLCType.ULINT:
SetPLCValue(item.Value, 200000);
break;
case PLCType.REAL:
SetPLCValue(item.Value, 2.23);
break;
case PLCType.LREAL:
SetPLCValue(item.Value, -32.15);
break;
case PLCType.STRING:
SetPLCValue(item.Value, "zxcvb");
break;
//case PLCType.STRING_CH:
// SetPLCValue(item.Value, "我的天哪");
// break;
case PLCType.WSTRING:
SetPLCValue(item.Value, "哇撒");
break;
case PLCType.STRUCT:
myStructArr mstruct = new myStructArr()
{
arr = new myStruct[9]
};
for (int i = 0; i < mstruct.arr.Length; i )
{
mstruct.arr[i].StructIni();
mstruct.arr[i].dint = i 1;
mstruct.arr[i].intArr[0] = i 1;
mstruct.arr[i].string_en = (i 1).ToString();
}
SetPLCValue(item.Value, mstruct,typeof(myStructArr));
break;
default:
break;
}
}
}
写入结果:
结构体数组:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhghfbcf
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13