手摸手教你撕碎西门子S7通讯协议10--S7Write写入float数据

news2024/9/9 5:46:40

1、S7通讯回顾

    - (1)建立TCP连接      Socket.Connect-》已实现

     - (2)发送访问请求     COTP-》已实现 

     - (3)交换通信信息     Setup Communication-》已实现

     - (4)执行相关操作     读、写、PLC启停、时间、上传下载-》本节实现写入float数据 

2、S7Write请求介绍

C#常用的有如下一些数据类型:

bool -> System.Boolean (布尔型,其值为 true 或者 false)
char -> System.Char (字符型,占有两个字节,表示 1 个 Unicode 字符)
byte -> System.Byte (字节型,占 1 字节,表示 8 位正整数,范围 0 ~ 255)
sbyte -> System.SByte (带符号字节型,占 1 字节,表示 8 位整数,范围 -128 ~ 127)
ushort -> System.UInt16 (无符号短整型,占 2 字节,表示 16 位正整数,范围 0 ~ 65,535)
uint -> System.UInt32 (无符号整型,占 4 字节,表示 32 位正整数,范围 0 ~ 4,294,967,295)
ulong -> System.UInt64 (无符号长整型,占 8 字节,表示 64 位正整数,范围 0 ~ 大约 10 的 20 次方)
short -> System.Int16 (短整型,占 2 字节,表示 16 位整数,范围 -32,768 ~ 32,767)
int -> System.Int32 (整型,占 4 字节,表示 32 位整数,范围 -2,147,483,648 到 2,147,483,647)
long -> System.Int64 (长整型,占 8 字节,表示 64 位整数,范围大约 -(10 的 19) 次方 到 10 的 19 次方)
float -> System.Single (单精度浮点型,占 4 个字节)
double -> System.Double (双精度浮点型,占 8 个字节)  

float类型占4个字节,那么一个float占4个字节的话,4个就是16个字节,而一个字节占8个bit,所以4个float数据就应该占128bit,转换成16进制就是0x80,因为写入数据的时候,这里是按位计算的,要注意哦,另外还有一个负数,如-9.4转换成字节是什么样的,还有大小端的问题要处理。

3、我来搞

1、先看PLC数据原来数据是什么

2、写入代码 

