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
}