---
id: adaptermodbus
title: Modbus协议
---

## 说明

本代码仅适用以下协议。

Modbus Tcp/Modbus Rtu

ModbusRtu同时支持Tcp/Udp链路

:::tip 提示

封装较多，以下代码只摘录部分，如果需要全部代码，请查看[开源地址](https://gitee.com/diego2098/ThingsGateway)

:::  

## 版权

该代码所有版权归Diego所有，使用时请务必注明。

## 代码

```csharp showLineNumbers
using ThingsGateway.Foundation.Extension.Generic;

namespace ThingsGateway.Foundation.Adapter.Modbus;

/// <summary>
/// ModbusTcpDataHandleAdapter
/// </summary>
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
{
    private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue);

    /// <summary>
    /// 检测事务标识符
    /// </summary>
    public bool IsCheckMessageId
    {
        get
        {
            return Request?.IsCheckMessageId ?? false;
        }
        set
        {
            Request.IsCheckMessageId = value;
        }
    }

    /// <inheritdoc/>
    public override byte[] PackCommand(byte[] command)
    {
        return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
    }

    /// <inheritdoc/>
    protected override ModbusTcpMessage GetInstance()
    {
        return new ModbusTcpMessage();
    }

    /// <inheritdoc/>
    protected override FilterResult UnpackResponse(ModbusTcpMessage request, byte[] send, byte[] body, byte[] response)
    {
        //理想状态检测
        var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
        if (result.IsSuccess)
        {
            request.ResultCode = result.ResultCode;
            request.Message = result.Message;
            request.Content = result.Content1;
        }
        else
        {
            request.ResultCode = result.ResultCode;
            request.Message = result.Message;
        }
        return result.Content2;

    }
}



using System.Text;

using ThingsGateway.Foundation.Extension.Bool;
using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic;

namespace ThingsGateway.Foundation.Adapter.Modbus;

internal class ModbusHelper
{
    /// <summary>
    /// 添加Crc16
    /// </summary>
    internal static byte[] AddCrc(byte[] command)
    {
        return EasyCRC16.CRC16(command);
    }

    /// <summary>
    /// 添加ModbusTcp报文头
    /// </summary>
    internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
    {
        byte[] tcp = new byte[modbus.Length + 6];
        tcp[0] = BitConverter.GetBytes(id)[1];
        tcp[1] = BitConverter.GetBytes(id)[0];
        tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
        tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
        modbus.CopyTo(tcp, 6);
        return tcp;
    }

    /// <summary>
    /// modbus地址格式说明
    /// </summary>
    /// <returns></returns>
    internal static string GetAddressDescription()
    {
        StringBuilder stringBuilder = new();
        stringBuilder.AppendLine("Modbus寄存器");
        stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
        stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
        stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
        stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
        stringBuilder.AppendLine("举例：");
        stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
        stringBuilder.AppendLine("额外格式：");
        stringBuilder.AppendLine("设备站号 ，比如40001;s=2; ，代表设备地址为2的保持寄存器第一个寄存器");
        stringBuilder.AppendLine("写入功能码 ，比如40001;w=16; ，代表保持寄存器第一个寄存器，写入值时采用0x10功能码，而不是默认的0x06功能码");
        return stringBuilder.ToString();
    }
    /// <summary>
    /// 通过错误码来获取到对应的文本消息
    /// </summary>
    internal static string GetDescriptionByErrorCode(byte code)
    {
        return code switch
        {
            1 => "不支持的功能码",
            2 => "读取寄存器越界",
            3 => "读取长度超限",
            4 => "读写异常",
            _ => "未知错误",
        };
    }

    /// <summary>
    /// 获取modbus数据区内容，返回数据需去除Crc和报文头，例如：01 03 02 00 01，发送数据需报文头
    /// </summary>
    /// <param name="send">发送数据</param>
    /// <param name="response">返回数据</param>
    /// <returns></returns>
    internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
    {
        try
        {
            if (response.Length < 3)
                return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };


            if (response[1] >= 0x80)//错误码
                return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
            if ((response.Length < response[2] + 3))
                return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };


            if (send.Length == 0)
            {
                var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
                result.Message = "接收数据正确，但主机并没有主动请求数据";
                return result;
            }
            if (send[0] != response[0])
                return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
            if (send[1] != response[1])
                return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
            return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
        }
        catch (Exception ex)
        {
            return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success };
        }
    }
    /// <summary>
    /// 去除Crc，返回modbus数据区
    /// </summary>
    /// <param name="send"></param>
    /// <param name="response"></param>
    /// <param name="crcCheck"></param>
    /// <returns></returns>
    internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
    {
        if (response.Length < 3)
            return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };

        if (crcCheck && !EasyCRC16.CheckCRC16(response))
            return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(response, ' ')) { Content2 = FilterResult.Success };
        return GetModbusData(send, response.RemoveLast(2));
    }
    /// <summary>
    /// 获取读取报文
    /// </summary>
    internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
    {
        try
        {
            ModbusAddress mAddress = new(address, station);
            return GetReadModbusCommand(mAddress, length);
        }
        catch (Exception ex)
        {
            return new OperResult<byte[]>(ex.Message);
        }
    }
    /// <summary>
    /// 获取写入布尔量报文，根据地址识别功能码
    /// </summary>
    internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
    {
        try
        {
            ModbusAddress mAddress = new(address, station);
            //功能码或实际长度
            if (values?.Length > 1 || mAddress.WriteFunction == 15)
                return GetWriteBoolModbusCommand(mAddress, values, values.Length);
            else
                return GetWriteBoolModbusCommand(address, values[0], station);
        }
        catch (Exception ex)
        {
            return new OperResult<byte[]>(ex.Message);
        }
    }

    /// <summary>
    /// 获取写入字报文，根据地址识别功能码
    /// </summary>
    internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
    {
        try
        {
            ModbusAddress mAddress = new(address, station);
            //功能码或实际长度
            if (value?.Length > 2 || mAddress.WriteFunction == 16)
                return GetWriteModbusCommand(mAddress, value);
            else
                return GetWriteOneModbusCommand(mAddress, value);

        }
        catch (Exception ex)
        {
            return new OperResult<byte[]>(ex.Message);
        }
    }
    /// <summary>
    /// 获取读取报文
    /// </summary>
    private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
    {
        byte[] array = new byte[6]
        {
        (byte) mAddress.Station,
        (byte) mAddress.ReadFunction,
        BitConverter.GetBytes(mAddress.AddressStart)[1],
        BitConverter.GetBytes(mAddress.AddressStart)[0],
        BitConverter.GetBytes(length)[1],
        BitConverter.GetBytes(length)[0]
        };

        return OperResult.CreateSuccessResult(array);
    }

    /// <summary>
    /// 获取05写入布尔量报文
    /// </summary>
    private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
    {
        try
        {
            if (address.IndexOf('.') <= 0)
            {
                ModbusAddress mAddress = new(address, station);
                return GetWriteBoolModbusCommand(mAddress, value);
            }
            return new("不支持写入字寄存器的某一位");
        }
        catch (Exception ex)
        {
            return new OperResult<byte[]>(ex.Message);
        }
    }

    /// <summary>
    /// 获取05写入布尔量报文
    /// </summary>
    private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
    {
        byte[] array = new byte[6]
        {
    (byte) mAddress.Station,
    (byte)5,
    BitConverter.GetBytes(mAddress.AddressStart)[1],
    BitConverter.GetBytes(mAddress.AddressStart)[0],
     0,
     0
        };
        if (value)
        {
            array[4] = 0xFF;
            array[5] = 0;
        }
        else
        {
            array[4] = 0;
            array[5] = 0;
        }
        return OperResult.CreateSuccessResult(array);
    }

    /// <summary>
    /// 获取15写入布尔量报文
    /// </summary>
    private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
    {
        try
        {
            byte[] numArray1 = values.BoolArrayToByte();
            byte[] numArray2 = new byte[7 + numArray1.Length];
            numArray2[0] = (byte)mAddress.Station;
            numArray2[1] = (byte)15;
            numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
            numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
            numArray2[4] = (byte)(length / 256);
            numArray2[5] = (byte)(length % 256);
            numArray2[6] = (byte)numArray1.Length;
            numArray1.CopyTo(numArray2, 7);
            return OperResult.CreateSuccessResult(numArray2);
        }
        catch (Exception ex)
        {
            return new OperResult<byte[]>(ex.Message);
        }
    }

    /// <summary>
    /// 获取16写入字报文
    /// </summary>
    private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
    {
        byte[] numArray = new byte[7 + values.Length];
        numArray[0] = (byte)mAddress.Station;
        numArray[1] = (byte)16;
        numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
        numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
        numArray[4] = (byte)(values.Length / 2 / 256);
        numArray[5] = (byte)(values.Length / 2 % 256);
        numArray[6] = (byte)values.Length;
        values.CopyTo(numArray, 7);
        return OperResult.CreateSuccessResult(numArray);

    }

    /// <summary>
    /// 获取6写入字报文
    /// </summary>
    private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
    {
        byte[] numArray = new byte[4 + values.Length];
        numArray[0] = (byte)mAddress.Station;
        numArray[1] = (byte)6;
        numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
        numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
        values.CopyTo(numArray, 4);
        return OperResult.CreateSuccessResult(numArray);
    }

}


```
