Modbus协议基础

文章目录

    • 1、Modbus协议基础知识
      • 1.1、Modbus存储范围
      • 1.2、Modbus协议功能码说明
      • 1.3、Modbus协议分类及测试
    • 2、ModbusRTU通信报文分析
      • 2.1、modbusRTU通信格式
    • 3、Modbus通信库开发
    • 4、通信库测试

1、Modbus协议基础知识

1.1、Modbus存储范围

modbus规定,每个存储区域的最大范围是65536。plc地址
西门子:MW100、DBI、DBD0,
三菱:D0、X0、Y0
绝对地址=区号+相对地址
Modbus地址=区号+(相对地址+1)
输出寄存器的第一个绝对地址是40001
地址模型:长地址模型、短地址模型
相对地址和绝对地址:说明文档用绝对地址、协议报文用相对地址。

1.2、Modbus协议功能码说明

协议的目的是传输数据,已经确定好了存储区,存储区不同的数据类型,那么必然会产生不同的行为方式,我们给每种行为制定一个代号,那么这个代号就是功能码,功能码其实就是行为的代号。

读取输入线圈、读取输出线圈、读取输入寄存器、读取输出寄存器、写入输出线圈、写入输入线圈

0x01 读取输入线圈
0x02 读取输出线圈
0x03 读取输出寄存器
0x04 读取输入寄存器
0x05 写入单个线圈
0x06 写入单个寄存器
0x0F 写入多个线圈
0x10 写入多个寄存器

1.3、Modbus协议分类及测试

报文帧 modbusRTU、modbusASCII、modbusTCP
通信介质 串口通信 232/485/422、以太网通信 TCP/IP UDP/IP
协议分类 modbusRTU协议、modbusASCII协议、modbusRTUOverTCP、modbusRTUOverTCP、modbusASCIIOverTCP、modbusASCIIOverUDP、modbusTCP协议、modbusUDP协议

2、ModbusRTU通信报文分析

2.1、modbusRTU通信格式

报文帧格式(字节):
从站地址(1):要和那个设备通信
功能码(1):要做什么
数据部分(N):读取发送:开始地址、读取数量。读取接收:字节计数、具体数据。写入单发送:字节计数、具体数据。写入多发送:开始地址、写入数量、写入数据。写入多接收:开始地址、写入数量。
校验部分(2):CRC16

校验部分: 这里的校验和串口的奇校验/偶校验是没有关系的,校验是为了保证数据的准确,校验的本质是算法。

01功能码:
发送格式:从站地址+功能码+开始线圈地址+线圈数量+CRC
接收报文格式:从站地址+功能码+字节计数+数据+CRC

读取线圈测试:读取1号站点从10开始的20个线圈的值
发送报文:01 01 00 0A 00 14 1C 07
接收报文:01 01 03 03 00 00 CC 4E

02功能码:
发送格式:从站地址+功能码+开始线圈地址+线圈数量+CRC
接收报文格式:从站地址+功能码+字节计数+数据+CRC
读取输入线圈:读取5号站点从20开始的10个线圈
发送报文 :05 02 00 14 00 0A B9 8D
接收报文:05 02 02 03 00 48 88

03功能码:
发送格式:从站地址+功能码+开始寄存器地址+寄存器数量+CRC
接收报文格式:从站地址+功能码+字节计数+数据+CRC
地区输出寄存器:读取2号站点从10开始的4个寄存器
CRC16MODBUS校验
发送报文 02 03 00 0A 00 04 64 38
接收报文 02 03 08 00 01 00 02 00 03 00 04 02 50

04功能码:
发送格式:从站地址+功能码+开始寄存器地址+寄存器数量+CRC
接收报文格式:从站地址+功能码+字节计数+数据+CRC
地区输出寄存器:读取2号站点从10开始的4个寄存器
发送报文:02 04 00 0A 00 04 64 38
接收报文:02 04 08 00 01 00 02 00 03 00 04 B3 8A

05功能码:
发送报文格式:从站地址+功能码+线圈地址+线圈的值+CRC
接收报文格式:从站地址+功能码+线圈地址+线圈的值+CRC
预置单线圈:将2号线圈点的05地址置位
发送报文格式:02 05 00 05 FF 00 9C 08
接收报文格式:02 05 00 05 FF 00 9C 08

