Files

128 lines
6.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// PLC 模块,负责向依赖注入容器注册 PLC 相关服务 | PLC module, responsible for registering PLC-related services to the DI container
/// </summary>
public class PLCModule : IModule
{
/// <summary>
/// 模块初始化方法 | Module initialization method
/// </summary>
/// <param name="containerProvider">容器提供者 | Container provider</param>
public void OnInitialized(IContainerProvider containerProvider)
{
// 注册模块级多语言资源到 Fallback Chain | Register module-level localization resources to Fallback Chain
var localizationService = containerProvider.Resolve<ILocalizationService>();
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<PlcService>();
var writeQueue = containerProvider.Resolve<PlcWriteQueue>();
// SignalDataService 持有的 directWriteChannel 由 DI 在构造时注入,这里再 Resolve 一个给 PlcService 管理连接
// 但 SignalDataService 已经拿到了同一个 IPlcClient 瞬态实例(不同实例),所以需要让 SignalDataService 的通道和 PlcService 管理的是同一个
// 解决方案:从 SignalDataService 获取其 directWriteChannel 引用
var signalDataService = containerProvider.Resolve<ISignalDataService>() 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<ConfigLoader>();
var plcConfig = configLoader.LoadPlcConfig();
_ = plcService.InitializeAsync(plcConfig);
System.Console.WriteLine("[PLCModule] PLC 自动连接已启动");
}
catch (Exception ex)
{
System.Console.WriteLine($"[PLCModule] PLC 自动连接异常: {ex.Message}");
}
}
/// <summary>
/// 注册类型到依赖注入容器 | Register types to the DI container
/// </summary>
/// <param name="containerRegistry">容器注册器 | Container registry</param>
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<IPlcClient, S7PlcClient>();
// 注册 PLC 服务为单例(依赖:IPlcClient, ILoggerService, XmlSignalParser
// Register PLC service as singleton (deps: IPlcClient, ILoggerService, XmlSignalParser)
containerRegistry.RegisterSingleton<PlcService>();
// 注册 IPlcService 接口映射到同一个 PlcService 单例,供跨模块使用
// Register IPlcService interface mapping to the same PlcService singleton for cross-module usage
containerRegistry.Register<IPlcService>(c => c.Resolve<PlcService>());
// 注册配置加载器(瞬态)| Register configuration loader (transient)
containerRegistry.Register<ConfigLoader>();
// 注册 PLC 测试工具窗口(瞬态)| Register PLC Test Bench Window (transient)
containerRegistry.Register<PlcTestBenchWindow>();
// 注册 XML 信号解析器(瞬态)| Register XML signal parser (transient)
containerRegistry.Register<XmlSignalParser>();
// 注册 PLC 信号地址定义编辑器窗口(瞬态)| Register PLC Address Config Editor Window (transient)
containerRegistry.Register<PlcAddrConfigEditorWindow>();
// 注册 PLC 写入队列为单例(依赖:IPlcClient, ILoggerService
// Register PLC write queue as singleton (deps: IPlcClient, ILoggerService)
containerRegistry.RegisterSingleton<PlcWriteQueue>();
// 注册信号数据交互服务为单例(依赖:PlcService, PlcWriteQueue, IPlcClient, ILoggerService
// Register signal data service as singleton (deps: PlcService, PlcWriteQueue, IPlcClient, ILoggerService)
containerRegistry.RegisterSingleton<ISignalDataService, SignalDataService>();
}
}
}