当前位置: 首页 > news >正文

C#使用TCP-S7协议读写西门子PLC(三)

接上篇

C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客

这里我们进行封装读写西门子PLC的S7协议命令以及连接西门子PLC并两次握手

新建部分类文件SiemensS7ProtocolUtil.ReadWrite.cs

主要方法:

连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC

public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000)

生成一个写入字节数据的指令

public static OperateResult<byte[]> BuildWriteByteCommand(OperateResult<byte, int, ushort> analysis, byte[] data)

生成一个读取字数据指令头的通用方法【一个字Word占用两个字节Byte】

public static OperateResult<byte[]> BuildReadCommand(OperateResult<byte, int, ushort>[] address, ushort[] length)

SiemensS7ProtocolUtil.ReadWrite.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace PlcSiemesS7Demo
{/// <summary>/// 西门子S7协议,封装读写命令/// 关键方法:连接PLC并发送两次握手、生成写PLC命令,生成读PLC命令/// (1).public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000)/// (2).public static OperateResult&lt;byte[]&gt; BuildWriteByteCommand(OperateResult&lt;byte, int, ushort&gt; analysis, byte[] data)/// (3).BuildReadCommand(OperateResult&lt;byte, int, ushort&gt;[] address, ushort[] length)/// </summary>public partial class SiemensS7ProtocolUtil{#region 西门子S7协议握手【两次握手】命令,固定.不同型号的PLC握手命令有少许不同private byte[] plcHead1 = new byte[22]{0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC0,0x01,0x0A,0xC1,0x02,0x01,0x02,0xC2,0x02,0x01,0x00};private byte[] plcHead2 = new byte[25]{0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0x04,0x00,0x00,0x08,0x00,0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x01,0xE0};private byte[] plcHead1_200smart = new byte[22]{0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,0x02,0x03,0x00,0xC0,0x01,0x0A};private byte[] plcHead2_200smart = new byte[25]{0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0};#endregion/// <summary>/// 连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC/// 使用网络终结点和PLC型号枚举进行连接/// </summary>/// <param name="siemensPlcCategory"></param>/// <param name="endPoint"></param>/// <param name="timeout"></param>/// <returns></returns>public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, IPEndPoint endPoint, int timeout = 3000){isConnected = false;this.SiemensPlcCategory = siemensPlcCategory;switch (siemensPlcCategory){case SiemensPlcCategory.S1200:case SiemensPlcCategory.S1500:plcHead1[21] = 0;break;case SiemensPlcCategory.S300:plcHead1[21] = 2;break;case SiemensPlcCategory.S400:plcHead1[21] = 3;plcHead1[17] = 0x00;break;case SiemensPlcCategory.S200Smart:plcHead1 = plcHead1_200smart;plcHead2 = plcHead2_200smart;break;default:plcHead1[18] = 0;break;}// 重新连接之前,先将旧的数据进行清空CoreSocket?.Close();OperateResult<Socket> result = ConnectPlc(endPoint, timeout);if (result.IsSuccess){// 第一次握手 -> First handshakeOperateResult<byte[]> read_first = SendDataAndWaitResult(result.Content, plcHead1);if (!read_first.IsSuccess){RecordLogEvent?.Invoke($"第一次握手出错:{read_first.Message}");return read_first;}// 第二次握手 -> Second handshakeOperateResult<byte[]> read_second = SendDataAndWaitResult(result.Content, plcHead2);if (!read_second.IsSuccess){RecordLogEvent?.Invoke($"第二次握手出错:{read_second.Message}");return read_second;}// 返回成功的信号 CoreSocket = result.Content;result.IsSuccess = true;isConnected = true;RecordLogEvent?.Invoke($"连接PLC【{endPoint}】成功并且两次握手成功");}else{result.Content?.Close();CoreSocket = null;result.IsSuccess = false;}return result;}/// <summary>/// 连接西门子PLC并发送两次握手。两次握手成功后,才真正连接到PLC/// 使用IP地址和端口【默认102】和PLC型号枚举进行连接/// </summary>/// <param name="siemensPlcCategory"></param>/// <param name="ipAddress"></param>/// <param name="port"></param>/// <param name="timeout"></param>/// <returns></returns>public OperateResult ConnectPlcAndHandshake(SiemensPlcCategory siemensPlcCategory, string ipAddress, int port = 102, int timeout = 3000){return ConnectPlcAndHandshake(siemensPlcCategory, new IPEndPoint(IPAddress.Parse(ipAddress), port), timeout);}private OperateResult<byte[]> Read(OperateResult<byte, int, ushort>[] address, ushort[] length){// 构建指令 -> Build read commandOperateResult<byte[]> command = BuildReadCommand(address, length);if (!command.IsSuccess) return command;// 核心交互 -> Core InteractionsOperateResult<byte[]> read = SendDataAndWaitResult(command.Content);if (!read.IsSuccess) return read;// 分析结果 -> Analysis resultsint receiveCount = 0;for (int i = 0; i < length.Length; i++){receiveCount += length[i];}if (read.Content.Length >= 21 && read.Content[20] == length.Length){byte[] buffer = new byte[receiveCount];int kk = 0;int ll = 0;for (int ii = 21; ii < read.Content.Length; ii++){if ((ii + 1) < read.Content.Length){if (read.Content[ii] == 0xFF && read.Content[ii + 1] == 0x04){Array.Copy(read.Content, ii + 4, buffer, ll, length[kk]);ii += length[kk] + 3;ll += length[kk];kk++;}}}return OperateResult.CreateSuccessResult(buffer);}else{return new OperateResult<byte[]>() { ErrorCode = read.ErrorCode, Message = "数据块长度校验失败,请检查是否开启put/get以及关闭db块优化" };}}private OperateResult<byte[]> ReadBitFromPLC(OperateResult<byte, int, ushort> analysis){// 指令生成 -> Build bit read commandOperateResult<byte[]> command = BuildBitReadCommand(analysis);if (!command.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(command);// 核心交互 ->  Core interactiveOperateResult<byte[]> read = SendDataAndWaitResult(command.Content);if (!read.IsSuccess) return read;// 分析结果 -> Analysis read resultint receiveCount = 1;if (read.Content.Length >= 21 && read.Content[20] == 1){byte[] buffer = new byte[receiveCount];if (22 < read.Content.Length){if (read.Content[21] == 0xFF && read.Content[22] == 0x03){buffer[0] = read.Content[25];}}return OperateResult.CreateSuccessResult(buffer);}else{return new OperateResult<byte[]>(read.ErrorCode, "数据块长度校验失败,请检查是否开启put/get以及关闭db块优化");}}/// <summary>/// 基础的写入数据的操作支持 -> Operational support for the underlying write data/// </summary>/// <param name="entireValue">完整的字节数据 -> Full byte data</param>/// <returns>是否写入成功的结果对象 -> Whether to write a successful result object</returns>private OperateResult WriteBase(byte[] entireValue){OperateResult<byte[]> write = SendDataAndWaitResult(entireValue);if (!write.IsSuccess) return write;if (write.Content[write.Content.Length - 1] != 0xFF){// 写入异常 -> WriteErrorreturn new OperateResult(write.Content[write.Content.Length - 1], "写入数据异常,代号为:" + write.Content[write.Content.Length - 1]);}else{return OperateResult.CreateSuccessResult();}}/// <summary>/// 生成一个写入字节数据的指令 -> Generate an instruction to write byte data/// </summary>/// <param name="analysis">内存区域标识,起始地址*8,DB块编号 </param>/// <param name="data">原始的字节数据 -> Raw byte data</param>/// <returns>包含结果对象的报文 -> Message containing the result object</returns>public static OperateResult<byte[]> BuildWriteByteCommand(OperateResult<byte, int, ushort> analysis, byte[] data){byte[] _PLCCommand = new byte[35 + data.Length];_PLCCommand[0] = 0x03;_PLCCommand[1] = 0x00;// 长度 -> Length_PLCCommand[2] = (byte)((35 + data.Length) / 256);_PLCCommand[3] = (byte)((35 + data.Length) % 256);// 固定 -> Fixed_PLCCommand[4] = 0x02;_PLCCommand[5] = 0xF0;_PLCCommand[6] = 0x80;_PLCCommand[7] = 0x32;// 命令 发 -> command to send_PLCCommand[8] = 0x01;// 标识序列号 -> Identification serial Number_PLCCommand[9] = 0x00;_PLCCommand[10] = 0x00;_PLCCommand[11] = 0x00;_PLCCommand[12] = 0x01;// 固定 -> Fixed_PLCCommand[13] = 0x00;_PLCCommand[14] = 0x0E;// 写入长度+4 -> Write Length +4_PLCCommand[15] = (byte)((4 + data.Length) / 256);_PLCCommand[16] = (byte)((4 + data.Length) % 256);// 读写指令 -> Read and write instructions_PLCCommand[17] = 0x05;// 写入数据块个数 -> Number of data blocks written_PLCCommand[18] = 0x01;// 固定,返回数据长度 -> Fixed, return data length_PLCCommand[19] = 0x12;_PLCCommand[20] = 0x0A;_PLCCommand[21] = 0x10;// 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word_PLCCommand[22] = 0x02;// 写入数据的个数 -> Number of Write Data_PLCCommand[23] = (byte)(data.Length / 256);_PLCCommand[24] = (byte)(data.Length % 256);// DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block_PLCCommand[25] = (byte)(analysis.Content3 / 256);_PLCCommand[26] = (byte)(analysis.Content3 % 256);// 写入数据的类型 -> Types of writing data_PLCCommand[27] = analysis.Content1;// 偏移位置 -> Offset position 因65535*8 即0x07 FF F8 偏移地址占用三个字节:下面注释的三行等价于BitConverter.GetBytes(Int32) //_PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256);//_PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);//_PLCCommand[30] = (byte)(analysis.Content2 % 256);byte[] offsetAddress = BitConverter.GetBytes(analysis.Content2);_PLCCommand[28] = offsetAddress[2];_PLCCommand[29] = offsetAddress[1];_PLCCommand[30] = offsetAddress[0];// 按字写入 -> Write by Word_PLCCommand[31] = 0x00;_PLCCommand[32] = 0x04;// 按位计算的长度 -> The length of the bitwise calculation_PLCCommand[33] = (byte)(data.Length * 8 / 256);_PLCCommand[34] = (byte)(data.Length * 8 % 256);data.CopyTo(_PLCCommand, 35);return OperateResult.CreateSuccessResult(_PLCCommand);}public static OperateResult<byte[]> BuildWriteBitCommand(OperateResult<byte, int, ushort> analysis, bool data){//OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);byte[] buffer = new byte[1];buffer[0] = data ? (byte)0x01 : (byte)0x00;byte[] _PLCCommand = new byte[35 + buffer.Length];_PLCCommand[0] = 0x03;_PLCCommand[1] = 0x00;// 长度 -> length_PLCCommand[2] = (byte)((35 + buffer.Length) / 256);_PLCCommand[3] = (byte)((35 + buffer.Length) % 256);// 固定 -> fixed_PLCCommand[4] = 0x02;_PLCCommand[5] = 0xF0;_PLCCommand[6] = 0x80;_PLCCommand[7] = 0x32;// 命令 发 -> command to send_PLCCommand[8] = 0x01;// 标识序列号 -> Identification serial Number_PLCCommand[9] = 0x00;_PLCCommand[10] = 0x00;_PLCCommand[11] = 0x00;_PLCCommand[12] = 0x01;// 固定 -> fixed_PLCCommand[13] = 0x00;_PLCCommand[14] = 0x0E;// 写入长度+4 -> Write Length +4_PLCCommand[15] = (byte)((4 + buffer.Length) / 256);_PLCCommand[16] = (byte)((4 + buffer.Length) % 256);// 命令起始符 -> Command start character_PLCCommand[17] = 0x05;// 写入数据块个数 -> Number of data blocks written_PLCCommand[18] = 0x01;_PLCCommand[19] = 0x12;_PLCCommand[20] = 0x0A;_PLCCommand[21] = 0x10;// 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word_PLCCommand[22] = 0x01;// 写入数据的个数 -> Number of Write Data_PLCCommand[23] = (byte)(buffer.Length / 256);_PLCCommand[24] = (byte)(buffer.Length % 256);// DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block_PLCCommand[25] = (byte)(analysis.Content3 / 256);_PLCCommand[26] = (byte)(analysis.Content3 % 256);// 写入数据的类型 -> Types of writing data_PLCCommand[27] = analysis.Content1;// 偏移位置 -> Offset position_PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256);_PLCCommand[29] = (byte)(analysis.Content2 / 256);_PLCCommand[30] = (byte)(analysis.Content2 % 256);// 按位写入 -> Bitwise Write_PLCCommand[31] = 0x00;_PLCCommand[32] = 0x03;// 按位计算的长度 -> The length of the bitwise calculation_PLCCommand[33] = (byte)(buffer.Length / 256);_PLCCommand[34] = (byte)(buffer.Length % 256);buffer.CopyTo(_PLCCommand, 35);return OperateResult.CreateSuccessResult(_PLCCommand);}/// <summary>/// 解析地址,返回元组【地址类型,起始地址,DB块编号】/// </summary>/// <param name="plcRegisterCategory">寄存器类型</param>/// <param name="offsetAddress">偏移量,偏移地址</param>/// <param name="dbNumber">DB块号</param>/// <param name="bitIndex">位索引0~7</param>/// <returns></returns>private static OperateResult<byte, int, ushort> AnalysisAddress(PlcRegisterCategory plcRegisterCategory, ushort offsetAddress, ushort dbNumber = 0, int bitIndex = 0){//地址类型,起始地址,DB块编号OperateResult<byte, int, ushort> result = new OperateResult<byte, int, ushort>();result.Content3 = 0;switch (plcRegisterCategory){case PlcRegisterCategory.DB:result.Content1 = 0x84;result.Content2 = offsetAddress * 8 + bitIndex;result.Content3 = dbNumber;break;case PlcRegisterCategory.M:result.Content1 = 0x83;result.Content2 = offsetAddress * 8 + bitIndex;break;case PlcRegisterCategory.Input:result.Content1 = 0x81;result.Content2 = offsetAddress * 8 + bitIndex;break;case PlcRegisterCategory.Quit:result.Content1 = 0x82;result.Content2 = offsetAddress * 8 + bitIndex;break;case PlcRegisterCategory.T:result.Content1 = 0x1D;result.Content2 = offsetAddress * 8 + bitIndex;break;case PlcRegisterCategory.C:result.Content1 = 0x1C;result.Content2 = offsetAddress * 8 + bitIndex;break;case PlcRegisterCategory.V:result.Content1 = 0x84;result.Content2 = offsetAddress * 8 + bitIndex;result.Content3 = 1;break;default:result.Message = "输入的类型不支持,请重新输入";result.Content1 = 0;result.Content2 = 0;result.Content3 = 0;return result;}result.IsSuccess = true;return result;}/// <summary>/// 生成一个读取字数据指令头的通用方法【一个字Word占用两个字节Byte】 ->/// A general method for generating a command header to read a Word data/// </summary>/// <param name="address">起始地址,例如M100,I0,Q0,DB2.100 ->/// Start address, such as M100,I0,Q0,DB2.100</param>/// <param name="length">读取数据长度 -> Read Data length</param>/// <returns>包含结果对象的报文 -> Message containing the result object</returns>public static OperateResult<byte[]> BuildReadCommand(OperateResult<byte, int, ushort>[] address, ushort[] length){if (address == null) throw new NullReferenceException("address");if (length == null) throw new NullReferenceException("count");if (address.Length != length.Length) throw new Exception("两个参数的个数不一致");if (length.Length > 19) throw new Exception("读取的数组数量不允许大于19");int readCount = length.Length;byte[] _PLCCommand = new byte[19 + readCount * 12];// ======================================================================================_PLCCommand[0] = 0x03;                                                // 报文头 -> Head_PLCCommand[1] = 0x00;_PLCCommand[2] = (byte)(_PLCCommand.Length / 256);                    // 长度 -> Length_PLCCommand[3] = (byte)(_PLCCommand.Length % 256);_PLCCommand[4] = 0x02;                                                // 固定 -> Fixed_PLCCommand[5] = 0xF0;_PLCCommand[6] = 0x80;_PLCCommand[7] = 0x32;                                                // 协议标识 -> Protocol identification_PLCCommand[8] = 0x01;                                                // 命令:发 -> Command: Send_PLCCommand[9] = 0x00;                                                // 冗余标识(保留)-> redundancy identification (reserved): 0x0000;_PLCCommand[10] = 0x00;                                               // protocol data unit reference; it’s increased by request event;_PLCCommand[11] = 0x00;_PLCCommand[12] = 0x01;                                               // 参数命令数据总长度 -> Parameter command Data total length_PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);_PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);_PLCCommand[15] = 0x00;                                               // 读取内部数据时为00,读取CPU型号为Data数据长度 -> Read internal data is 00, read CPU model is data length_PLCCommand[16] = 0x00;// =====================================================================================_PLCCommand[17] = 0x04;                                               // 读写指令,04读,05写 -> Read-write instruction, 04 read, 05 Write_PLCCommand[18] = (byte)readCount;                                    // 读取数据块个数 -> Number of data blocks readfor (int ii = 0; ii < readCount; ii++){//===========================================================================================// 指定有效值类型 -> Specify a valid value type_PLCCommand[19 + ii * 12] = 0x12;// 接下来本次地址访问长度 -> The next time the address access length_PLCCommand[20 + ii * 12] = 0x0A;// 语法标记,ANY -> Syntax tag, any_PLCCommand[21 + ii * 12] = 0x10;// 按字为单位 -> by word_PLCCommand[22 + ii * 12] = 0x02;// 访问数据的个数 -> Number of Access data_PLCCommand[23 + ii * 12] = (byte)(length[ii] / 256);_PLCCommand[24 + ii * 12] = (byte)(length[ii] % 256);// DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block_PLCCommand[25 + ii * 12] = (byte)(address[ii].Content3 / 256);_PLCCommand[26 + ii * 12] = (byte)(address[ii].Content3 % 256);// 访问数据类型 -> Accessing data types_PLCCommand[27 + ii * 12] = address[ii].Content1;// 偏移位置 -> Offset position_PLCCommand[28 + ii * 12] = (byte)(address[ii].Content2 / 256 / 256 % 256);_PLCCommand[29 + ii * 12] = (byte)(address[ii].Content2 / 256 % 256);_PLCCommand[30 + ii * 12] = (byte)(address[ii].Content2 % 256);}return OperateResult.CreateSuccessResult(_PLCCommand);}/// <summary>/// 生成一个位读取数据指令头的通用方法 ->/// A general method for generating a bit-read-Data instruction header/// </summary>/// <param name="address">起始地址,例如M100.0,I0.1,Q0.1,DB2.100.2 ->/// Start address, such as M100.0,I0.1,Q0.1,DB2.100.2/// </param>/// <returns>包含结果对象的报文 -> Message containing the result object</returns>public static OperateResult<byte[]> BuildBitReadCommand(OperateResult<byte, int, ushort> analysis){//OperateResult<byte, int, ushort> analysis = AnalysisAddress(address);if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);byte[] _PLCCommand = new byte[31];_PLCCommand[0] = 0x03;_PLCCommand[1] = 0x00;// 长度 -> Length_PLCCommand[2] = (byte)(_PLCCommand.Length / 256);_PLCCommand[3] = (byte)(_PLCCommand.Length % 256);// 固定 -> Fixed_PLCCommand[4] = 0x02;_PLCCommand[5] = 0xF0;_PLCCommand[6] = 0x80;_PLCCommand[7] = 0x32;// 命令:发 -> command to send_PLCCommand[8] = 0x01;// 标识序列号_PLCCommand[9] = 0x00;_PLCCommand[10] = 0x00;_PLCCommand[11] = 0x00;_PLCCommand[12] = 0x01;// 命令数据总长度 -> Identification serial Number_PLCCommand[13] = (byte)((_PLCCommand.Length - 17) / 256);_PLCCommand[14] = (byte)((_PLCCommand.Length - 17) % 256);_PLCCommand[15] = 0x00;_PLCCommand[16] = 0x00;// 命令起始符 -> Command start character_PLCCommand[17] = 0x04;// 读取数据块个数 -> Number of data blocks read_PLCCommand[18] = 0x01;//===========================================================================================// 读取地址的前缀 -> Read the prefix of the address_PLCCommand[19] = 0x12;_PLCCommand[20] = 0x0A;_PLCCommand[21] = 0x10;// 读取的数据时位 -> Data read-time bit_PLCCommand[22] = 0x01;// 访问数据的个数 -> Number of Access data_PLCCommand[23] = 0x00;_PLCCommand[24] = 0x01;// DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block_PLCCommand[25] = (byte)(analysis.Content3 / 256);_PLCCommand[26] = (byte)(analysis.Content3 % 256);// 访问数据类型 -> Types of reading data_PLCCommand[27] = analysis.Content1;// 偏移位置 -> Offset position_PLCCommand[28] = (byte)(analysis.Content2 / 256 / 256 % 256);_PLCCommand[29] = (byte)(analysis.Content2 / 256 % 256);_PLCCommand[30] = (byte)(analysis.Content2 % 256);return OperateResult.CreateSuccessResult(_PLCCommand);}}
}