06功能码:
发送报文格式:从站地址+功能码+寄存器地址+寄存器值+CRC
接收报文格式:从站地址+功能码+寄存器地址+寄存器值+CRC

3、Modbus通信库开发

创建windows窗体应用
在这里插入图片描述
通信库内容:
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace ModbusRTULib
{public class ModbusRTU{#region 字段和属性//串口通信对象private SerialPort serialPort;/// <summary>/// 读取超时时间/// </summary>private int ReadTimeOut { set; get; } = 2000;/// <summary>/// 串口延迟时间/// </summary>private int DelayTime { set; get; } = 10;/// <summary>/// 最大写入时间/// </summary>private int MaxWriteTime { set; get; } = 5000;/// <summary>/// 写入超时时间/// </summary>private int WriteTimeOut { set; get; } = 2000;private bool dtrEnable = false;/// <summary>/// Dtr使能标志/// </summary>private bool DtrEnable{get { return dtrEnable; }set { dtrEnable = value;this.serialPort.DtrEnable = dtrEnable;}}private bool rtsEnable = false;/// <summary>/// Rts使能标志/// </summary>private bool RtsEnable{get { return rtsEnable; }set{rtsEnable = value;this.serialPort.RtsEnable = rtsEnable;}}#endregion#region 构造方法//构造方法public ModbusRTU(){serialPort = new SerialPort();}#endregion#region 连接方法/// <summary>/// 建立连接/// </summary> n    /// <param name="portName">串口号</param>/// <param name="baudRate">波特率</param>/// <param name="parity">校验位</param>/// <param name="dataBis">数据位</param>/// <param name="stopBits">停止位</param>/// <returns></returns>public bool Connect(String portName,int baudRate=9600,Parity parity=Parity.None,int dateBits=8,StopBits stopBits = StopBits.One){//如果窗口打开就关闭if (serialPort != null && serialPort.IsOpen){serialPort.Close();}//参数设置serialPort.PortName = portName;serialPort.BaudRate = baudRate;serialPort.Parity = parity;serialPort.DataBits = dateBits;serialPort.StopBits = stopBits;serialPort.ReadTimeout = this.ReadTimeOut;serialPort.WriteTimeout = this.WriteTimeOut;try{serialPort.Open();}catch(Exception){return false;}return true;}/// <summary>/// 断开连接/// </summary>public void DisConnect(){//如果窗口打开就关闭if (serialPort != null && serialPort.IsOpen){serialPort.Close();}}#endregion#region 01H读取输出线圈/// <summary>/// 读取输出线圈/// </summary>/// <param name="slaveId">站地址</param>/// <param name="start">起始地址</param>/// <param name="length">读取长度</param>/// <returns></returns>public byte[] ReadOutputCoils(byte slaveId,ushort start,ushort length){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x01);SendCommand.Add(((byte)(start/256)));SendCommand.Add(((byte)(start%256)));SendCommand.Add((byte)(length / 256));SendCommand.Add((byte)(length % 256));SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;int byteLength = length % 8 == 0 ? length / 8 : length / 8 + 1;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 5 + byteLength && receive[2] == byteLength){if (receive[0] == slaveId && receive[1] == 0x01){//5 解析报文byte[] result = new byte[byteLength];Array.Copy(receive, 3, result, 0, byteLength);return result;}}}return null;}#endregion#region 02H读取输入线圈/// <summary>/// 读取输入线圈/// </summary>/// <param name="slaveId">站地址</param>/// <param name="start">起始地址</param>/// <param name="length">读取长度</param>/// <returns></returns>public byte[] ReadInputCoils(byte slaveId, ushort start, ushort length){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x02);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add((byte)(length / 256));SendCommand.Add((byte)(length % 256));SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;int byteLength = length % 8 == 0 ? length / 8 : length / 8 + 1;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 5 + byteLength&& receive[2] == byteLength){if (receive[0] == slaveId && receive[1] == 0x02){//5 解析报文byte[] result = new byte[byteLength];Array.Copy(receive, 3, result, 0, byteLength);return result;}}}return null;}#endregion#region 03H读取输出寄存public byte[] ReadOutputRegisters(byte slaveId,ushort start,ushort length){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x03);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add((byte)(length / 256));SendCommand.Add((byte)(length % 256));SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;int byteLength = length * 2;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 5 + byteLength){if (receive[0] == slaveId && receive[1] == 0x03 && receive[2] == byteLength){//5 解析报文byte[] result = new byte[byteLength];Array.Copy(receive, 3, result, 0, byteLength);return result;}}}return null;}#endregion#region 04H读取输如寄存public byte[] ReadInputRegisters(byte slaveId, ushort start, ushort length){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x04);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add((byte)(length / 256));SendCommand.Add((byte)(length % 256));SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;int byteLength = length * 2;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 5 + byteLength){if (receive[0] == slaveId && receive[1] == 0x04 && receive[2] == byteLength){//5 解析报文byte[] result = new byte[byteLength];Array.Copy(receive, 3, result, 0, byteLength);return result;}}}return null;}#endregion#region 05H预置单线圈public bool PreSetSingleCoil(byte slaveId, ushort start, bool value){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x05);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add(value?(byte)0xFF:(byte)0x00);SendCommand.Add(0x00);SendCommand.AddRange(CRC16(SendCommand.ToArray(),SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 8){return ByteArrayEquals(SendCommand.ToArray(), receive);}}return false;}#endregion#region 06H预置寄存器/// <summary>/// 06H预置寄存器/// </summary>/// <param name="slaveId">站地址</param>/// <param name="start">起始地址</param>/// <param name="value"></param>/// <returns></returns>public bool PreSetSingleRegister(byte slaveId, ushort start, byte[] value){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();SendCommand.Add(slaveId);SendCommand.Add(0x06);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.AddRange(value);SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 8){return ByteArrayEquals(SendCommand.ToArray(), receive);}}return false;}public bool PreSetSingleRegister(byte slaveId,ushort start,short value){return PreSetSingleRegister(slaveId, start, BitConverter.GetBytes(value).Reverse().ToArray());}public bool PreSetSingleRegister(byte slaveId, ushort start, ushort value){return PreSetSingleRegister(slaveId, start, BitConverter.GetBytes(value).Reverse().ToArray());}#endregion#region 0FH预置多线圈public bool PreSetManyCoil(byte slaveId, ushort start, bool[] value){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCList<byte> SendCommand = new List<byte>();byte[] setArray = GetByteArrayFromBoolArray(value);SendCommand.Add(slaveId);SendCommand.Add(0x0F);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add(((byte)(value.Length / 256)));SendCommand.Add(((byte)(value.Length % 256)));SendCommand.Add((byte)setArray.Length);SendCommand.AddRange(setArray);SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 8){for(int i = 0; i < 6; i++){if (SendCommand[i] != receive[i]){return false;}}return true;}}return false;}#endregion#region 10H预置多寄存器public bool PreSetMutiRegister(byte slaveId, ushort start, byte[] value){//1 拼接报文 从站地址+功能码+开始线圈地址+线圈数量+CRCif (value == null || value.Length == 0 || value.Length % 2 == 1){return false;}List<byte> SendCommand = new List<byte>();int registerLength = value.Length / 2;SendCommand.Add(slaveId);SendCommand.Add(0x10);SendCommand.Add(((byte)(start / 256)));SendCommand.Add(((byte)(start % 256)));SendCommand.Add(((byte)(registerLength / 256)));SendCommand.Add(((byte)(registerLength % 256)));SendCommand.Add(((byte)value.Length));SendCommand.AddRange(value);SendCommand.AddRange(CRC16(SendCommand.ToArray(), SendCommand.Count));//2 发送报文//3 接收报文byte[] receive = null;if (SendAndReceive(SendCommand.ToArray(), ref receive)){//4 验证报文if (CheckCRC(receive) && receive.Length == 8){for (int i = 0; i < 6; i++){if (SendCommand[i] != receive[i]){return false;}}return true;}}return false;}#endregion#region 发送和接受报文private bool SendAndReceive(byte[] send,ref byte[] receive){SimpleHybirdLock hybirdLock = new SimpleHybirdLock();//加锁hybirdLock.Enter();try{//发送报文serialPort.Write(send, 0, send.Length);//定义一个buffer缓冲区byte[] buffer = new byte[1024];//定义一个内存MemoryStream stream = new MemoryStream();//定义一个开始时间DateTime start = DateTime.Now;//为了一次性读取不完整,需要循环读取while (true){Thread.Sleep(DelayTime);//延迟一段时间再读取if (this.serialPort.BytesToRead > 0){int count = serialPort.Read(buffer, 0, buffer.Length);//读取到缓冲区stream.Write(buffer, 0, count);//缓冲区写入到内存}else{if (stream.Length > 0){break;}else if ((DateTime.Now - start).TotalMilliseconds > MaxWriteTime){return false;}}}receive = stream.ToArray();return true;}catch (Exception){return false;}finally{//解锁hybirdLock.Leave();}}#endregion#region 简单的混合锁public sealed class SimpleHybirdLock : IDisposable{private bool disposedValue = false;void Dispose(bool disposting){if (!disposedValue){if (disposedValue){}m_waiterLock.Close();disposedValue = true;}}public void Dispose(){Dispose(true);}private Int32 m_waiters = 0;private AutoResetEvent m_waiterLock = new AutoResetEvent(false);public void Enter(){if (Interlocked.Increment(ref m_waiters) == 1) return;m_waiterLock.WaitOne();}public void Leave(){if (Interlocked.Decrement(ref m_waiters) == 0) return;m_waiterLock.Set();}public bool IsWaitting => m_waiters == 0;}#endregion#region CRC校验private static readonly byte[] aucCRCHi ={0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,0x40};private static readonly byte[] aucCRCLo ={0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,0x40};private byte[] CRC16(byte[] pucFrame, int usLen){int i = 0;byte[] res = new byte[2] { 0xFF, 0xFF };ushort index;while (usLen-- > 0){index = (ushort)(res[0] ^ pucFrame[i++]);res[0] = (byte)(res[1] ^ aucCRCHi[index]);res[1] = aucCRCLo[index];}return res;}private bool CheckCRC(byte[] value){if (value == null) return false;if (value.Length <= 2) return false;int length = value.Length;byte[] buf = new byte[length - 2];Array.Copy(value, 0, buf, 0, buf.Length);byte[] CRCbuf = CRC16(buf, buf.Length);if (CRCbuf[0] == value[length - 2] && CRCbuf[1] == value[length - 1])return true;return false;}#endregion#region 数组比较方法private bool ByteArrayEquals(byte[] b1, byte[] b2){if (b1 == null || b2 == null) return false;if (b1.Length != b2.Length) return false;for(int i = 0; i < b1.Length; i++){if (b1[i] != b2[i]) return false;}return true;}#endregion#region 将布尔数组转换为字节数组private byte[] GetByteArrayFromBoolArray(bool[] value){int byteLength = value.Length % 8 == 0 ? value.Length / 8 : value.Length / 8 + 1;byte[] result = new byte[byteLength];for(int i = 0; i < result.Length; i++){//获取每个字节值int total = value.Length < 8 * (i + 1) ? value.Length - 8 * i : value.Length / 8 + 1;for(int j = 0; j < total; j++){result[i] = SetBitValue(result[i], j, value[8 * i + j]);}}return result;}private byte SetBitValue(byte src,int bit ,bool value){return value ? (byte)(src | (byte)Math.Pow(2, bit)) : (byte)(src & ~(byte)Math.Pow(2, bit));}#endregion}
}

4、通信库测试

测试页面
在这里插入图片描述

主要方法
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ModbusRTULib;
using thinger.DataConvertLib;namespace Modbus_project
{public partial class Modbus_ui : Form{public enum StoreArea{输出线圈0x,输入线圈1x,输出寄存器4x,输入寄存器3x}public Modbus_ui(){InitializeComponent();init();}//Modbus通信对象private ModbusRTU modBus = new ModbusRTU();//当前连接状态private bool isConnected = false;private DataFormat dataFormat = DataFormat.ABCD;#region 初始化/// <summary>/// 初始化参数/// </summary>private void init(){//获取本机的端口列表String[] portList = SerialPort.GetPortNames();if (portList.Length > 0){this.port_comboBox.Items.AddRange(portList);this.port_comboBox.SelectedIndex = 0;}//波特率this.rate_comboBox.Items.AddRange(new String[] { "2400", "4800", "9600", "19200", "38400" });this.rate_comboBox.SelectedIndex = 2;//校验位this.check_comboBox.DataSource = Enum.GetNames(typeof(Parity));//enum提供枚举的基类,typeof运算符用于获取某个类型的 System.Type 实例。 typeof 运算符的实参必须是类型或类型形参的名称this.check_comboBox.SelectedIndex = 0;//停止位this.stop_comboBox.DataSource = Enum.GetNames(typeof(StopBits));this.stop_comboBox.SelectedIndex = 1;//数据位this.data_comboBox.Items.AddRange(new string[] { "7", "8" });this.data_comboBox.SelectedIndex = 1;//大小端枚举this.big_comboBox.DataSource = Enum.GetNames(typeof(DataFormat));this.big_comboBox.SelectedIndex = 0;//存储区this.store_comboBox.DataSource = Enum.GetNames(typeof(StoreArea));this.big_comboBox.SelectedIndex = 0;//数据类型this.datatype_comboBox.DataSource = Enum.GetNames(typeof(DataType));this.datatype_comboBox.SelectedIndex = 0;//动态修改listview第二列宽度this.read_listView.Columns[1].Width = this.read_listView.Width - this.read_listView.Columns[0].Width - 20;}#endregion#region 建立连接 & 断开连接private void connect_button_Click(object sender, EventArgs e){if (isConnected){AddLog(1,"modbus已经连接");return;}Parity parity = (Parity)Enum.Parse(typeof(Parity), this.check_comboBox.Text, true);StopBits stopBits = (StopBits)Enum.Parse(typeof(StopBits), this.stop_comboBox.Text, true);isConnected = modBus.Connect(this.port_comboBox.Text, Convert.ToInt32(this.rate_comboBox.Text), parity, Convert.ToInt32(this.data_comboBox.Text), stopBits);if (isConnected){AddLog(0, "modbus连接成功");}else{AddLog(2, "modbus连接失败");}}/// <summary>/// 断开连接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void close_button_Click(object sender, EventArgs e){modBus.DisConnect();isConnected = false;AddLog(0, "modbus断开连接");}#endregion#region 通用日志/// <summary>/// 通用日志方法/// </summary>/// <param name="level"></param>/// <param name="info"></param>private void AddLog(int level,string info){ListViewItem lst = new ListViewItem("  "+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), level);lst.SubItems.Add(info);//让最新的数据在上面this.read_listView.Items.Insert(0, lst);}#endregion#region 读取数据private void read_button1(object sender, EventArgs e){if (commentVerify()){byte bvId = byte.Parse(this.slave_textBox.Text.Trim());ushort start = ushort.Parse(this.start_textBox.Text.Trim());ushort length = ushort.Parse(this.length_textBox.Text.Trim());//enum提供枚举的基类DataType dataType = (DataType)Enum.Parse(typeof(DataType), this.datatype_comboBox.Text, true);StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), this.store_comboBox.Text, true);dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), this.big_comboBox.Text, true);switch (dataType){case DataType.Bool:ReadBool(storeArea, bvId, start, length);break;case DataType.Byte:ReadByte(storeArea, bvId, start, length);break;case DataType.Short:ReadShort(storeArea, bvId, start, length);break;case DataType.UShort:ReadUShort(storeArea, bvId, start, length);break;case DataType.Int:ReadInt(storeArea, bvId, start, length);break;case DataType.UInt:ReadUInt(storeArea, bvId, start, length);break;case DataType.Float:ReadFloat(storeArea, bvId, start, length);break;case DataType.Double:case DataType.Long:case DataType.ULong:case DataType.String:case DataType.ByteArray:case DataType.HexString:AddLog(1, "读取失败,暂时不支持");return;default:break;}}}#endregion#region 读取boolprivate void ReadBool(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:result = modBus.ReadOutputCoils(devId, start, length);break;case StoreArea.输入线圈1x:result = modBus.ReadInputCoils(devId, start, length);break;case StoreArea.输出寄存器4x:case StoreArea.输入寄存器3x:AddLog(1, "读取失败不支持该寄存器");return;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(BitLib.GetBitArrayFromByteArray(result, 0, length)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取byteprivate void ReadByte(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:result = modBus.ReadOutputCoils(devId, start, length);break;case StoreArea.输入线圈1x:result = modBus.ReadInputCoils(devId, start, length);break;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, length);break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, length);break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(result));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取shortprivate void ReadShort(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:AddLog(1, "读取失败,不支持该存储区");return;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, length);break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, length);break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(ShortLib.GetShortArrayFromByteArray(result, this.dataFormat)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取ushortprivate void ReadUShort(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:AddLog(1, "读取失败,不支持该存储区");return;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, length);break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, length);break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(UShortLib.GetUShortArrayFromByteArray(result, this.dataFormat)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取intprivate void ReadInt(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:AddLog(1, "读取失败,不支持该存储区");return;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, (ushort)(length * 2));break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, (ushort)(length * 2));break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(IntLib.GetIntArrayFromByteArray(result, this.dataFormat)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取uintprivate void ReadUInt(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:AddLog(1, "读取失败,不支持该存储区");return;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, (ushort)(length * 2));break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, (ushort)(length * 2));break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(UIntLib.GetUIntArrayFromByteArray(result, this.dataFormat)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 读取floatprivate void ReadFloat(StoreArea storeArea, byte devId, ushort start, ushort length){byte[] result = null;switch (storeArea){case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:AddLog(1, "读取失败,不支持该存储区");return;case StoreArea.输出寄存器4x:result = modBus.ReadOutputRegisters(devId, start, (ushort)(length * 2));break;case StoreArea.输入寄存器3x:result = modBus.ReadInputRegisters(devId, start, (ushort)(length * 2));break;}if (result != null){AddLog(0, "读取成功:" + StringLib.GetStringFromValueArray(FloatLib.GetFloatArrayFromByteArray(result, this.dataFormat)));}else{AddLog(1, "读取失败,请检查参数");}}#endregion#region 写入数据private void write_button1(object sender, EventArgs e){if (commentVerify()){byte bvId = byte.Parse(this.slave_textBox.Text.Trim());ushort start = ushort.Parse(this.start_textBox.Text.Trim());//enum提供枚举的基类DataType dataType = (DataType)Enum.Parse(typeof(DataType), this.datatype_comboBox.Text, true);StoreArea storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), this.store_comboBox.Text, true);string setValue = this.write_textBox.Text.Trim();dataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), this.big_comboBox.Text, true);switch (dataType){case DataType.Bool:WriteBool(storeArea, bvId, start, setValue);break;case DataType.Byte:WriteByte(storeArea, bvId, start, setValue);break;case DataType.Short:WriteShort(storeArea, bvId, start, setValue);break;case DataType.UShort:WriteUShort(storeArea, bvId, start, setValue);break;case DataType.Int:WriteInt(storeArea, bvId, start, setValue);break;case DataType.UInt:WriteUInt(storeArea, bvId, start, setValue);break;case DataType.Float:WriteFloat(storeArea, bvId, start, setValue);break;case DataType.Double:case DataType.Long:case DataType.ULong:case DataType.String:case DataType.ByteArray:case DataType.HexString:AddLog(1, "读取失败,暂时不支持");return;default:break;}}}#endregion#region 写入boolprivate void WriteBool(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出线圈0x:bool[] values = BitLib.GetBitArrayFromBitArrayString(setValue);if (values.Length == 1){result = modBus.PreSetSingleCoil(devId, start, values[0]);}else{result = modBus.PreSetManyCoil(devId, start, values);}break;case StoreArea.输入线圈1x:case StoreArea.输出寄存器4x:case StoreArea.输入寄存器3x:AddLog(1, "读写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功" );}else{AddLog(1, "写入失败");}}#endregion#region 写入Byteprivate void WriteByte(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromHexString(setValue));break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 写入Shortprivate void WriteShort(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:short[] values = ShortLib.GetShortArrayFromString(setValue);if (values.Length == 1){result = modBus.PreSetSingleRegister(devId, start, values[0]);}else{result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromShortArray(values));}break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 写入UShortprivate void WriteUShort(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:ushort[] values = UShortLib.GetUShortArrayFromString(setValue);if (values.Length == 1){result = modBus.PreSetSingleRegister(devId, start, values[0]);}else{result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromUShortArray(values));}break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 写入Intprivate void WriteInt(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:int[] values = IntLib.GetIntArrayFromString(setValue);result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromIntArray(values));break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 写入UIntprivate void WriteUInt(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:uint[] values = UIntLib.GetUIntArrayFromString(setValue);result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromUIntArray(values));break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 写入Floatprivate void WriteFloat(StoreArea storeArea, byte devId, ushort start, string setValue){bool result = false;switch (storeArea){case StoreArea.输出寄存器4x:float[] values = FloatLib.GetFloatArrayFromString(setValue);result = modBus.PreSetMutiRegister(devId, start, ByteArrayLib.GetByteArrayFromFloatArray(values));break;case StoreArea.输出线圈0x:case StoreArea.输入线圈1x:case StoreArea.输入寄存器3x:AddLog(1, "写入失败不支持该存储区");return;}if (result){AddLog(0, "写入成功");}else{AddLog(1, "写入失败");}}#endregion#region 通用验证方法private bool commentVerify(){if (isConnected==false){AddLog(1, "请检查连接是否正常");return false;}if(byte.TryParse(this.slave_textBox.Text,out _) == false){AddLog(1, "请检查站地址是否正常");return false;}if (ushort.TryParse(this.start_textBox.Text, out _) == false){AddLog(1, "请检查起始地址是否正常");return false;}//将数字的字符串表示形式转换为其等效的 16 位无符号整数if (ushort.TryParse(this.length_textBox.Text, out _) == false){AddLog(1, "请检查长度格式是否正常");return false;}return true;}#endregion}
}

一些注意:

  1. 过程中会用到thinger.DataConvertLib数据转换库
    在这里插入图片描述

  2. 在listview列表中,需要用imagelist来添加标签

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑想安装 Windows 11 需要开启 TPM 2.0 怎么办?

尽管 TPM 2.0 已经内置在许多新电脑中&#xff0c;但很多人并不知道如何激活这一功能&#xff0c;甚至完全忽略了它的存在。其实&#xff0c;只需简单的几步操作&#xff0c;你就能开启这项强大的安全特性&#xff0c;为你的数字生活增添一层坚固的防护屏障。无论你是普通用户还…

node 使用 Redis 缓存

缓存是什么&#xff1f; 高并发下&#xff0c;一个项目最先出问题的&#xff0c;并不是程序本身&#xff0c;而是数据库最先承受不住。 在数据库上我们可以做很多优化&#xff0c;例如优化 SQL 语句&#xff0c;优化索引&#xff0c;如果数据量大了&#xff0c;还可以分库、分表…

解决双系统开机显示gnu grub version 2.06 Minimal BASH Like Line Editing is Supported

找了好多教程都没有用&#xff0c;终于解决了&#xff01;&#xff01;我是因为ubuntu分区的时候出问题了 问题描述&#xff1a; 双系统装好&#xff0c;隔天开机找不到引导项&#xff0c;黑屏显示下列 因为我用的D盘划分出来的部分空闲空间&#xff0c;而不是全部&#xff0c…

NLP-RNN-LSTM浅析

目录 双向 LSTM&#xff08;Bi - LSTM&#xff09; 双向 LSTM&#xff08;Bi - LSTM&#xff09;原理深入讲解 代码示例&#xff08;基于 PyTorch&#xff09; LSTM 应用到双向 RNN 中 代码示例&#xff08;基于 PyTorch&#xff09; 双向 LSTM - CRF&#xff08;Conditio…

自动化之ansible(二)

一、ansible中playbook&#xff08;剧本&#xff09; 官方文档&#xff1a; Ansible playbooks — Ansible Community Documentation 1、playbook的基本结构 一个基本的playbook由以下几个主要部分组成 hosts: 定义要执行任务的主机组或主机。 become: 是否需要使用超级用户…

uni-app小程序开发 基础知识2

目标&#xff1a; 构建一个文章发表平台。 我们先来写一个静态框架。 以下是 首页初代码文章列表页代码&#xff1a; <template><view class"content"><!-- 轮播图 --><swiper class"swiper-container" autoplay"true"…

kafka-集群扩容

一. 前言&#xff1a; 随着业务增加&#xff0c;我们会面临这kafka当性能问题&#xff0c;需要进行集群扩容&#xff0c;增加broker节点。 二. 扩容说明: 增加新服务到kafka集群是很容易的(参考&#xff1a; kafka-部署安装-CSDN博客 )&#xff0c;只要为新服务分配一个独一无…

uni-app开发app时 使用uni.chooseLocation遇到的问题

问题一&#xff1a;不显示 问题二&#xff1a;选择地址列表一直在加载中 因为 uni-app 接口文档 中已经说明&#xff0c;使用腾讯的话需要开启云服务&#xff0c;具体可看官网&#xff0c;这就是为什么使用时直接不显示的原因&#xff0c;所以我使用的高德&#xff0c;但又出现…

RabbitMQ的脑裂(网络分区)问题

问题描述&#xff1a; Mnesia reports that this RabbitMQ cluster has experienced a network partition. There is a risk of losing data 一、什么是MQ脑裂&#xff1f; 网络分区 如果另一个节点在一段时间内&#xff08;默认为 60 秒&#xff09;无法与其联系&#xff0…

SQL知识体系

SQL复习 MySQL SQL介绍 SQL SQL的全拼是什么&#xff1f; SQL全拼&#xff1a;Structured Query Language&#xff0c;也叫结构化查询语言。 SQL92和SQL99有什么区别呢&#xff1f; SQL92和SQL99分别代表了92年和99年颁布的SQL标准。 在 SQL92 中采用&#xff08;&#xff…

企业商业秘密百问百答之五十三【商业秘密转让】

《企业商业秘密百问百答》是由天禾律所陈军律师团队精心编撰的成果&#xff0c;汇集了该团队律师在处理商业秘密相关的刑事和民事案件中的丰富经验。近年来&#xff0c;这份资料已通过线上和线下的方式向全国近千家企业进行了广泛宣讲&#xff0c;并获得了积极的社会反响。 其…

巧用GitHub的CICD功能免费打包部署前端项目

近年来&#xff0c;随着前端技术的发展&#xff0c;前端项目的构建和打包过程变得越来越复杂&#xff0c;占用的资源也越来越多。我有一台云服务器&#xff0c;原本打算使用Docker进行部署&#xff0c;以简化操作流程。然而&#xff0c;只要执行sudo docker-compose -f deploy/…

STM32 CubeMx配置串口收发使用DMA并调用Idle模式(二)

本篇主要结合代码落实&#xff0c;之前串口已经配置好的DMA方式。 一、首先我们把串口看成一个对象&#xff0c;它有属性、私有数据和方法&#xff1b; 每个串口都有名字属性&#xff1b;有初始化、发送、接收方法&#xff1b;还有一个私有数据&#xff08;这个私有数据是每个…

react实例与总结(二)

目录 一、脚手架基础语法(16~17) 1.1、hello react 1.2、组件样式隔离(样式模块化) 1.3、react插件 二、React Router v5 2.1、react-router-dom相关API 2.1.1、内置组件 2.1.1.1、BrowserRouter 2.1.1.2、HashRouter 2.1.1.3、Route 2.1.1.4、Redirect 2.1.1.5、L…

【并发测试】Redis并发性能测试

arthas 测试示例 Redis配置类 Slf4j Configuration public class RedisConfig {BeanJedisConnectionFactory jedisConnectionFactory() {RedisStandaloneConfiguration configuration new RedisStandaloneConfiguration();configuration.setHostName(redisHost);configuratio…

【Linux基础八】计算机体系结构(冯诺依曼和操作系统)

【Linux基础八】计算机体系结构&#xff08;冯诺依曼和操作系统&#xff09; 1.冯诺依曼体系结构2.冯诺依曼的优势3.硬件间的交流4.操作系统对硬件资源的管理 1.冯诺依曼体系结构 计算机大部分遵守冯诺依曼体系 输入设备&#xff1a;键盘、鼠标、摄像头、麦克风、磁盘、网卡…

深入解析Qt事件循环

在Qt开发中&#xff0c;QApplication::exec()这行代码是每个开发者都熟悉的“魔法咒语”。为什么GUI程序必须调用它才能响应操作&#xff1f;为何耗时操作会导致界面冻结&#xff1f;本文将以事件循环为核心&#xff0c;揭示Qt高效运转的底层逻辑&#xff0c;探讨其设计哲学与最…

Hadoop 基础原理

Hadoop 基础原理 基本介绍Hadoop 的必要性Hadoop 核心组件Hadoop 生态系统中的附加组件 HDFSHDFS 集群架构HDFS 读写流程HDFS 写流程HDFS 读流程 NameNode 持久化机制 MapReduce底层原理示例 Hadoop 是一个由 Apache 基金会开发的分布式系统基础架构&#xff0c;主要解决海量数…

视觉分析之边缘检测算法

9.1 Roberts算子 Roberts算子又称为交叉微分算法&#xff0c;是基于交叉差分的梯度算法&#xff0c;通过局部差分计算检测边缘线条。 常用来处理具有陡峭的低噪声图像&#xff0c;当图像边缘接近于正45度或负45度时&#xff0c;该算法处理效果更理想。 其缺点是对边缘的定位…

【从0做项目】Java音缘心动(1)———项目介绍设计

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;项目结果展示 一&#xff1a;音乐播放器Web网页介绍 二&#xff1a;前期准备工作&…