下面将展示如何使用建造者模式来实现一个支持多种 Modbus RTU 设备数据采集的系统。

1. 定义 Modbus 采集配置产品类

c#
// Modbus采集配置类
public class ModbusDeviceConfiguration
{
    public string DeviceName { get; set; }
    public string DeviceType { get; set; }
    public byte SlaveId { get; set; }
    public string ComPort { get; set; }
    public int BaudRate { get; set; }
    public Parity Parity { get; set; }
    public int DataBits { get; set; }
    public StopBits StopBits { get; set; }
    public Dictionary<string, RegisterInfo> RegisterMap { get; set; }
    public int PollingInterval { get; set; }
}

// 寄存器信息类
public class RegisterInfo
{
    public ushort Address { get; }
    public RegisterType Type { get; }
    public string Description { get; }
    public float ScaleFactor { get; }

    public RegisterInfo(ushort address, RegisterType type, string description, float scaleFactor = 1.0f)
    {
        Address = address;
        Type = type;
        Description = description;
        ScaleFactor = scaleFactor;
    }
}

public enum RegisterType
{
    Int16,
    UInt16,
    Int32,
    UInt32,
    Float32
}

2. 定义抽象建造者接口

c#
// Modbus设备配置建造者接口
public interface IModbusDeviceBuilder
{
    void SetDeviceInfo(string deviceName, byte slaveId);
    void SetCommunicationParameters(string comPort, int baudRate);
    void SetRegisterMap();
    void SetPollingInterval(int interval);
    ModbusDeviceConfiguration GetConfiguration();
}

3. 实现具体设备建造者

 3.1 电能表建造者

c#
public class PowerMeterBuilder : IModbusDeviceBuilder
{
    private ModbusDeviceConfiguration _config = new ModbusDeviceConfiguration();

    public void SetDeviceInfo(string deviceName, byte slaveId)
    {
        _config.DeviceName = deviceName;
        _config.DeviceType = "电能表";
        _config.SlaveId = slaveId;
    }

    public void SetCommunicationParameters(string comPort, int baudRate)
    {
        _config.ComPort = comPort;
        _config.BaudRate = baudRate;
        _config.Parity = Parity.Even;
        _config.DataBits = 8;
        _config.StopBits = StopBits.One;
    }

    public void SetRegisterMap()
    {
        _config.RegisterMap = new Dictionary<string, RegisterInfo>
        {
            { "Voltage", new RegisterInfo(0x0000, RegisterType.Float32, "电压(V)", 0.1f) },
            { "Current", new RegisterInfo(0x0002, RegisterType.Float32, "电流(A)", 0.01f) },
            { "ActivePower", new RegisterInfo(0x0004, RegisterType.Float32, "有功功率(kW)", 0.1f) },
            { "Energy", new RegisterInfo(0x0008, RegisterType.Float32, "累计电能(kWh)", 1.0f) }
        };
    }

    public void SetPollingInterval(int interval)
    {
        _config.PollingInterval = interval;
    }

    public ModbusDeviceConfiguration GetConfiguration()
    {
        return _config;
    }
}

3.2 温度传感器建造者

c#
public class TemperatureSensorBuilder : IModbusDeviceBuilder
{
    private ModbusDeviceConfiguration _config = new ModbusDeviceConfiguration();

    public void SetDeviceInfo(string deviceName, byte slaveId)
    {
        _config.DeviceName = deviceName;
        _config.DeviceType = "温度传感器";
        _config.SlaveId = slaveId;
    }

    public void SetCommunicationParameters(string comPort, int baudRate)
    {
        _config.ComPort = comPort;
        _config.BaudRate = 9600; // 温度传感器通常使用9600波特率
        _config.Parity = Parity.None;
        _config.DataBits = 8;
        _config.StopBits = StopBits.One;
    }

    public void SetRegisterMap()
    {
        _config.RegisterMap = new Dictionary<string, RegisterInfo>
        {
            { "Temperature", new RegisterInfo(0x0000, RegisterType.Int16, "温度(°C)", 0.1f) },
            { "Humidity", new RegisterInfo(0x0001, RegisterType.Int16, "湿度(%RH)", 0.1f) },
            { "Status", new RegisterInfo(0x0002, RegisterType.UInt16, "设备状态") }
        };
    }