相关文章:

C#使用TCP-S7协议读写西门子PLC(三)

接上篇 C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客 这里我们进行封装读写西门子PLC的S7协议命令以及连接西门子PLC并两次握手 新建部分类文件SiemensS7ProtocolUtil.ReadWrite.cs 主要方法&#xff1a; 连接西门子PLC并发送两次握手。两次握手成功后&#xff0c;才真正连…...

铝型材及其常用紧固件、连接件介绍

铝型材介绍&#xff08;包括紧固件和连接件以及走线&#xff09; 铝型材 铝型材一般是6063铝合金挤压成型&#xff0c;分为欧标和国标两个标准。&#xff08;左边国标&#xff0c;右边欧标&#xff0c;欧标槽宽一点&#xff09; 由于槽型不一样&#xff0c;相关的螺栓和螺母也…...

【裸机装机系列】7.kali(ubuntu)-安装开发所需工具

如果你是后端或是人工智能AI岗&#xff0c;可以安装以下推荐的软件&#xff1a; 1> sublime sublime官网 下载deb文件 安装命令 sudo dpkg -i sublime-text_build-4143_amd64.deb2> vscode 安装前置软件 sudo apt install curl gpg software-properties-common apt-t…...

[C语言]第九节 函数一基础知识到高级技巧的全景探索