完整代码 

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace west.siemenscomm
{
    internal class Program
    {
        /// <summary>
        /// plc的ip地址
        /// </summary>
        static string _ip = "192.168.1.66";
        /// <summary>
        /// 端口号
        /// </summary>
        static int _port = 102;
        /// <summary>
        /// 机柜号,插槽号
        /// </summary>
        static byte _rack = 0, _slot = 1;
        /// <summary>
        /// socket对象
        /// </summary>
        static Socket socket = null;
        /// <summary>
        /// 时间事件
        /// </summary>
        static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
        /// <summary>
        /// 连接状态 
        /// </summary>
        static bool connectState = false;
        /// <summary>
        /// 通讯连接的pdu长度
        /// </summary>
        static short _pduSize = 240;

        static void Main(string[] args)
        {
            Connect();
            if (connectState)
            {
                COTPConnection();
                if (connectState)
                {
                    SetupCommunication();
                    if (connectState)
                    {
                        #region 读数据
                        //Console.WriteLine(ReadBool());
                        //Console.WriteLine("读取1个short");
                        //Console.WriteLine(ReadOneShort());
                        //Console.WriteLine("读取2个short");
                        //List<short> myshort = ReadMuiShort();
                        //if (myshort.Count != 0)
                        //{
                        //    myshort.ForEach(x => Console.WriteLine(x));
                        //}
                        //Console.WriteLine("读取2个float");
                        //List<float> myfloat = ReadMuiFloat();
                        //if (myfloat.Count != 0)
                        //{
                        //    myfloat.ForEach(x => Console.WriteLine(x));
                        //}
                        #endregion


                        #region 写数据
                        //WriteBool();
                        //WriteShort();
                        WriteFloat();
                        #endregion
                    }
                }
            }

            Console.ReadKey();
        }

        /// <summary>
        /// 写入float
        /// </summary>
        static void WriteFloat()
        {
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);//Version,版本默认3
            tpktBytes.Add(0x00);//Reserved,保留默认0
            // --------整个字节数组的长度


            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);//当前字节以后的字节数,注意这里是指cotp这个部分,而不是指整个字节部分,cotp占3个字节,所以这里是02
            cotpBytes.Add(0xf0);//PDU Type,
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);//Protocol Id,默认 
            headerBytes.Add(0x01);//ROSCTR:JOB
            headerBytes.Add(0x00);//Redundancy Identification (Reserved),占2个字节 
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);//Protocol Data Unit Reference,占2个字节 
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度

            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x05);// 功能码,固定的
            paramBytes.Add(0x01);//item cout数量,一个

            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);//结构标识,一般默认0x12
            itemBytes_1.Add(0x0a);//此字节往后的字节长度,不包括自己,0x0a转换成10进制就是10,所以从它后面有10个字节,但不包括自己
            itemBytes_1.Add(0x10);//Syntax Id:  S7ANY
            itemBytes_1.Add(0x06);// 读取类型  01-Bit  02-Byte  03-Char  04-Word  06-DWord
            // 写入长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x04);
            // DB块编号  
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81>I   82>Q   83>M   84>DB
            // 地址DB1.DBW2
            //int address = startAddr * 8 + bitAddr;
            int addr = (6<< 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion

            #region data
            List<byte> dataBytes_1 = new List<byte>();
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x04);//Transport size  数据类型  00-null , 03-bit , 04-byte/Word/DWord ,05-integer, 07-real ,09-0ctet string 
            //数据长度,这是长度是以bit为单位的,比如写入float那就是4个字节,因此是32个bit
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x80);
            //数据值dd
            var dd = 3.3;
            Type type = typeof(float);
            // 每一个type都有一个Parse方法
            MethodInfo mi = type.GetMethods().FirstOrDefault(m => m.Name == "Parse");
            dynamic v = mi.Invoke(type, new object[] { dd.ToString() });
            List<byte> valueBytes = new List<byte>(BitConverter.GetBytes(v));// 获取字节数组 
                                                                             // 需要进行颠倒字节序  在Windows系统下,BitConverter默认是小端
                                                                             // 0x00 0x01  // 大端
                                                                             // 0x01 0x00  // 小端
            valueBytes.Reverse();//反转字节顺序
            dataBytes_1.AddRange(valueBytes);

            dd = -5.5;
            v = mi.Invoke(type, new object[] { dd.ToString() });
            valueBytes = new List<byte>(BitConverter.GetBytes(v));// 获取字节数组  
            valueBytes.Reverse();//反转字节顺序
            dataBytes_1.AddRange(valueBytes);

            dd = 78;
            v = mi.Invoke(type, new object[] { dd.ToString() });
            valueBytes = new List<byte>(BitConverter.GetBytes(v));// 获取字节数组  
            valueBytes.Reverse();//反转字节顺序
            dataBytes_1.AddRange(valueBytes);


            dd = -26;
            v = mi.Invoke(type, new object[] { dd.ToString() });
            valueBytes = new List<byte>(BitConverter.GetBytes(v));// 获取字节数组  
            valueBytes.Reverse();//反转字节顺序
            dataBytes_1.AddRange(valueBytes);



            #endregion

            paramBytes.AddRange(itemBytes_1);

            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256)); // Parameter Lenght

            headerBytes.Add((byte)(dataBytes_1.Count / 256 % 256));
            headerBytes.Add((byte)(dataBytes_1.Count % 256));// Data Length
            headerBytes.AddRange(paramBytes);
            headerBytes.AddRange(dataBytes_1);

            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes);

            //发送
            socket.Send(tpktBytes.ToArray());

            // TPKT占4个字节
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);//先接收前4个字节,即TPKT部分
            byte[] lenBytes = new byte[2];//响应报文的整个字节长度
            lenBytes[0] = bytes[3];//高位
            lenBytes[1] = bytes[2];//低位
            short len = BitConverter.ToInt16(lenBytes, 0);//将字节转换成整形
            //这里减4的目的就是减去TPKT的长度
            len -= 4;//除TPKT后剩下的长度

            //获取除TPKT之后的所有字节
            byte[] respBytes = new byte[len];
            socket.Receive(respBytes, 0, len, SocketFlags.None);
            if (respBytes[13] == 0x00 && respBytes[14] == 0x00 && respBytes[17] == 0xff)
            {
                Console.WriteLine("写入float成功");
            }
        }



        /// <summary>
        /// 写入short
        /// </summary>
        static void WriteShort()
        {
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);//Version,版本默认3
            tpktBytes.Add(0x00);//Reserved,保留默认0
            // --------整个字节数组的长度


            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);//当前字节以后的字节数,注意这里是指cotp这个部分,而不是指整个字节部分,cotp占3个字节,所以这里是02
            cotpBytes.Add(0xf0);//PDU Type,
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);//Protocol Id,默认 
            headerBytes.Add(0x01);//ROSCTR:JOB
            headerBytes.Add(0x00);//Redundancy Identification (Reserved),占2个字节 
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);//Protocol Data Unit Reference,占2个字节 
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度

            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x05);// 功能码,固定的
            paramBytes.Add(0x01);//item cout数量,一个

            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);//结构标识,一般默认0x12
            itemBytes_1.Add(0x0a);//此字节往后的字节长度,不包括自己,0x0a转换成10进制就是10,所以从它后面有10个字节,但不包括自己
            itemBytes_1.Add(0x10);//Syntax Id:  S7ANY
            itemBytes_1.Add(0x04);// 读取类型  01-Bit  02-Byte  03-Char  04-Word  06-DWord
            // 写入长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // DB块编号  
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81>I   82>Q   83>M   84>DB
            // 地址DB1.DBW2
            //int address = startAddr * 8 + bitAddr;
            int addr = (2 << 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion

            #region data
            List<byte> dataBytes_1 = new List<byte>();
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x04);//Transport size  数据类型  00-null , 03-bit , 04-byte/Word/DWord ,05-integer, 07-real ,09-0ctet string 
            //数据长度,这是长度是以bit为单位的,比如写入short那就是2个字节,因此是16个bit
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x10);
            //数据值123 
            var dd = -66;
            Type type =typeof(short);
            // 每一个type都有一个Parse方法
            MethodInfo mi = type.GetMethods().FirstOrDefault(m => m.Name == "Parse");
            dynamic v = mi.Invoke(type, new object[] { dd.ToString() });
            List<byte> valueBytes = new List<byte>(BitConverter.GetBytes(v));// 获取字节数组 
             // 需要进行颠倒字节序  在Windows系统下,BitConverter默认是小端
                                                                             // 0x00 0x01  // 大端
                                                                             // 0x01 0x00  // 小端
            valueBytes.Reverse();//反转字节顺序
            dataBytes_1.AddRange(valueBytes); 
            #endregion

            paramBytes.AddRange(itemBytes_1);

            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256)); // Parameter Lenght

            headerBytes.Add((byte)(dataBytes_1.Count / 256 % 256));
            headerBytes.Add((byte)(dataBytes_1.Count % 256));// Data Length
            headerBytes.AddRange(paramBytes);
            headerBytes.AddRange(dataBytes_1);

            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes);

            //发送
            socket.Send(tpktBytes.ToArray());

            // TPKT占4个字节
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);//先接收前4个字节,即TPKT部分
            byte[] lenBytes = new byte[2];//响应报文的整个字节长度
            lenBytes[0] = bytes[3];//高位
            lenBytes[1] = bytes[2];//低位
            short len = BitConverter.ToInt16(lenBytes, 0);//将字节转换成整形
            //这里减4的目的就是减去TPKT的长度
            len -= 4;//除TPKT后剩下的长度

            //获取除TPKT之后的所有字节
            byte[] respBytes = new byte[len];
            socket.Receive(respBytes, 0, len, SocketFlags.None);
            if (respBytes[13] == 0x00 && respBytes[14] == 0x00 && respBytes[17] == 0xff)
            {
                Console.WriteLine("写入short成功");
            }
        }  

        /// <summary>
        /// 写入bool
        /// </summary>
        static void WriteBool()
        {
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);//Version,版本默认3
            tpktBytes.Add(0x00);//Reserved,保留默认0
            // --------整个字节数组的长度


            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);//当前字节以后的字节数,注意这里是指cotp这个部分,而不是指整个字节部分,cotp占3个字节,所以这里是02
            cotpBytes.Add(0xf0);//PDU Type,
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);//Protocol Id,默认 
            headerBytes.Add(0x01);//ROSCTR:JOB
            headerBytes.Add(0x00);//Redundancy Identification (Reserved),占2个字节 
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);//Protocol Data Unit Reference,占2个字节 
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度

            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x05);// 功能码,固定的
            paramBytes.Add(0x01);//item cout数量,一个

            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);//结构标识,一般默认0x12
            itemBytes_1.Add(0x0a);//此字节往后的字节长度,不包括自己,0x0a转换成10进制就是10,所以从它后面有10个字节,但不包括自己
            itemBytes_1.Add(0x10);//Syntax Id:  S7ANY
            itemBytes_1.Add(0x01);// 数据类型  01-Bit  02-Byte  03-Char  04-Word  06-DWord
            // 写入长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // DB块编号  
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81>I   82>Q   83>M   84>DB
            // 地址DB1.DBX0.0 
            //int address = startAddr * 8 + bitAddr;
            int addr = (0 << 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion

            #region data
            List<byte> dataBytes_1 = new List<byte>();
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x03);//Transport size   数据类型  00-null , 03-bit , 04-byte/Word/DWord ,05-integer, 07-real ,09-0ctet string 
            //数据长度
            dataBytes_1.Add(0x00);
            dataBytes_1.Add(0x01); 
            //数据值
            dataBytes_1.Add(0x00);// true为01,false为00
            dataBytes_1.Add(0x00);// 填充字节  
            #endregion

            paramBytes.AddRange(itemBytes_1);

            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256)); // Parameter Lenght

            headerBytes.Add((byte)(dataBytes_1.Count / 256 % 256));
            headerBytes.Add((byte)(dataBytes_1.Count % 256));// Data Length
            headerBytes.AddRange(paramBytes);
            headerBytes.AddRange(dataBytes_1); 

            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes);

            //发送
            socket.Send(tpktBytes.ToArray());

            // TPKT占4个字节
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);//先接收前4个字节,即TPKT部分
            byte[] lenBytes = new byte[2];//响应报文的整个字节长度
            lenBytes[0] = bytes[3];//高位
            lenBytes[1] = bytes[2];//低位
            short len = BitConverter.ToInt16(lenBytes,0);//将字节转换成整形
            //这里减4的目的就是减去TPKT的长度
            len -= 4;//除TPKT后剩下的长度

            //获取除TPKT之后的所有字节
            byte[] respBytes = new byte[len];
            socket.Receive(respBytes, 0, len, SocketFlags.None);
            if (respBytes[13] == 0x00&& respBytes[14] == 0x00&&respBytes[17] == 0xff)
            {
                Console.WriteLine("写入成功");
            }
        }


        /// <summary>
        /// 读取多个float数据
        /// </summary>
        /// <returns></returns>
        static List<float> ReadMuiFloat()
        {
            List<float> listfloat = new List<float>();
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);
            tpktBytes.Add(0x00);
            // --------整个字节数组的长度 

            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);
            cotpBytes.Add(0xf0);
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);
            headerBytes.Add(0x01);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度


            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x04);// Read Var
            paramBytes.Add(0x01);// 如果有多个区域请求的情况下,这里需要计算,计算Item的个数


            // Item Bytes   
            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);
            itemBytes_1.Add(0x0a);
            itemBytes_1.Add(0x10);
            itemBytes_1.Add(0x06);// 读取类型: 01Bit  02Byte  03Char  04Word 06DWord
            // 读取长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x04);
            // DB块编号   DB1.DBD6 
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81=I   82=Q   83=M   84=DB
            // 地址DB1.DBD6 
            //int address = startAddr * 8 + bitAddr;
            int addr = (6 << 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion

        
            // 拼装Parameter&Item
            paramBytes.AddRange(itemBytes_1);
          
            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256));
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.AddRange(paramBytes);
            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes);

            socket.Send(tpktBytes.ToArray());
            // 拿多少数据
            // TPKT
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);
            byte[] lenBytes = new byte[2];
            lenBytes[0] = bytes[3];
            lenBytes[1] = bytes[2];
            short len = BitConverter.ToInt16(lenBytes, 0);
            len -= 4;
            byte[] buffer = new byte[len];
            socket.Receive(buffer, 0, len, SocketFlags.None);
            // 判断是否有异常,buffer[13]是error class,buffer[14]是error code,buffer[17]是return code
            int index = 17;
            if (buffer[13] == 0x00 && buffer[14] == 0x00 && buffer[index] == 0xff)
            {
                lenBytes[0] = buffer[index + 3];
                lenBytes[1] = buffer[index + 2];
                ushort dataLen = (ushort)(BitConverter.ToUInt16(lenBytes, 0) / 8);// 数据响应长度  
                int typeLen = 4;//数据长度,float占4个字节

                List<byte> dataList = new List<byte>();
                byte[] dataBuffer = new byte[typeLen];//返回的具体数据
                // 所有返回的数据字节
                for (int sit = 0; sit < dataLen/typeLen;sit++)
                {
                    int myter = sit;
                    myter++;
                    Array.Copy(buffer, index + myter*4, dataBuffer, 0, typeLen); //数组拷贝 
                    dataList = new List<byte>(dataBuffer);
                    dataList.Reverse();// 反转顺序,处理大小端问题 
                    listfloat.Add(BitConverter.ToSingle(dataList.ToArray(), 0));//字节转换成浮点
                } 
            }
            return listfloat;
        }

        /// <summary>
        /// 读取多个short数据
        /// </summary>
        /// <returns></returns>
        static List<short> ReadMuiShort()
        {
            List<short> listshort = new List<short>();
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);
            tpktBytes.Add(0x00);
            // --------整个字节数组的长度 

            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);
            cotpBytes.Add(0xf0);
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);
            headerBytes.Add(0x01);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度


            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x04);// Read Var
            paramBytes.Add(0x02);// 如果有多个区域请求的情况下,这里需要计算,计算Item的个数


            // Item Bytes   
            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);
            itemBytes_1.Add(0x0a);
            itemBytes_1.Add(0x10);
            itemBytes_1.Add(0x04);// 读取类型: 01Bit  02Byte  03Char  04Word
            // 读取长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // DB块编号   DB1.DBW2 
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81=I   82=Q   83=M   84=DB
            // 地址DB1.DBW2 
            //int address = startAddr * 8 + bitAddr;
            int addr = (2 << 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion

            #region Item2
            List<byte> itemBytes_2 = new List<byte>();
            itemBytes_2.Add(0x12);
            itemBytes_2.Add(0x0a);
            itemBytes_2.Add(0x10);
            itemBytes_2.Add(0x04);// 读取类型: 01Bit  02Byte  03Char  04Word
            // 读取长度
            itemBytes_2.Add(0x00);
            itemBytes_2.Add(0x01);
            // DB块编号   DB1.DBW4 
            itemBytes_2.Add(0x00);
            itemBytes_2.Add(0x01);
            // 数据区域
            itemBytes_2.Add(0x84);  //81=I   82=Q   83=M   84=DB
            // 地址DB1.DBW4 
            //int address = startAddr * 8 + bitAddr;
            int addr2 = (4 << 3) + 0;
            itemBytes_2.Add((byte)(addr2 / 256 / 256 % 256));
            itemBytes_2.Add((byte)(addr2 / 256 % 256));
            itemBytes_2.Add((byte)(addr2 % 256));
            #endregion 

            // 拼装Parameter&Item
            paramBytes.AddRange(itemBytes_1);
            paramBytes.AddRange(itemBytes_2);
            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256));
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.AddRange(paramBytes);
            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes);

            socket.Send(tpktBytes.ToArray());
            // 拿多少数据
            // TPKT
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);
            byte[] lenBytes = new byte[2];
            lenBytes[0] = bytes[3];
            lenBytes[1] = bytes[2];
            short len = BitConverter.ToInt16(lenBytes, 0);
            len -= 4;
            byte[] buffer = new byte[len];
            socket.Receive(buffer, 0, len, SocketFlags.None);
            // 判断是否有异常,buffer[13]是error class,buffer[14]是error code,buffer[17]是return code
            int index = 17;
            if (buffer[13] == 0x00 && buffer[14] == 0x00 && buffer[index] == 0xff)
            {
                lenBytes[0] = buffer[index + 3];
                lenBytes[1] = buffer[index + 2];
                ushort dataLen = (ushort)(BitConverter.ToUInt16(lenBytes, 0) / 8);// 数据响应长度,这个长度是位长度,除以8得到字节,这里应该是2,因为short占2个字节宽度

                byte[] dataBuffer = new byte[dataLen];//返回的具体数据
                //第一个数据位置 index+4
                Array.Copy(buffer, index + 4, dataBuffer, 0, dataLen); //数组拷贝 
                List<byte> dataList = new List<byte>(dataBuffer);
                dataList.Reverse();// 处理大小端问题 
                listshort.Add(BitConverter.ToInt16(dataList.ToArray(), 0)); 
               
                //第二个数据位置index+10,不是index+8
                Array.Copy(buffer, index + 10, dataBuffer, 0, dataLen); //数组拷贝 
                dataList.Clear();//清空
                dataList = new List<byte>(dataBuffer);
                dataList.Reverse();// 处理大小端问题 
                listshort.Add(BitConverter.ToInt16(dataList.ToArray(), 0)); 
            }
            return listshort;
        }

        /// <summary>
        /// 读取一个short数据
        /// </summary>
        /// <returns></returns>
        static short ReadOneShort()
        {
            // TPKT
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);
            tpktBytes.Add(0x00);
            // --------整个字节数组的长度 

            // COTP
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);
            cotpBytes.Add(0xf0);
            cotpBytes.Add(0x80);

            // Header
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32);
            headerBytes.Add(0x01);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            // 添加Parameter字节数组的长度
            // 添加Data字节数组的长度


            // Parameter
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x04);// Read Var
            paramBytes.Add(0x01);// 如果有多个区域请求的情况下,这里需要计算,计算Item的个数


            // Item Bytes   
            #region Item1
            List<byte> itemBytes_1 = new List<byte>();
            itemBytes_1.Add(0x12);
            itemBytes_1.Add(0x0a);
            itemBytes_1.Add(0x10);
            itemBytes_1.Add(0x04);// 读取类型: 01Bit  02Byte  03Char  04Word
            // 读取长度
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // DB块编号   DB1.DBW2 
            itemBytes_1.Add(0x00);
            itemBytes_1.Add(0x01);
            // 数据区域
            itemBytes_1.Add(0x84);  //81=I   82=Q   83=M   84=DB
            // 地址DB1.DBW2 
            //int address = startAddr * 8 + bitAddr;
            int addr = (2 << 3) + 0;
            itemBytes_1.Add((byte)(addr / 256 / 256 % 256));
            itemBytes_1.Add((byte)(addr / 256 % 256));
            itemBytes_1.Add((byte)(addr % 256));
            #endregion 

            // 拼装Parameter&Item
            paramBytes.AddRange(itemBytes_1); 
            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));
            headerBytes.Add((byte)(paramBytes.Count % 256));
            headerBytes.Add(0x00);
            headerBytes.Add(0x00);
            headerBytes.AddRange(paramBytes);
            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes); 

            socket.Send(tpktBytes.ToArray());
            // 拿多少数据
            // TPKT
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None);
            byte[] lenBytes = new byte[2];
            lenBytes[0] = bytes[3];
            lenBytes[1] = bytes[2];
            short len = BitConverter.ToInt16(lenBytes,0);
            len -= 4; 
            byte[] buffer = new byte[len];
            socket.Receive(buffer, 0, len, SocketFlags.None);
            // 判断是否有异常,buffer[13]是error class,buffer[14]是error code,buffer[17]是return code
            int index = 17;
            if (buffer[13] == 0x00 && buffer[14] == 0x00 && buffer[index] == 0xff)
            {
                lenBytes[0] = buffer[index + 3];
                lenBytes[1] = buffer[index + 2]; 
                ushort dataLen = (ushort)(BitConverter.ToUInt16(lenBytes,0) / 8);// 数据响应长度,这个长度是位长度,除以8得到字节,这里应该是2,因为short占2个字节宽度

                byte[] dataBuffer = new byte[dataLen];//返回的具体数据
                Array.Copy(buffer, index + 4, dataBuffer, 0, dataLen); //数组拷贝 
                List<byte> dataList = new List<byte>(dataBuffer);
                dataList.Reverse();// 处理大小端问题 
                return BitConverter.ToInt16(dataList.ToArray(),0);
            }
            return 0;
        }


        /// <summary>
        /// 读取bool
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private static bool ReadBool()
        { 
            // TPKT,占4个字节
            List<byte> tpktBytes = new List<byte>();
            tpktBytes.Add(0x03);//Version,版本默认3
            tpktBytes.Add(0x00);//Reserved,保留默认
            // --------整个字节数组的长度,这个稍留着,要等到后面计算出来 

            // COTP,占3个字节
            List<byte> cotpBytes = new List<byte>();
            cotpBytes.Add(0x02);//当前字节以后的字节数
            cotpBytes.Add(0xf0);//PDU Type,数据传输
            cotpBytes.Add(0x80);//TPDU number,固定值

            // Header,占10个字节
            List<byte> headerBytes = new List<byte>();
            headerBytes.Add(0x32); // Protocol Id,默认
            headerBytes.Add(0x01); // ROSCTR:JOB
            headerBytes.Add(0x00); // Redundancy Identification (Reserved
            headerBytes.Add(0x00); // 
            headerBytes.Add(0x00); // Protocol Data Unit Reference
            headerBytes.Add(0x00); // 
            // 添加Parameter字节数组的长度,这个稍留着,要等到后面计算出来 
            // 添加Data字节数组的长度,这个稍留着,要等到后面计算出来 


            // Parameter,占2个字节
            List<byte> paramBytes = new List<byte>();
            paramBytes.Add(0x04); // Function: Read Var (0x04)[
            paramBytes.Add(0x01);// Item count: 1如果有多个区域请求的情况下,这里需要计算,计算Item的个数


            // Item Bytes  ,占12个字节 
            List<byte> itemBytes = new List<byte>();
            itemBytes.Add(0x12); // 结构标识,一般默认0x12
            itemBytes.Add(0x0a); // 此字节往后的字节长度
            itemBytes.Add(0x10); // Syntax Id: S7ANY (0x10) 
            itemBytes.Add(0x01); // Transport size: bit (2) 
            // 读取长度
            itemBytes.Add(0x00); // 高位
            itemBytes.Add(0x01); // 低位
            // DB块编号
            itemBytes.Add(0x00); // 高位
            itemBytes.Add(0x01); // 低位
            // 数据区域
            itemBytes.Add(0x84); // 数据区域
            // 地址,DB1.DBX0.0 
            //计算公式:int address = startAddr * 8 + bitAddr;
            int addr = (0 << 3) +0;
            //字节地址
            itemBytes.Add((byte)(addr / 256 / 256 % 256));
            itemBytes.Add((byte)(addr / 256 % 256));
            //位地址
            itemBytes.Add((byte)(addr % 256)); 

            // 拼装Parameter&Item
            paramBytes.AddRange(itemBytes);
            // 拼装Header&Parameter
            headerBytes.Add((byte)(paramBytes.Count / 256 % 256));//Parameter长度
            headerBytes.Add((byte)(paramBytes.Count % 256));
            headerBytes.Add(0x00);//Data length,读取操作没有数据,这里自然是0
            headerBytes.Add(0x00);
            headerBytes.AddRange(paramBytes);
            // 拼装COTP&Header
            cotpBytes.AddRange(headerBytes);
            //拼装 TPKT&COTP
            // tpkt现有长度+报文总长度2个字节+COTP长度
            int count = tpktBytes.Count + 2 + cotpBytes.Count;
            tpktBytes.Add((byte)(count / 256 % 256));
            tpktBytes.Add((byte)(count % 256));
            tpktBytes.AddRange(cotpBytes); 

            //发送
            socket.Send(tpktBytes.ToArray());
             
            //响应数据处理
            byte[] bytes = new byte[4];
            socket.Receive(bytes, 0, 4, SocketFlags.None); // TPKT
            byte[] lenBytes = new byte[2];//整个响应长度的字节数 
            lenBytes[0] = bytes[3];
            lenBytes[1] = bytes[2];
            short len = BitConverter.ToInt16(lenBytes,0);
            len -= 4;//减去 TPKT部分,就是剩下的长度

            byte[] buffer = new byte[len];
            socket.Receive(buffer, 0, len, SocketFlags.None);//接收剩下的全部数据
            // 判断是否有异常,buffer[13]是error class,buffer[14]是error code,buffer[17]是return code
            int index = 17;
            if (buffer[13] == 0x00 && buffer[14] == 0x00 && buffer[index] == 0xff)
            {
                //数据响应长度所在位置 
                lenBytes[0] = buffer[index + 3];
                lenBytes[1] = buffer[index + 2];
                //得到响应的数据长度
                ushort dataLen = BitConverter.ToUInt16(lenBytes,0); 
                byte[] dataBuffer = new byte[dataLen];
                //获取响应的数据
                Array.Copy(buffer, index + 4, dataBuffer, 0, dataLen); 
                return dataBuffer[0] == 0x01;
            }
            return false;
        }

        /// <summary>
        /// 通讯连接
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        private static bool SetupCommunication()
        {
            //s7comm连接包括4个部分,共25个字节,即25=4+3+10+8 
            byte[] setupBytes = new byte[] {
                // 1)TPKT包括4个字节
                0x03,//版本默认3
                0x00,//保留默认0
                0x00,//整个请求字节数高位
                0x19,//整个请求字节数低位,0x19转换成10进制就是25
               //2)COTP包括3个字节
                0x02,//当前字节以后的字节数(不包括自已,0x02转换成10进制就是2),注意这个“当前字节以后的字节数”是指COTP这部分,而不是整个字节部分
                0xf0,//PDU Type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
                0x80,//TPDU number,固定值
                // 3)Header包括10个字节
                0x32,//默认值,协议id
                0x01,//ROSCTR,0x01 Job request。主站发送请求,0x02 Ack。从站响应请求不带数据,0x03 Ack_Data。从站响应请求并带有数据,0x07 Userdata。原始协议的扩展。读取编程/调试、SZL读取、安全功能、时间设置等
                0x00,//Redundancy Identification (Reserved)固定值,占2个字节
                0x00,
                0x00,//Protocol Data Unit Reference固定值,占2个字节
                0x00,
                0x00,//Parameter length参数长度,占2个字节
                0x08,
                0x00,//Data length数据长度,占2个字节
                0x00,
                // 4)Parameter包括8个字节
                0xf0,//Function功能码,具体是:0x00 CPU服务,0xF0 设置通信,0x04 读取变量,0x05 写变量,0x1A 请求下载,0x1B 下载块,0x1C 下载结束,0x1D 开始上传,0x1E 上传,0x1F 结束上传,0x28 PLC 控制,0x29 PLC 停止
                0x00,//保留默认值
                0x00,//Max AmQ(parallel jobs with ack) calling,占2个字节
                0x03,
                0x00,//Max AmQ(parallel jobs with ack) called,占2个字节
                0x03,
                0x03,//PDU length,占2个字节,0x03co转换成10进制就是960
                0xc0
            };
            try
            {
                socket.Send(setupBytes);
                //响应报文的长度就是固定的27个字节
                byte[] respBytes = new byte[27];
                int count = socket.Receive(respBytes);
                // 拿到PDU长度   后续进行报文组装和接收的时候可以参考
                byte[] pdu_size = new byte[2];
                pdu_size[0] = respBytes[26];
                pdu_size[1] = respBytes[25];
                _pduSize = BitConverter.ToInt16(pdu_size,0);
                if (respBytes[17] != 0x00&& respBytes[18] != 0x00)
                {
                    Console.WriteLine("粗问题,COMM连接响应异常");
                    connectState = false;
                }
                else
                {
                    Console.WriteLine("太好了,COMM连接响应正常");
                    connectState = true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Setup通信未建立!" + ex.Message);
                connectState = false;
            }
            return connectState;
        }

        /// <summary>
        /// cotp连接
        /// </summary>
        private static bool COTPConnection()
        {
            //COTP连接包括2个部分,共22个字节),22=4+18
            byte[] cotpBytes = new byte[] {
                //1)TPKT包括4个字节
                0x03,//版本号,版本默认3
                0x00,//默认保留为0
                0x00,//整个请求字节高位
                0x16,//整个请求字节低位(0x16转换成为10进制就是22)
                //2)COTP包括18个字节 
                0x11,//当前字节以后的字节数(不包括自已,0x11转换成10进制就是17)
                0xe0,//PDU type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
                0x00,//DST reference(2个字节)
                0x00,//
                0x00,//SRC reference(2个字节)
                0x00,//
                0x00,//class(固定的) 
                0xc1, //Parameter-code  src-tsap 上位机
                0x02,  //Parameter-Len   
                0x10 ,   //Source TSAP:01->PG;02->OP;03->S7单边(服务器模式);0x10->S7双边通 
                0x00,   //机架与插槽号为0   
                0xc2,//Parameter-code  dst-tsap PLC 
                0x02,//Parameter len  
                0x03,//Destination TSAP  
                (byte)(_rack*32+_slot),//机架与插槽号: 
                0xc0,  //    Parameter code:tpdu-size 
                0x01,   //   Parameter length
                0x0a  //    TPDU size 
            };
            try
            {
                socket.Send(cotpBytes);
                //响应报文的长度是固定的22个字节
                byte[] respBytes = new byte[22];
                int count = socket.Receive(respBytes, 0, 22, SocketFlags.None);
                //第5个字节是pdu type,具体是:0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
                if (respBytes[5] != 0xd0)
                {
                    Console.WriteLine("粗问题,COTP连接响应异常");
                    connectState = false;
                }
                else
                {
                    Console.WriteLine("太好了,COTP连接响应正常");
                    connectState = true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("COTP连接未建立!" + ex.Message);
                connectState = false;
            }
            return connectState;
        }
        /// <summary>
        /// tcp连接 
        /// </summary>
        /// <param name="timeout"></param>
        private static void Connect(int timeout = 50)
        {
            TimeoutObject.Reset();
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.BeginConnect(_ip, _port, callback =>
                {
                    connectState = false;
                    var cbSocket = callback.AsyncState as Socket;
                    if (cbSocket != null)
                    {
                        connectState = cbSocket.Connected;
                        if (cbSocket.Connected)
                            cbSocket.EndConnect(callback);
                    }
                    TimeoutObject.Set();
                }, socket);
                TimeoutObject.WaitOne(2000, false);
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == 10060)
                    Console.WriteLine(ex.Message);
            }
            if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
            {
                Console.WriteLine("网络连接失败");
            }
            Console.WriteLine(connectState == true ? "连接成功" : "连接失败");
        }
    }
}

 

 3、运行效果

