using Prism.Ioc; using Prism.Modularity; using System; using System.IO; using System.Resources; using XP.Common.Localization; using XP.Common.Localization.Interfaces; using XP.Hardware.Plc.Abstractions; using XP.Hardware.Plc.Core; using XP.Hardware.Plc.Services; using XP.Hardware.PLC.Configs; using XP.Hardware.PLC.Helpers; using XP.Hardware.PLC.Services; using XP.Hardware.PLC.Views; namespace XP.Hardware.PLC { /// /// PLC 模块,负责向依赖注入容器注册 PLC 相关服务 | PLC module, responsible for registering PLC-related services to the DI container /// public class PLCModule : IModule { /// /// 模块初始化方法 | Module initialization method /// /// 容器提供者 | Container provider public void OnInitialized(IContainerProvider containerProvider) { // 注册模块级多语言资源到 Fallback Chain | Register module-level localization resources to Fallback Chain var localizationService = containerProvider.Resolve(); var resourceManager = new ResourceManager( "XP.Hardware.PLC.Resources.Resources", typeof(PLCModule).Assembly); localizationService.RegisterResourceSource("XP.Hardware.PLC", resourceManager); // 初始化 LocalizationHelper,使其通过 ILocalizationService 获取字符串(支持 Fallback Chain) // Initialize LocalizationHelper to use ILocalizationService for string lookup (supports Fallback Chain) LocalizationHelper.Initialize(localizationService); // 将写入队列和直接写入通道注册到 PlcService,由 PlcService 统一管理生命周期 // Register write queue and direct write channel to PlcService for unified lifecycle management var plcService = containerProvider.Resolve(); var writeQueue = containerProvider.Resolve(); // SignalDataService 持有的 directWriteChannel 由 DI 在构造时注入,这里再 Resolve 一个给 PlcService 管理连接 // 但 SignalDataService 已经拿到了同一个 IPlcClient 瞬态实例(不同实例),所以需要让 SignalDataService 的通道和 PlcService 管理的是同一个 // 解决方案:从 SignalDataService 获取其 directWriteChannel 引用 var signalDataService = containerProvider.Resolve() as SignalDataService; plcService.RegisterWriteComponents(writeQueue, signalDataService?.DirectWriteChannel); // 自动加载信号定义文件 | Auto-load signal definitions try { var xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PlcAddrDfn.xml"); if (File.Exists(xmlPath)) { plcService.LoadSignalDefinitions(xmlPath); System.Console.WriteLine($"[PLCModule] 信号定义已加载: {xmlPath}"); } else { System.Console.WriteLine($"[PLCModule] 警告: PlcAddrDfn.xml 未找到: {xmlPath}"); } } catch (Exception ex) { System.Console.WriteLine($"[PLCModule] 加载信号定义异常: {ex.Message}"); } // 自动连接 PLC | Auto-connect PLC try { var configLoader = containerProvider.Resolve(); var plcConfig = configLoader.LoadPlcConfig(); _ = plcService.InitializeAsync(plcConfig); System.Console.WriteLine("[PLCModule] PLC 自动连接已启动"); } catch (Exception ex) { System.Console.WriteLine($"[PLCModule] PLC 自动连接异常: {ex.Message}"); } } /// /// 注册类型到依赖注入容器 | Register types to the DI container /// /// 容器注册器 | Container registry public void RegisterTypes(IContainerRegistry containerRegistry) { // === IPlcClient 瞬态注册策略说明 | IPlcClient Transient Registration Strategy === // IPlcClient 注册为瞬态,每次 Resolve 创建独立的 S7PlcClient 实例。 // 各单例服务在首次创建时各自获取独立的 IPlcClient 实例: // - PlcService → 主通讯通道,用于周期性批量读取 | Main channel for periodic bulk reads // - PlcWriteQueue → 写入队列通道,用于顺序写入 | Write queue channel for sequential writes // - SignalDataService → 直接写入通道(Direct_Write_Channel),用于高优先级写入+回读校验 | Direct write channel for high-priority write + read-back verify // 三个通道相互独立,避免并发竞争 | Three independent channels to avoid contention containerRegistry.Register(); // 注册 PLC 服务为单例(依赖:IPlcClient, ILoggerService, XmlSignalParser) // Register PLC service as singleton (deps: IPlcClient, ILoggerService, XmlSignalParser) containerRegistry.RegisterSingleton(); // 注册 IPlcService 接口映射到同一个 PlcService 单例,供跨模块使用 // Register IPlcService interface mapping to the same PlcService singleton for cross-module usage containerRegistry.Register(c => c.Resolve()); // 注册配置加载器(瞬态)| Register configuration loader (transient) containerRegistry.Register(); // 注册 PLC 测试工具窗口(瞬态)| Register PLC Test Bench Window (transient) containerRegistry.Register(); // 注册 XML 信号解析器(瞬态)| Register XML signal parser (transient) containerRegistry.Register(); // 注册 PLC 信号地址定义编辑器窗口(瞬态)| Register PLC Address Config Editor Window (transient) containerRegistry.Register(); // 注册 PLC 写入队列为单例(依赖:IPlcClient, ILoggerService) // Register PLC write queue as singleton (deps: IPlcClient, ILoggerService) containerRegistry.RegisterSingleton(); // 注册信号数据交互服务为单例(依赖:PlcService, PlcWriteQueue, IPlcClient, ILoggerService) // Register signal data service as singleton (deps: PlcService, PlcWriteQueue, IPlcClient, ILoggerService) containerRegistry.RegisterSingleton(); } } }