目录 9.1 函数的概念 9.2 库函数 9.2.1 标准库与库函数 示例&#xff1a;常见库函数 9.2.2 标准库与头文件的关系 参考资料和学习工具 如何使用库函数 ​编辑 9.3 ⾃定义函数 9.3.1 函数的语法形式 9.3.2函数的举例 9.4 实参与形参 9.4.1 什么是实参&#xff1f; 9…...

1.1 计算机网络基本概述

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、网络的基本概念二、集线器、交换机和路由器三、互连网与互联网四、网络的类型五、互连网的组成1. 边缘部分2. 核心部分 六、网络协议 前言 计算机网络是现代信息社会…...

Linux环境基础开发工具使用(gcc/g++与makefile)

1.Linux编译器-gcc/g使用 1. 背景知识 接下来的操作&#xff0c;我以gcc为例&#xff0c;因为两者选项都是通用的&#xff0c;所以也就相当于间接学习了 1.预处理&#xff08;进行宏替换) 2.编译&#xff08;生成汇编) 3.汇编&#xff08;生成机器可识别代码&#xff09;…...

PointNet++改进策略 :模块改进 | EdgeConv | DGCNN, 动态图卷积在3d任务上应用

目录 介绍核心思想及其实现核心思想实现步骤 如何改进PointNet**局部几何结构的处理****动态图的引入****特征聚合的灵活性****全局和局部特征的结合** 论文题目&#xff1a;Dynamic Graph CNN for Learning on Point Clouds发布期刊&#xff1a;TOG作者单位&#xff1a;麻省理…...

