工厂模式在设备驱动中的应用

工厂模式在设备驱动中的应用
C# 上位机开发 - 工厂模式在设备驱动中的应用一篇搞定 建议收藏作者萨达大 | 标签工厂模式、设备驱动、上位机、配置化导读现场常见「同一套软件A 线 Modbus RTUB 线 Modbus TCPC 线厂家私有协议」。用工厂 配置创建IDeviceDriverUI 与业务层只依赖接口换设备不改代码。一、问题场景if-else 地狱不少项目启动类里写着if(settings.UseTcp)drivernewModbusTcpDriver(...);elseif(settings.UseRtu)drivernewModbusRtuDriver(...);elsedrivernewCustomProtocolDriver(...);窗体、服务、测试里复制粘贴三遍新增一种驱动就要改 N 处。工厂模式把「创建哪种驱动」收敛到一个工厂类 一份配置。二、统一抽象IDeviceDriver与分层架构 01 篇一致驱动层只关心连接与收发详见上一篇 HAL。此处扩展为多种实现实现类适用场景连接参数ModbusRtuDriverRS485/RS232端口名、波特率、校验ModbusTcpDriver以太网 PLCIP、端口通常 502CustomProtocolDriver扫码枪、称重仪等串口或 TCP 自定义结束符publicinterfaceIDeviceDriver:IAsyncDisposable{stringName{get;}boolIsConnected{get;}TaskConnectAsync(CancellationTokenctdefault);TaskDisconnectAsync(CancellationTokenctdefault);Taskbyte[]TransceiveAsync(byte[]request,inttimeoutMs,CancellationTokenctdefault);}三、ModbusRtuDriver 示例publicsealedclassModbusRtuDriver:IDeviceDriver{privatereadonlySerialPort_port;publicstringNameModbusRtu;publicboolIsConnected_port.IsOpen;publicModbusRtuDriver(stringportName,intbaudRate,Parityparity){_portnewSerialPort(portName,baudRate,parity,8,StopBits.One);}publicTaskConnectAsync(CancellationTokenctdefault){if(!_port.IsOpen)_port.Open();returnTask.CompletedTask;}publicTaskDisconnectAsync(CancellationTokenctdefault){if(_port.IsOpen)_port.Close();returnTask.CompletedTask;}publicasyncTaskbyte[]TransceiveAsync(byte[]request,inttimeoutMs,CancellationTokenctdefault){_port.Write(request,0,request.Length);varbufnewbyte[512];varreadawaitTask.Run(()_port.Read(buf,0,buf.Length),ct);returnbuf.AsSpan(0,read).ToArray();}publicasyncValueTaskDisposeAsync(){awaitDisconnectAsync();_port.Dispose();}}四、ModbusTcpDriver 示例publicsealedclassModbusTcpDriver:IDeviceDriver{privatereadonlystring_host;privatereadonlyint_port;privateTcpClient?_client;publicstringNameModbusTcp;publicboolIsConnected_client?.Connectedtrue;publicModbusTcpDriver(stringhost,intport502){_hosthost;_portport;}publicasyncTaskConnectAsync(CancellationTokenctdefault){_clientnewTcpClient();await_client.ConnectAsync(_host,_port,ct);}publicTaskDisconnectAsync(CancellationTokenctdefault){_client?.Close();returnTask.CompletedTask;}publicasyncTaskbyte[]TransceiveAsync(byte[]request,inttimeoutMs,CancellationTokenctdefault){if(_clientnull)thrownewInvalidOperationException(Not connected);varstream_client.GetStream();awaitstream.WriteAsync(request,ct);varbufnewbyte[512];varnawaitstream.ReadAsync(buf,ct);returnbuf.AsSpan(0,n).ToArray();}publicasyncValueTaskDisposeAsync(){awaitDisconnectAsync();_client?.Dispose();}}五、CustomProtocolDriver自定义 ASCII 协议例如设备返回ST,GS,0012.34kg\r\npublicsealedclassCustomProtocolDriver:IDeviceDriver{privatereadonlySerialPort_port;publicstringNameCustomAscii;publicboolIsConnected_port.IsOpen;publicCustomProtocolDriver(stringportName,intbaudRate){_portnewSerialPort(portName,baudRate);_port.NewLine\r\n;}publicTaskConnectAsync(CancellationTokenctdefault){if(!_port.IsOpen)_port.Open();returnTask.CompletedTask;}publicTaskDisconnectAsync(CancellationTokenctdefault){if(_port.IsOpen)_port.Close();returnTask.CompletedTask;}publicTaskbyte[]TransceiveAsync(byte[]request,inttimeoutMs,CancellationTokenctdefault){_port.Write(request,0,request.Length);varline_port.ReadLine();returnTask.FromResult(Encoding.ASCII.GetBytes(line));}publicasyncValueTaskDisposeAsync(){awaitDisconnectAsync();_port.Dispose();}}协议解析仍在协议层Driver 只保证「发命令、收一行」。六、DeviceDriverFactory配置驱动创建6.1 配置模型publicsealedclassDriverConfig{publicstringDriverType{get;set;}ModbusRtu;// ModbusRtu | ModbusTcp | CustomAsciipublicstring?PortName{get;set;}publicintBaudRate{get;set;}9600;publicstringParity{get;set;}Even;publicstring?Host{get;set;}publicintTcpPort{get;set;}502;}6.2 JSON 配置示例devices.json{LineA:{DriverType:ModbusRtu,PortName:COM3,BaudRate:9600,Parity:Even},LineB:{DriverType:ModbusTcp,Host:192.168.1.10,TcpPort:502},Scale1:{DriverType:CustomAscii,PortName:COM5,BaudRate:2400}}6.3 工厂实现publicstaticclassDeviceDriverFactory{publicstaticIDeviceDriverCreate(DriverConfigcfg){switch(cfg.DriverType){caseModbusRtu:varparityEnum.ParseParity(cfg.Parity);returnnewModbusRtuDriver(cfg.PortName!,cfg.BaudRate,parity);caseModbusTcp:returnnewModbusTcpDriver(cfg.Host!,cfg.TcpPort);caseCustomAscii:returnnewCustomProtocolDriver(cfg.PortName!,cfg.BaudRate);default:thrownewNotSupportedException($Unknown driver:{cfg.DriverType});}}publicstaticDictionarystring,IDeviceDriverCreateAll(IConfigurationconfiguration){varsectionconfiguration.GetSection(Devices);vardictnewDictionarystring,IDeviceDriver();foreach(varchildinsection.GetChildren()){varcfgchild.GetDriverConfig()??newDriverConfig();dict[child.Key]Create(cfg);}returndict;}}启动时vardriversDeviceDriverFactory.CreateAll(builder.Configuration);varlineAProtocolnewModbusProtocolClient(drivers[LineA]);varservicenewDeviceService(lineAProtocol);换 B 线设备只改devices.json里LineB的DriverType与参数零 UI 改动。七、与分层架构的配合devices.jsonDeviceDriverFactoryIDeviceDriverModbusProtocolClientDeviceServiceMainForm工厂只出现在应用启动 / DI 注册阶段业务代码不出现new ModbusRtuDriver。八、依赖注入可选进阶ASP.NET Core 风格注册services.AddSingletonIDeviceDriver(sp{varcfgsp.GetRequiredServiceIConfiguration().GetSection(Devices:LineA).GetDriverConfig()!;returnDeviceDriverFactory.Create(cfg);});services.AddSingletonIProtocolClient(spnewModbusProtocolClient(sp.GetRequiredServiceIDeviceDriver()));services.AddSingletonDeviceService();WinForms 可在Program.cs里手动组装原理相同。九、切换设备实战 checklist步骤操作1确认新设备物理连接COM/IP2修改 JSON 中DriverType与参数3若协议不同换IProtocolClient实现仍通过工厂或第二个工厂4业务寄存器地址在配置表维护不改 Form5用 Mock Driver 跑一遍回归测试十、常见问题 FAQQ1工厂和抽象工厂有什么区别本文是简单工厂按字符串创建上位机足够用。若驱动种类爆炸且族系复杂可升级为抽象工厂 产品族但维护成本更高。Q2多个设备共用一个串口网关怎么办一个ModbusRtuDriver实例多个ModbusProtocolClient共用同一 Driver 时需加互斥锁或网关 SDK 在 HAL 内排队。Q3配置放 JSON 还是数据库单机工控机 JSON 热更新已够用多产线 MES 下发可用数据库工厂读表生成DriverConfig。Q4DriverType 用字符串还是枚举配置文件用字符串可读性好工厂内switch或字典注册FuncDriverConfig, IDeviceDriver避免巨型 switch。Q5如何单元测试工厂注入临时 JSONCreate后断言Name、IsConnected为 false用 Mock 串口需接口隔离或ISerialPortWrapper。Q6和 01 篇 HalSerialPort 重复吗HalSerialPort是教学用 HALModbusRtuDriver是现场命名。可合并为一个类只要实现IDeviceDriver即可。十一、小结所有硬件差异收敛到IDeviceDriver实现类。DeviceDriverFactory JSON实现换线不换代码。UI 只依赖DeviceService不依赖具体 Driver。下一篇介绍轮询数据如何推送到界面——观察者模式与绑定。上一篇01 上位机分层架构设计下一篇03 观察者模式与数据绑定

最新新闻

日新闻

周新闻

月新闻