咱们工人有力量,屌丝逆天顶得住

4、小结

从读写操作来看,发现一个问题,那就是如果类型是变化的,地址是变化的,数量是变化的,长度是变化的,那代码很死板,不能应对啊,怎么办?怎么办?没有关系,大佬在法,为你封装通讯库,有了通讯库,可以非常灵活地应对类型,长度,地址等问题,下节继续浪起来

原创不易,打字截图不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,动动你的金手指,早日实现财务自由 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1961774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

器件学习——磁珠(2024.07.30)

参考链接1: 【器件篇】-25-磁珠的选型 在此感谢各位前辈大佬的总结&#xff0c;写这个只是为了记录学习大佬资料的过程&#xff0c;内容基本都是搬运的大佬博客&#xff0c;觉着有用自己搞过来自己记一下&#xff0c;如果有大佬觉着我搬过来不好&#xff0c;联系我删。 器件学习…

【MyBatis】史上最全的MyBatis执行SQL原理分析

目录 一、前言 二、简介 三、SQL 执行过程分析 3.1 SQL 执行入口分析 3.1.0 获取SqlSession对象 3.1.1 为 Mapper 接口创建代理对象 3.1.2 执行代理逻辑 3.1.2.1 获取 / 创建 MapperMethod 对象 3.1.2.1.1 创建 SqlCommand 对象 3.1.2.1.2 创建 MethodSignature 对象…