FFmpeg源码:skip_bits、skip_bits1、show_bits函数分析

GetBitContext结构体和其相关的函数分析&#xff1a; FFmpeg中位操作相关的源码&#xff1a;GetBitContext结构体&#xff0c;init_get_bits函数、get_bits1函数和get_bits函数分析 FFmpeg源码&#xff1a;skip_bits、skip_bits1、show_bits函数分析 一、skip_bits函数 skip…...

加密

一、加密 加密运算需要两个输入&#xff1a;密钥和明文 解密运算也需要两个输入&#xff1a;密钥和密文 密文通常看起来都是晦涩难懂、毫无逻辑的&#xff0c;所以我们一般会通过传输或者存储密文来保护私密数据&#xff0c;当然&#xff0c;这建立在一个基础上&#xff0c;…...

Kibana:如何使用魔法公式创建具有影响力的可视化效果?(第 1 部分)

作者&#xff1a;来自 Elastic Vincent du Sordet 我们将看到 Kibana Lens 编辑器中的神奇数学公式如何帮助突出显示高值。 简介 在上一篇博文《作为非设计师设计直观的 Kibana 仪表板》中&#xff0c;我们强调了创建直观仪表板的重要性。它展示了简单的更改&#xff08;分组…...

【C++】多态and多态原理

