using System.Text.Json; using System.Text; namespace GSModbus.Config { /// /// 配置管理器 - 负责加载、验证和保存Modbus配置 /// public class ConfigurationManager { #region 常量定义 /// /// 默认配置文件路径 /// public const string DEFAULT_CONFIG_PATH = "config/modbus_config.json"; /// /// 配置文件搜索模式 /// public const string CONFIG_FILE_PATTERN = "*.json"; /// /// 支持的配置文件版本 /// public readonly string[] SUPPORTED_VERSIONS = { "1.0" }; #endregion #region 私有字段 /// /// 当前加载的配置 /// private ModbusConfiguration? _currentConfiguration; /// /// 当前配置文件路径 /// private string? _currentConfigPath; /// /// JSON序列化选项 /// private readonly JsonSerializerOptions _jsonOptions; #endregion #region 事件定义 /// /// 配置加载事件 /// public event EventHandler? ConfigurationLoaded; /// /// 配置错误事件 /// public event EventHandler? ConfigurationError; #endregion #region 公共属性 /// /// 获取当前配置 /// public ModbusConfiguration? CurrentConfiguration => _currentConfiguration; /// /// 获取当前配置文件路径 /// public string? CurrentConfigPath => _currentConfigPath; /// /// 获取配置是否已加载 /// public bool IsConfigurationLoaded => _currentConfiguration != null; #endregion #region 构造函数 /// /// 初始化配置管理器 /// public ConfigurationManager() { // 配置JSON序列化选项 _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = null, // 保持原始属性名 WriteIndented = true, // 格式化输出 Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // 支持中文 AllowTrailingCommas = true, // 允许尾随逗号 ReadCommentHandling = JsonCommentHandling.Skip // 跳过注释 }; } #endregion #region 配置加载方法 /// /// 加载默认配置文件 /// /// 是否加载成功 public async Task LoadDefaultConfigurationAsync() { return await LoadConfigurationAsync(DEFAULT_CONFIG_PATH); } /// /// 从指定路径加载配置文件 /// /// 配置文件路径 /// 是否加载成功 public async Task LoadConfigurationAsync(string configPath) { try { // 检查文件是否存在 if (!File.Exists(configPath)) { var error = $"配置文件不存在: {configPath}"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, null)); return false; } // 读取配置文件内容 LogInfo($"正在加载配置文件: {configPath}"); var jsonContent = await File.ReadAllTextAsync(configPath, Encoding.UTF8); // 反序列化配置 var configuration = JsonSerializer.Deserialize(jsonContent, _jsonOptions); if (configuration == null) { var error = "配置文件格式无效,反序列化失败"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, null)); return false; } // 验证配置 var validationResult = ValidateConfiguration(configuration); if (!validationResult.IsValid) { var error = $"配置验证失败: {string.Join(", ", validationResult.Errors)}"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, null)); return false; } // 设置当前配置 _currentConfiguration = configuration; _currentConfigPath = configPath; LogInfo($"配置加载成功: {configuration.ProjectName}"); OnConfigurationLoaded(new ConfigurationLoadedEventArgs(configuration, configPath)); return true; } catch (JsonException jsonEx) { var error = $"JSON格式错误: {jsonEx.Message}"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, jsonEx)); return false; } catch (Exception ex) { var error = $"加载配置文件时发生错误: {ex.Message}"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, ex)); return false; } } /// /// 重新加载当前配置文件 /// /// 是否重新加载成功 public async Task ReloadConfigurationAsync() { if (string.IsNullOrEmpty(_currentConfigPath)) { LogWarning("没有当前配置文件可以重新加载"); return false; } return await LoadConfigurationAsync(_currentConfigPath); } #endregion #region 配置保存方法 /// /// 保存当前配置到文件 /// /// 保存路径,如果为空则使用当前路径 /// 是否保存成功 public async Task SaveConfigurationAsync(string? configPath = null) { if (_currentConfiguration == null) { LogError("没有配置可以保存"); return false; } var savePath = configPath ?? _currentConfigPath; if (string.IsNullOrEmpty(savePath)) { LogError("没有指定保存路径"); return false; } try { // 确保目录存在 var directory = Path.GetDirectoryName(savePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 序列化配置 var jsonContent = JsonSerializer.Serialize(_currentConfiguration, _jsonOptions); // 写入文件 await File.WriteAllTextAsync(savePath, jsonContent, Encoding.UTF8); // 更新当前路径 _currentConfigPath = savePath; LogInfo($"配置已保存到: {savePath}"); return true; } catch (Exception ex) { var error = $"保存配置文件时发生错误: {ex.Message}"; LogError(error); OnConfigurationError(new ConfigurationErrorEventArgs(error, ex)); return false; } } #endregion #region 配置验证方法 /// /// 验证配置完整性和有效性 /// /// 要验证的配置 /// 验证结果 public ConfigurationValidationResult ValidateConfiguration(ModbusConfiguration configuration) { var result = new ConfigurationValidationResult(); var errors = new List(); try { // 验证配置版本 if (string.IsNullOrEmpty(configuration.ConfigVersion)) { errors.Add("配置版本不能为空"); } else if (!SUPPORTED_VERSIONS.Contains(configuration.ConfigVersion)) { errors.Add($"不支持的配置版本: {configuration.ConfigVersion}"); } // 验证连接配置 ValidateConnectionConfig(configuration.Connection, errors); // 验证通信配置 ValidateCommunicationConfig(configuration.Communication, errors); // 验证地址配置 ValidateAddressConfig(configuration, errors); result.IsValid = errors.Count == 0; result.Errors = errors; } catch (Exception ex) { errors.Add($"验证过程中发生错误: {ex.Message}"); result.IsValid = false; result.Errors = errors; } return result; } /// /// 验证连接配置 /// private void ValidateConnectionConfig(ConnectionConfig connection, List errors) { if (string.IsNullOrEmpty(connection.IpAddress)) { errors.Add("IP地址不能为空"); } else if (!System.Net.IPAddress.TryParse(connection.IpAddress, out _)) { errors.Add($"IP地址格式无效: {connection.IpAddress}"); } if (connection.Port <= 0 || connection.Port > 65535) { errors.Add($"端口号无效: {connection.Port}"); } if (connection.ConnectionTimeoutMs <= 0) { errors.Add("连接超时时间必须大于0"); } if (connection.OperationTimeoutMs <= 0) { errors.Add("操作超时时间必须大于0"); } } /// /// 验证通信配置 /// private void ValidateCommunicationConfig(CommunicationConfig communication, List errors) { if (communication.HeartbeatIntervalMs <= 0) { errors.Add("心跳间隔必须大于0"); } if (communication.DataPollingIntervalMs <= 0) { errors.Add("数据轮询间隔必须大于0"); } if (communication.ReconnectDelayMs <= 0) { errors.Add("重连延迟必须大于0"); } if (communication.MaxRetryCount < 0) { errors.Add("最大重试次数不能小于0"); } } /// /// 验证地址配置 /// private void ValidateAddressConfig(ModbusConfiguration configuration, List errors) { // 验证输出地址 if (configuration.OutputAddresses.HeartbeatAddress.Address < 0) { errors.Add("心跳地址无效"); } if (configuration.OutputAddresses.DataConfirmationAddress.Address < 0) { errors.Add("数据确认地址无效"); } // 验证输入地址中的数据字段 var allFields = new List(); allFields.AddRange(configuration.InputAddresses.ControlSignals.Values); allFields.AddRange(configuration.InputAddresses.ProductData.Values); allFields.AddRange(configuration.InputAddresses.MeasurementData.Values); foreach (var field in allFields) { if (field.Address < 0) { errors.Add($"字段地址无效: {field.DisplayName}"); } if (field.Length <= 0) { errors.Add($"字段长度无效: {field.DisplayName}"); } if (string.IsNullOrEmpty(field.DataType)) { errors.Add($"字段数据类型不能为空: {field.DisplayName}"); } } } #endregion #region 工具方法 /// /// 创建默认配置 /// /// 默认配置对象 public static ModbusConfiguration CreateDefaultConfiguration() { return new ModbusConfiguration { ConfigVersion = "1.0", ProjectName = "GSModbus默认配置", Description = "默认的Modbus通信配置", Connection = new ConnectionConfig { IpAddress = "192.168.1.100", Port = 502, ConnectionTimeoutMs = 5000, OperationTimeoutMs = 3000 }, Communication = new CommunicationConfig { HeartbeatIntervalMs = 1000, DataPollingIntervalMs = 500, EnableAutoReconnect = true, ReconnectDelayMs = 3000, MaxRetryCount = 3 }, OutputAddresses = new OutputAddressConfig { Description = "MES发送给PLC的地址", HeartbeatAddress = new AddressField { Address = 6000, DataType = "Byte", Description = "心跳地址" }, DataConfirmationAddress = new AddressField { Address = 6001, DataType = "Byte", Description = "确认地址" } }, InputAddresses = new InputAddressConfig { Description = "从PLC读取的地址", ControlSignals = new Dictionary(), ProductData = new Dictionary(), MeasurementData = new Dictionary() } }; } /// /// 获取配置文件列表 /// /// 搜索目录 /// 配置文件路径列表 public static List GetConfigurationFiles(string searchDirectory = "config") { var configFiles = new List(); try { if (Directory.Exists(searchDirectory)) { configFiles.AddRange(Directory.GetFiles(searchDirectory, CONFIG_FILE_PATTERN, SearchOption.TopDirectoryOnly)); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"搜索配置文件时发生错误: {ex.Message}"); } return configFiles; } #endregion #region 事件触发方法 /// /// 触发配置加载事件 /// protected virtual void OnConfigurationLoaded(ConfigurationLoadedEventArgs e) { ConfigurationLoaded?.Invoke(this, e); } /// /// 触发配置错误事件 /// protected virtual void OnConfigurationError(ConfigurationErrorEventArgs e) { ConfigurationError?.Invoke(this, e); } #endregion #region 日志方法 private void LogInfo(string message) { Console.WriteLine($"[CONFIG-INFO] {DateTime.Now:HH:mm:ss} - {message}"); } private void LogWarning(string message) { Console.WriteLine($"[CONFIG-WARN] {DateTime.Now:HH:mm:ss} - {message}"); } private void LogError(string message) { Console.WriteLine($"[CONFIG-ERROR] {DateTime.Now:HH:mm:ss} - {message}"); } #endregion } #region 事件参数类 /// /// 配置加载事件参数 /// public class ConfigurationLoadedEventArgs : EventArgs { public ModbusConfiguration Configuration { get; } public string ConfigPath { get; } public ConfigurationLoadedEventArgs(ModbusConfiguration configuration, string configPath) { Configuration = configuration; ConfigPath = configPath; } } /// /// 配置错误事件参数 /// public class ConfigurationErrorEventArgs : EventArgs { public string ErrorMessage { get; } public Exception? Exception { get; } public ConfigurationErrorEventArgs(string errorMessage, Exception? exception) { ErrorMessage = errorMessage; Exception = exception; } } #endregion #region 验证结果类 /// /// 配置验证结果 /// public class ConfigurationValidationResult { /// /// 验证是否通过 /// public bool IsValid { get; set; } /// /// 验证错误列表 /// public List Errors { get; set; } = new(); /// /// 验证警告列表 /// public List Warnings { get; set; } = new(); } #endregion }