倍福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;
                }
            }
        }

写入结果:
在这里插入图片描述
结构体数组:
在这里插入图片描述

  • 3
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 倍福(Phytec倍福电子技术(上海)有限公司)是一家专业从事嵌入式系统的设计、开发和制造的公司,其ADS(Advanced Development System)是一套基于C++语言开发的通用控制面板软件开发工具包。 倍福ADS上位机的通信方式,可以通过TCP/IP协议或者串口通信实现。在TCP/IP协议下,ADS会作为一个服务器的角色(监听一个指定的端口),当上位机连接到ADS时,ADS会创建一个新的套接字来处理与该上位机的通信;在串口通信下,ADS将串口配置为指定的波特率、校验方式、数据位和停止位等参数,通过串口收发数据来实现与上位机的通信。 在具体实现过程中,可以采用socket编程实现TCP/IP通信,或者使用串口库实现串口通信。同时,ADS可定制化配置,可根据客户的需求,选择合适的通信方案,并进行相应的编程实现,以实现ADS上位机之间的高效、稳定的通信。 ### 回答2: 倍福是一种智能硬件系统,可以在工业控制和自动化等领域中应用。在工作过程中,倍福需要通过串口与上位机进行通信。而在实际应用中,具体的ADS通信和上位机通信步骤如下: 首先,用户需要连接倍福上位机所在的串口,然后运行ADS软件,确保ADS已经连接上了硬件设备。在建立通信之前,需要首先给硬件系统进行编程,然后把编程代码上传到硬件设备中,这样才可以使设备与ADS软件相互通信。 接下来,用户需要在ADS中选择相应的端口、波特率等配置,并设置相应的命令。这些命令可以包括读取传感器数据、设定倍福输出信号等。在设置完成之后,用户可以通过ADS界面操作,并通过串口与硬件系统进行交互。 在整个通信过程中,硬件系统需要实现以下功能:处理ADS发送的命令信息,根据命令信息进行相应的操作,例如读取数据、处理数据等。在数据处理完成之后,硬件系统需要将结果通过串口返回给上位机。 总的来说,ADS上位机通信是一个重要的功能,它能够让倍福智能硬件系统与用户进行数据交互,实现更多的工业控制和自动化应用。 ### 回答3: 倍福ADS(Active Dual Sensor)是一种基于MEMS制造技术的高精度角度测量传感器,它可以通过串行外设接口(SPI)与上位机进行通信。具体来说,通过ADS的SPI接口可以向上位机发送传感器采集的数据,比如角度值、温度值等等。同时,ADS也可以接收来自上位机的指令,比如配置传感器的采样率、滤波器、校准等参数。通过这样的通信方式,上位机可以实时地获取倍福传感器的输出数据,进而进行更加精细的算法计算和控制应用。 在硬件接口方面,倍福ADS可以与各种MCU(Micro Controller Unit)进行接口。一些常用的开发板包括STM32F系列,Arduino,Raspberry Pi等等。同时,倍福也提供了Windows下的GUI(Graphical User Interface)软件,可以对ADS进行配置和数据读取。值得一提的是,ADS的SPI接口通信速率可以高达10MHz,保证了高效率的数据传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值