目录 一、多态的概念 二、多态的定义及实现 &#x1f31f;多态的构成条件 &#x1f31f;虚函数 &#x1f31f;虚函数的重写 &#x1f320;小贴士&#xff1a; &#x1f31f;C11 override 和 final &#x1f31f;重载、重写&#xff08;覆盖&#xff09;、重定义&#xf…...

C# 实现二维数据数组导出到 Excel

目录 功能需求 范例运行环境 Excel DCOM 配置 设计实现 组件库引入 ​编辑​ 方法设计 生成二维数据数组 核心方法实现 调用示例 总结 功能需求 将数据库查询出来的数据导出并生成 Excel 文件&#xff0c;是项目中经常使用的一项功能。本文将介绍通过数据集生成二维…...

nlohmann::json中有中文时调用dump转string抛出异常的问题

问题描述 Winodows下C开发想使用一个json库&#xff0c;使用的nlohmann::json&#xff0c;但是遇到json中使用中文时&#xff0c;转成string&#xff0c;会抛出异常。 nlohmann::json contentJson;contentJson["chinese"] "哈哈哈";std::string test con…...

Unity中InputField一些属性的理解

先看代码&#xff1a; using UnityEngine; using UnityEngine.UI;public class TestInput : MonoBehaviour {[SerializeField]InputField inputField;void Start(){Debug.Log(inputField.text);Debug.Log(inputField.text.Length);Debug.Log(inputField.preferredWidth);Debug…...

