倍福PLC——ADS上位机通讯

  • 前言
  • 一、ADS服务
  • 二、使用ads函数进行数据通讯
    • 1.通过句柄读写
      • c#读取写入代码

前言

工程中涉及与倍福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;}}}

写入结果:

结构体数组:

倍福PLC——ADS上位机通讯相关推荐

  1. 上位机使用C++通过ADS协议与倍福PLC通信例程-字符串变量读取

    前言 建议初学者先看这一章节内容,里面包括一些基础的环境配置和项目建立流程,以后开发项目这些流程是通用的,务必掌握并熟练. 链接: 上位机使用C++通过ADS协议与倍福PLC通信例程-布尔变量的读取 ...

  2. 倍福PLC的C++ ADS通讯定义数据类型时注意事项

    在C++程序与倍福PLC通过ADS通讯时,如果C++程序中定义的变量与PLC程序中相应变量定义的数据类型不对应时,可能会出现数据读取或者写入错误,以下为调试过程中容易出错的实践总结记录. 1.向PLC ...

  3. C# TCP/IP通讯协议的整理(三)附带——与倍福PLC通讯

    首先,需要一个和倍福PLC通讯的dll,一般厂家会提供 添加到引用后,直接创建通讯类 using System; using System.Collections.Generic; using Sys ...

  4. 倍福ads通讯软件_倍福TwinCAT ADS通讯-高级语言.ppt

    倍福TwinCAT ADS通讯-高级语言 * ADS组件库文件组成简介 ADS组件库集成在TwinCAT软件中,安装任何版本的TwinCAT软件都包含ADS通讯组件,如果用户希望在没有安装TwinCA ...

  5. 倍福PLC和C#通过ADS通信传输Bool数组变量

    在倍福PLC和C#通信,采用ADS通信,本文讲解C#如何读取和写入bool类型数组变量. 操作流程 1.1. PLC程序设计 首先定义相关的变量,如下所示,激活配置: 1.2. C#程序设计 关于C# ...

  6. 倍福PLC和C#通过ADS通信传输int类型变量

    在倍福PLC和C#通信,采用ADS通信,本文讲解C#如何读取和写入int类型变量. 操作流程 1.1. PLC程序设计 首先定义相关的变量,如下所示,激活配置: 1.2. C#程序设计 关于C#和倍福 ...

  7. 倍福PLC和C#通过ADS通信传输bool类型变量

    在倍福PLC和C#通信,采用ADS通信,本文讲解C#如何读取和写入bool类型变量. 操作流程 1.1. PLC程序设计 首先定义相关的变量,如下所示,激活配置: 1.2. C#程序设计 关于C#和倍 ...

  8. LabVIEW TCP网口通讯倍福 BeckhoffPLC ADS 通讯协议

    LabVIEW TCP网口通讯倍福 BeckhoffPLC ADS 通讯协议. 常用功能一网打尽. 1.命令帧读写. 2.支持 I16 I32 Float 批量读写. 3.支持字符串读写. 4.支持B ...

  9. 欧姆龙PLC码垛程序 电机:四个雷塞闭环步进电机,四个汇川伺服电机,总共八个电机。 PLC:CP1H-EX40DT-D,八个轴就用了两个PLC,还有跟上位机通讯

    欧姆龙PLC码垛程序(某上市公司设备),电机:四个雷塞闭环步进电机,四个汇川伺服电机,总共八个电机. PLC:CP1H-EX40DT-D,八个轴就用了两个PLC,还有跟上位机通讯. 图四是机台俯视图 ...

  10. C# 机器视觉工控通讯------西门子PLC之S7协议上位机通讯

    C# 机器视觉工控通讯------西门子PLC之S7协议上位机通讯 使用步骤 1.引入库 项目添加应用HslCommunication.dll和代码代码如下(示例): dll官方支持网站> us ...

最新文章

  1. 【算法学习】堆排序建立最大堆
  2. linux下配置apache多站点访问-小案例
  3. 【HAOI2010】订货
  4. 关于在Webservice里使用LinqToSQL遇到一对多关系的父子表中子表需要ToList输出泛型而产生循环引用错误的解决办法!(转)...
  5. ansible1.7.2源码安装教程
  6. [CODEVS 1087] 麦森数
  7. 关于谷歌地图无法获取到WebGL上下文问题
  8. jhipster 配置 mysql_java – 将jhipster后端和前端分成两个项目?
  9. 无人驾驶汽车之争本田为何未战先败
  10. views视图函数-模板语法
  11. 直播预告丨Oracle DataGuard 备份恢复最佳实践
  12. spark基础之spark streaming的checkpoint机制
  13. 美研计算机案例,美国研究生申请案例:耶鲁大学录取:计算机硕士【2010】
  14. 概率论与数理统计(一)—— 联合概率、条件概率与边缘概率
  15. 记录一次项目中代码大致优化方向
  16. 计算机房网络布线培训方案,网络工程综合布线实训授课计划.doc
  17. 给大家讲解一下 AIDL原理分析
  18. MIMIC数据库数据提取教程-提取时间维度数据
  19. Python PDF转高清图片 可设置转前几张
  20. 【全文翻译】Membership Inference Attacks Against Machine Learning Models

热门文章

  1. java安卓读取txt中字符串分割为map
  2. Vue3 Echarts散点图+高德地图+卫星地图(二)——Echarts配置散点图高德卫星地图版
  3. 副高 职称计算机 上海,高级职称评定
  4. Windows图片和传真查看器开启故障
  5. paper的经验和会议排名
  6. 解决tooltips鬼畜问题
  7. EKF扩展卡尔曼滤波算法做电池SOC估计,在Simulink环境下对电池进行建模
  8. 深入浅出DockerPDF
  9. Windows 10 下生成 ssh 密钥
  10. 设置虚拟机dns服务器域名,域名服务器DNS的设置实验