    public void SetPollingInterval(int interval)
    {
        _config.PollingInterval = interval;
    }

    public ModbusDeviceConfiguration GetConfiguration()
    {
        return _config;
    }
}

4. 定义指挥者 (Director)

c#
public class ModbusConfigDirector
{
    private IModbusDeviceBuilder _builder;

    public ModbusConfigDirector(IModbusDeviceBuilder builder)
    {
        _builder = builder;
    }

    public void ConstructConfiguration(string deviceName, byte slaveId, string comPort, int baudRate)
    {
        _builder.SetDeviceInfo(deviceName, slaveId);
        _builder.SetCommunicationParameters(comPort, baudRate);
        _builder.SetRegisterMap();
        _builder.SetPollingInterval(5000); // 默认5秒采集间隔
    }

    public ModbusDeviceConfiguration GetConfiguration()
    {
        return _builder.GetConfiguration();
    }
}

 5. Modbus 数据采集器实现

c#
public class ModbusDataCollector : IDisposable
{
    private readonly ModbusDeviceConfiguration _config;
    private IModbusSerialMaster _master;
    private SerialPort _serialPort;
    private Timer _pollingTimer;

    public ModbusDataCollector(ModbusDeviceConfiguration config)
    {
        _config = config;
        InitializeModbusMaster();
        StartPolling();
    }

    private void InitializeModbusMaster()
    {
        _serialPort = new SerialPort(
            _config.ComPort,
            _config.BaudRate,
            _config.Parity,
            _config.DataBits,
            _config.StopBits);

        _master = ModbusSerialMaster.CreateRtu(_serialPort);
        _serialPort.Open();
    }

    private void StartPolling()
    {
        _pollingTimer = new Timer(_config.PollingInterval);
        _pollingTimer.Elapsed += async (sender, e) => await PollDataAsync();
        _pollingTimer.AutoReset = true;
        _pollingTimer.Enabled = true;
    }

    private async Task PollDataAsync()
    {
        try
        {
            foreach (var register in _config.RegisterMap)
            {
                ushort[] values = await _master.ReadHoldingRegistersAsync(
                    _config.SlaveId,
                    register.Value.Address,
                    1);

                float scaledValue = values[0] * register.Value.ScaleFactor;
                Console.WriteLine($"{DateTime.Now}: {_config.DeviceName} - {register.Value.Description}: {scaledValue}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"采集 {_config.DeviceName} 数据时出错: {ex.Message}");
        }
    }

    public void Dispose()
    {
        _pollingTimer?.Dispose();
        _master?.Dispose();
        _serialPort?.Dispose();
    }
}

6. 客户端使用示例

c#
class Program
{
    static void Main(string[] args)
    {
        // 创建电能表配置
        var powerMeterBuilder = new PowerMeterBuilder();
        var director = new ModbusConfigDirector(powerMeterBuilder);
        director.ConstructConfiguration("电表1", 1, "COM3", 9600);
        var powerMeterConfig = director.GetConfiguration();

        // 创建温度传感器配置
        var tempSensorBuilder = new TemperatureSensorBuilder();
        director = new ModbusConfigDirector(tempSensorBuilder);
        director.ConstructConfiguration("温度传感器1", 2, "COM4", 9600);
        var tempSensorConfig = director.GetConfiguration();

        // 创建数据采集器
        var collectors = new List<ModbusDataCollector>
        {
            new ModbusDataCollector(powerMeterConfig),
            new ModbusDataCollector(tempSensorConfig)
        };

        Console.WriteLine("数据采集中...");

        // 清理资源
        foreach (var collector in collectors)
        {
            collector.Dispose();
        }
    }
}

以上案例结合实际情况稍微调整一下就可以使用,本案例主要演示建造者模式在多设备采集数据方面的应用,真正应用需要结合实际情况再做实际调整。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