【webpack4系列】webpack构建速度和体积优化策略(五)

文章目录 速度分析&#xff1a;使用 speed-measure-webpack-plugin体积分析&#xff1a;使用webpack-bundle-analyzer使用高版本的 webpack 和 Node.js多进程/多实例构建资源并行解析可选方案使用 HappyPack 解析资源使用 thread-loader 解析资源 多进程并行压缩代码方法一&…...

从零开始搭建 PHP

&#x1f6e0;️ 从零开始搭建 PHP 环境&#xff1a;详细教程 PHP&#xff08;Hypertext Preprocessor&#xff09;是最流行的后端脚本语言之一&#xff0c;广泛用于构建动态网站和 Web 应用程序。在开始 PHP 开发之前&#xff0c;首先需要搭建 PHP 运行环境。无论你使用的是 …...

【数据结构】8——图3,十字链表,邻接多重表

数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表 文章目录 数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表前言一、十字链表结构例子 复杂例子 二、邻接多重表&#xff08;Adjacency Multilist&#xff09;例子 前言 除了之前的邻接矩阵和邻接表 …...

eth-trunk 笔记

LACP&#xff1a;Link Aggregation Control protocol 链路聚合控制协议 将多条以太网物理链路捆绑在一起成为一条逻辑链路&#xff0c;从而实现增加链路带宽的目的。同时&#xff0c;这些捆绑在一起的链路通过相互间的动态备份&#xff0c;可以有效地提高链路的可靠性 一、配…...

