• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

倍福PLC——ADS上位机通讯

武飞扬头像
我叫DP
帮助1


前言

工程中涉及与倍福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
系列文章
更多 icon
同类精品
更多 icon
继续加载