华为OD机试 - Wonderland游乐园 - 动态规划(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、3个测…

答应我,在量化策略回测里,避开未来函数这4个坑

由于社群的原因&#xff0c;看过不少策略&#xff0c;今天就腆着脸唠唠&#xff0c;量化新手期经常碰到未来函数的4个坑&#xff0c;希望量化萌新们少掉点儿头发。新手向文章&#xff0c;大神请绕行~ 情景1:使用前复权价格数据。 由于股票会存在分红送股的情形&#xff0c;价格…

芋道源码yudao-cloud 二开笔记(Editor富文本本地图片上传报错问题)

&#xff1a; 于是找到富文本的组件代码Editor.vue&#xff0c;检查一下上传的接口地址和token有没有传&#xff0c;如下图&#xff1a; 都没有问题&#xff0c;但还是报错&#xff0c;所以试试自定义上传的方法&#xff1a; // 导入上传文件的接口 import * as FileApi from …

reese84分析

声明 本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 如有侵权,请联系我进行删除。 这里只是我分析的分析过程,以及一些重要点的记录…

最近火爆的GraphRAG是什么?真的那么有用吗?

最近&#xff0c;微软提出的GraphRAG项目引起了广泛关注。那么&#xff0c;GraphRAG究竟是什么&#xff1f;它真的那么实用吗&#xff1f;本文将为您详细解读GraphRAG的概念及其应用。 什么是传统的RAG&#xff1f; &#x1f4da; 在深入了解GraphRAG之前&#xff0c;我们首先…

掌握AJAX技术:从基础到实战

文章目录 **引言****1. 什么是AJAX&#xff1f;****2. AJAX的工作原理**AJAX 示例使用 Fetch API 实现 AJAX **3. 如何在项目中使用AJAX****4. 处理AJAX请求的常见问题****5. AJAX与JSON的结合****6. 使用AJAX框架和库****7. 实战&#xff1a;创建一个动态表单****8. AJAX中的事…

Python 解决 ImportError: cannot import name ‘example’

Python 解决 ImportError: cannot import name ‘example’ 在Python编程的广阔天地中&#xff0c;ImportError: cannot import name example 是一个令人头疼但又常见的错误。当你试图从某个模块中导入一个不存在的名称时&#xff0c;这个错误就会悄然降临。本文将带你深入探索…

AI推理硬件成本分析:AMD Instinct MI300X与Nvidia GPU比较

随着AI模型训练成本的上升&#xff0c;人们越来越关注推理硬件的成本&#xff0c;尤其是在需要低延迟响应的应用中。Transformer模型需要强大的硬件支持&#xff0c;例如200毫秒以下的响应时间。Artificial Analysis最近分析了AI模型性能和定价&#xff0c;特别指出AMD的“Anta…

「豆包Marscode体验官」AI加持的云端IDE——三种方法高效开发前后端聊天交互功能

豆包 MarsCode 是一个集成了AI功能的编程助手和云端IDE&#xff0c;旨在提高开发效率和质量。它支持多种编程语言和IDE&#xff0c;提供智能代码补全、代码解释、单元测试生成和问题修复等功能&#xff0c;同时具备AI对话视图和开发工具。 豆包 MarsCode 豆包 MarsCode 编程助…

跟着动脑学院学习Android 开发基础

跟着动脑学院up主学习Android开发&#xff0c;记录学习笔记 2022 最新 Android 基础教程&#xff0c;从开发入门到项目实战&#xff0c;看它就够了&#xff0c;更新中_哔哩哔哩_bilibili &#xff08;弱弱地说一句&#xff0c;绝大部分内容都是up主为我们准备好的资料里摘抄下…

机器学习 | 评估原理——模型评估与交叉验证

Hi&#xff0c;大家好&#xff0c;我是半亩花海。学完分类算法原理的知识&#xff0c;我们进入评估相关知识的学习&#xff0c;继续更新《白话机器学习的数学》这本书的学习笔记&#xff0c;在此分享模型评估与交叉验证相关评估原理。本章的基于前几节已建立的模型进行评估知识…

【C语言】Linux 飞翔的小鸟

【C语言】Linux 飞翔的小鸟 零、环境部署 安装Ncurses库 sudo apt-get install libncurses5-dev壹、编写代码 代码如下&#xff1a; bird.c #include<stdio.h> #include<time.h> #include<stdlib.h> #include<signal.h> #include<curses.h>…

LeetCode:相同的树(C语言)

1、问题概述&#xff1a;给2个二叉树的根节点p和q&#xff0c;如果2个树在结构和数值上都相同才为true&#xff0c;否则为false 2、示例 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;p [1,2], q […

做知识付费项目还能做吗?知识付费副业项目如何做?能挣多少钱?

hello,我是阿磊&#xff0c;一个20年的码农&#xff0c;6年前代码写不动了&#xff0c;转型专职做副业项目研究&#xff0c;为劳苦大众深度挖掘互联网副业项目&#xff0c;共同富裕。 现在做知识付费项目还能做吗&#xff1f; 互联网虚拟资源项目我一直在做&#xff0c;做了有…

AI绘画模型之:UNet、Imagen 与 DeepFloyd IF

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

spring boot(学习笔记第十五课)

spring boot(学习笔记第十五课) Spring boot的websocket(广播) 学习内容&#xff1a; Spring boot的websocket&#xff08;广播&#xff09; 1. Spring boot的websocket&#xff08;广播&#xff09; 回顾下web server的进化 第一代Web程序&#xff0c;使用整体页面刷新技术…

GPT-4o mini- 开发者的新宠儿

在人工智能的浪潮中,一颗新星正在冉冉升起。OpenAI最新发布的GPT-4o mini模型以其惊人的性能和极具竞争力的价格,正在成为开发者们的新宠儿。作为一名大数据开发者,我深深被这个"迄今为止最具成本效益的小模型"所吸引。让我们一起探索GPT-4o mini的魅力,看看它如何改…

一些问题 7/28

get post可以public吗 在Java Servlet中&#xff0c;doGet()和doPost()方法的访问修饰符通常是public&#xff0c;因为这些方法需要被Servlet容器&#xff08;如Tomcat&#xff09;调用。 如果将这些方法声明为private或protected&#xff0c;Servlet容器将无法访问它们&…