通信工程学习:什么是接入网(AN)中的TF传送功能

接入网&#xff08;AN&#xff09;中的TF传送功能 在通信工程中&#xff0c;TF&#xff08;Transfer Function&#xff09;传送功能是指为接入网&#xff08;AN&#xff09;不同位置之间提供通道和传输介质&#xff0c;以实现数据的有效传输。以下是关于TF传送功能的详细解释&a…...

【JavaEE】IO基础知识及代码演示

目录 一、File 1.1 观察get系列特点差异 1.2 创建文件 1.3.1 delete()删除文件 1.3.2 deleteOnExit()删除文件 1.4 mkdir 与 mkdirs的区别 1.5 文件重命名 二、文件内容的读写----数据流 1.1 InputStream 1.1.1 使用 read() 读取文件 1.2 OutputStream 1.3 代码演示…...

linux库(AI回答)

STL POSIX关系 DeepSeek-R1 回答完成 搜索全网22篇资料 STL&#xff08;标准模板库&#xff09;和 POSIX&#xff08;可移植操作系统接口&#xff09;是两种不同领域的技术标准&#xff0c;它们在 C/C 开发中各有侧重&#xff0c;但可以协同使用。以下是它们的关系和区别&…...

深度学习登上Nature子刊!特征选择创新思路

