基于.NET和C#构建光伏IoT物模型方案

一、目前国内接入最常见、最有代表性的 4 类光伏设备

二、华为 SUN2000 逆变器通讯报文示例

这是一个标准 Modbus TCP 请求报文

00 01 00 00 00 06 01 03 75 30 00 06

含义:

Modbus TCP 报文由两部分组成:

MBAP Header(7字节) + PDU(功能码 + 数据)

在光伏逆变器里,这类请求通常用于:

响应

这是一个标准的Modbus TCP 响应报文结构

00 01 00 00 00 0F 01 03 0C 09 C4 00 64 13 88 00 32

MBAP Header(7字节) + PDU(功能码 + 数据)

PDU数据解析

结合光伏业务的数值换算

厂家协议通常定义 倍率(Scale)

最终物模型字段赋值示例

{ "dcVoltage": 250.0, "dcCurrent": 10.0, "dcPower": 5.0, "internalTemp": 50 }

三、示例代码

报文请求构造 → 响应报文解析 → 寄存器解码 → 光伏设备物模型组织

目标:

  • 协议层、解析层、物模型层解耦

  • 方便后续做 多品牌 Adapter + 配置化映射

代码结构

1、Modbus TCP 请求报文构造

ModbusRequest.cs

auto 复制代码
public class ModbusRequest
{
    public ushort TransactionId { get; set; }
    public byte UnitId { get; set; }
    public byte FunctionCode { get; set; } = 0x03;
    public ushort StartAddress { get; set; }
    public ushort Quantity { get; set; }

    public byte[] ToBytes()
    {
        var buffer = new List<byte>();

        // Transaction ID
        buffer.AddRange(BitConverter.GetBytes(TransactionId).Reverse());

        // Protocol ID (0x0000)
        buffer.Add(0x00);
        buffer.Add(0x00);

        // Length = UnitId + PDU
        ushort length = 1 + 1 + 2 + 2;
        buffer.AddRange(BitConverter.GetBytes(length).Reverse());

        // Unit ID
        buffer.Add(UnitId);

        // PDU
        buffer.Add(FunctionCode);
        buffer.AddRange(BitConverter.GetBytes(StartAddress).Reverse());
        buffer.AddRange(BitConverter.GetBytes(Quantity).Reverse());

        return buffer.ToArray();
    }
}

2. Modbus TCP 响应解析

ModbusResponse.cs

auto 复制代码
public class ModbusResponse
{
    public ushort TransactionId { get; set; }
    public byte UnitId { get; set; }
    public byte FunctionCode { get; set; }
    public byte ByteCount { get; set; }
    public byte[] Data { get; set; }
}

ModbusParser.cs

auto 复制代码
public static class ModbusParser
{
    public static ModbusResponse ParseResponse(byte[] response)
    {
        if (response.Length < 9)
            throw new Exception("Invalid Modbus response length");

        var result = new ModbusResponse();

        result.TransactionId = ReadUInt16(response, 0);
        ushort protocolId = ReadUInt16(response, 2);
        ushort length = ReadUInt16(response, 4);

        if (protocolId != 0)
            throw new Exception("Invalid Protocol ID");

        result.UnitId = response[6];
        result.FunctionCode = response[7];
        result.ByteCount = response[8];

        result.Data = response.Skip(9).Take(result.ByteCount).ToArray();

        return result;
    }

    private static ushort ReadUInt16(byte[] buffer, int index)
    {
        return (ushort)((buffer[index] << 8) | buffer[index + 1]);
    }
}

3. 寄存器解码(倍率 + 字节序)

RegisterDecoder.cs

auto 复制代码
public static class RegisterDecoder
{
    public static ushort ReadUInt16(byte[] data, int registerIndex)
    {
        int offset = registerIndex * 2;
        return (ushort)((data[offset] << 8) | data[offset + 1]);
    }

    public static double ReadScaledValue(
        byte[] data,
        int registerIndex,
        double scale)
    {
        return ReadUInt16(data, registerIndex) / scale;
    }
}

4、光伏设备物模型定义

PvThingModel.cs

auto 复制代码
public class PvThingModel
{
    public double DcVoltage { get; set; }
    public double DcCurrent { get; set; }
    public double DcPower { get; set; }
    public double InternalTemperature { get; set; }

    public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}

5. 从响应到物模型的映射

auto 复制代码
public static class PvThingModelBuilder
{
    public static PvThingModel FromModbus(ModbusResponse response)
    {
        var data = response.Data;

        return new PvThingModel
        {
            DcVoltage = RegisterDecoder.ReadScaledValue(data, 0, 10),     // 30000
            DcCurrent = RegisterDecoder.ReadScaledValue(data, 1, 10),     // 30001
            DcPower = RegisterDecoder.ReadScaledValue(data, 2, 1000),     // 30002
            InternalTemperature = RegisterDecoder.ReadUInt16(data, 3)     // 30003
        };
    }
}

6. 完整示例调用(Demo)

auto 复制代码
public class Demo
{
    public static void Run()
    {
        // 1. 构造请求
        var request = new ModbusRequest
        {
            TransactionId = 1,
            UnitId = 1,
            StartAddress = 30000,
            Quantity = 6
        };

        byte[] requestBytes = request.ToBytes();

        // === 这里通常通过 TCP Socket 发送 requestBytes ===

        // 2. 模拟接收到的响应报文
        byte[] responseBytes =
        {
            0x00,0x01,0x00,0x00,0x00,0x0F,0x01,0x03,0x0C,
            0x09,0xC4,0x00,0x64,0x13,0x88,0x00,0x32
        };

        // 3. 解析响应
        var response = ModbusParser.ParseResponse(responseBytes);

        // 4. 构建物模型
        PvThingModel model = PvThingModelBuilder.FromModbus(response);

        Console.WriteLine($"DC Voltage: {model.DcVoltage} V");
        Console.WriteLine($"DC Current: {model.DcCurrent} A");
        Console.WriteLine($"DC Power: {model.DcPower} kW");
        Console.WriteLine($"Temperature: {model.InternalTemperature} ℃");
    }
}