2025深度学习发论文&模型涨点之——特征选择 特征选择作为机器学习与数据挖掘领域的核心预处理步骤&#xff0c;其重要性在当今高维数据时代日益凸显。 通过识别最具判别性的特征子集&#xff0c;特征选择算法能够有效缓解"维度灾难"、提升模型泛化能力&#x…...

打卡第39天:Dataset 和 Dataloader类

知识点回顾&#xff1a; 1.Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09; 2.Dataloader类 3.minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 import torch import torch.nn as nn import…...

力扣面试150题--课程表

Day 63 题目描述 做法 初次思路&#xff1a;本质就是将所有前置课程和后置课程作为一个有向图&#xff08;前者指向后者&#xff09;&#xff0c;判断这个图是否是一个有向无环图&#xff08;即是否存在拓扑排序&#xff09;&#xff08;本质做法是dfs&#xff09; 做法&…...

C++----剖析list

前面学习了vector和string&#xff0c;接下来剖析stl中的list&#xff0c;在数据库中学习过&#xff0c;list逻辑上是连续的&#xff0c;但是存储中是分散的&#xff0c;这是与vector这种数组类型不同的地方。所以list中的元素设置为一个结构体&#xff0c;将list设计成双向的&…...

大数据Spark(六十一):Spark基于Standalone提交任务流程

文章目录 Spark基于Standalone提交任务流程 一、Standalone-Client模式 1、提交命令 2、任务执行流程 二、Standalone-Cluster模式 1、提交命令 2、任务执行流程 Spark基于Standalone提交任务流程 在Standalone模式下&#xff0c;Spark的任务提交根据Driver程序运行的位…...

yaffs2目录搜索上下文数据结构struct yaffsfs_dirsearchcontext yaffsfs_dsc[] 详细解析

1. 目录搜索上下文&#xff08;Directory Search Context&#xff09; struct yaffsfs_dirsearchcontext 是 YAFFS2 文件系统中用于 目录遍历操作 的核心数据结构&#xff0c;专门管理 readdir() 等目录操作的状态。 结构体定义&#xff08;典型实现&#xff09; struct yaf…...

dvwa5——File Upload

LOW 在dvwa里建一个testd2.php文件&#xff0c;写入一句话木马&#xff0c;密码password antsword连接 直接上传testd2.php文件&#xff0c;上传成功 MEDIUM 查看源码&#xff0c;发现这一关只能提交jpg和png格式的文件 把testd2.php的后缀改成jpg&#xff0c;上传时用bp抓包…...

【学习记录】如何使用 Python 提取 PDF 文件中的内容

如何使用 Python 提取 PDF 文件中的内容 在文档自动化处理、数据提取和信息分析等任务中&#xff0c;从 PDF 文件中提取文本是一项常见需求。PDF 文件通常分为两种类型&#xff1a;基于文本的 PDF 和 包含扫描图像的 PDF。 本文将介绍如何使用 Python 分别提取这两种类型的 P…...

源码级拆解:如何搭建高并发「数字药店+医保购药」一体化平台?

在全民“掌上看病、线上购药”已成常态的今天&#xff0c;数字药店平台正在以惊人的速度扩张。而将数字药店与医保系统打通&#xff0c;实现线上医保购药&#xff0c;更是未来互联网医疗的关键拼图。 那么&#xff0c;如何从技术底层搭建一个 支持高并发、可扩展、安全合规的数…...