using System.Text.Json;
|
using System.Text;
|
|
namespace GSModbus.Config
|
{
|
/// <summary>
|
/// 配置管理器 - 负责加载、验证和保存Modbus配置
|
/// </summary>
|
public class ConfigurationManager
|
{
|
#region 常量定义
|
|
/// <summary>
|
/// 默认配置文件路径
|
/// </summary>
|
public const string DEFAULT_CONFIG_PATH = "config/modbus_config.json";
|
|
/// <summary>
|
/// 配置文件搜索模式
|
/// </summary>
|
public const string CONFIG_FILE_PATTERN = "*.json";
|
|
/// <summary>
|
/// 支持的配置文件版本
|
/// </summary>
|
public readonly string[] SUPPORTED_VERSIONS = { "1.0" };
|
|
#endregion
|
|
#region 私有字段
|
|
/// <summary>
|
/// 当前加载的配置
|
/// </summary>
|
private ModbusConfiguration? _currentConfiguration;
|
|
/// <summary>
|
/// 当前配置文件路径
|
/// </summary>
|
private string? _currentConfigPath;
|
|
/// <summary>
|
/// JSON序列化选项
|
/// </summary>
|
private readonly JsonSerializerOptions _jsonOptions;
|
|
#endregion
|
|
#region 事件定义
|
|
/// <summary>
|
/// 配置加载事件
|
/// </summary>
|
public event EventHandler<ConfigurationLoadedEventArgs>? ConfigurationLoaded;
|
|
/// <summary>
|
/// 配置错误事件
|
/// </summary>
|
public event EventHandler<ConfigurationErrorEventArgs>? ConfigurationError;
|
|
#endregion
|
|
#region 公共属性
|
|
/// <summary>
|
/// 获取当前配置
|
/// </summary>
|
public ModbusConfiguration? CurrentConfiguration => _currentConfiguration;
|
|
/// <summary>
|
/// 获取当前配置文件路径
|
/// </summary>
|
public string? CurrentConfigPath => _currentConfigPath;
|
|
/// <summary>
|
/// 获取配置是否已加载
|
/// </summary>
|
public bool IsConfigurationLoaded => _currentConfiguration != null;
|
|
#endregion
|
|
#region 构造函数
|
|
/// <summary>
|
/// 初始化配置管理器
|
/// </summary>
|
public ConfigurationManager()
|
{
|
// 配置JSON序列化选项
|
_jsonOptions = new JsonSerializerOptions
|
{
|
PropertyNamingPolicy = null, // 保持原始属性名
|
WriteIndented = true, // 格式化输出
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // 支持中文
|
AllowTrailingCommas = true, // 允许尾随逗号
|
ReadCommentHandling = JsonCommentHandling.Skip // 跳过注释
|
};
|
}
|
|
#endregion
|
|
#region 配置加载方法
|
|
/// <summary>
|
/// 加载默认配置文件
|
/// </summary>
|
/// <returns>是否加载成功</returns>
|
public async Task<bool> LoadDefaultConfigurationAsync()
|
{
|
return await LoadConfigurationAsync(DEFAULT_CONFIG_PATH);
|
}
|
|
/// <summary>
|
/// 从指定路径加载配置文件
|
/// </summary>
|
/// <param name="configPath">配置文件路径</param>
|
/// <returns>是否加载成功</returns>
|
public async Task<bool> 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<ModbusConfiguration>(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;
|
}
|
}
|
|
/// <summary>
|
/// 重新加载当前配置文件
|
/// </summary>
|
/// <returns>是否重新加载成功</returns>
|
public async Task<bool> ReloadConfigurationAsync()
|
{
|
if (string.IsNullOrEmpty(_currentConfigPath))
|
{
|
LogWarning("没有当前配置文件可以重新加载");
|
return false;
|
}
|
|
return await LoadConfigurationAsync(_currentConfigPath);
|
}
|
|
#endregion
|
|
#region 配置保存方法
|
|
/// <summary>
|
/// 保存当前配置到文件
|
/// </summary>
|
/// <param name="configPath">保存路径,如果为空则使用当前路径</param>
|
/// <returns>是否保存成功</returns>
|
public async Task<bool> 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 配置验证方法
|
|
/// <summary>
|
/// 验证配置完整性和有效性
|
/// </summary>
|
/// <param name="configuration">要验证的配置</param>
|
/// <returns>验证结果</returns>
|
public ConfigurationValidationResult ValidateConfiguration(ModbusConfiguration configuration)
|
{
|
var result = new ConfigurationValidationResult();
|
var errors = new List<string>();
|
|
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;
|
}
|
|
/// <summary>
|
/// 验证连接配置
|
/// </summary>
|
private void ValidateConnectionConfig(ConnectionConfig connection, List<string> 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");
|
}
|
}
|
|
/// <summary>
|
/// 验证通信配置
|
/// </summary>
|
private void ValidateCommunicationConfig(CommunicationConfig communication, List<string> 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");
|
}
|
}
|
|
/// <summary>
|
/// 验证地址配置
|
/// </summary>
|
private void ValidateAddressConfig(ModbusConfiguration configuration, List<string> errors)
|
{
|
// 验证输出地址
|
if (configuration.OutputAddresses.HeartbeatAddress.Address < 0)
|
{
|
errors.Add("心跳地址无效");
|
}
|
|
if (configuration.OutputAddresses.DataConfirmationAddress.Address < 0)
|
{
|
errors.Add("数据确认地址无效");
|
}
|
|
// 验证输入地址中的数据字段
|
var allFields = new List<DataField>();
|
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 工具方法
|
|
/// <summary>
|
/// 创建默认配置
|
/// </summary>
|
/// <returns>默认配置对象</returns>
|
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<string, DataField>(),
|
ProductData = new Dictionary<string, DataField>(),
|
MeasurementData = new Dictionary<string, DataField>()
|
}
|
};
|
}
|
|
/// <summary>
|
/// 获取配置文件列表
|
/// </summary>
|
/// <param name="searchDirectory">搜索目录</param>
|
/// <returns>配置文件路径列表</returns>
|
public static List<string> GetConfigurationFiles(string searchDirectory = "config")
|
{
|
var configFiles = new List<string>();
|
|
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 事件触发方法
|
|
/// <summary>
|
/// 触发配置加载事件
|
/// </summary>
|
protected virtual void OnConfigurationLoaded(ConfigurationLoadedEventArgs e)
|
{
|
ConfigurationLoaded?.Invoke(this, e);
|
}
|
|
/// <summary>
|
/// 触发配置错误事件
|
/// </summary>
|
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 事件参数类
|
|
/// <summary>
|
/// 配置加载事件参数
|
/// </summary>
|
public class ConfigurationLoadedEventArgs : EventArgs
|
{
|
public ModbusConfiguration Configuration { get; }
|
public string ConfigPath { get; }
|
|
public ConfigurationLoadedEventArgs(ModbusConfiguration configuration, string configPath)
|
{
|
Configuration = configuration;
|
ConfigPath = configPath;
|
}
|
}
|
|
/// <summary>
|
/// 配置错误事件参数
|
/// </summary>
|
public class ConfigurationErrorEventArgs : EventArgs
|
{
|
public string ErrorMessage { get; }
|
public Exception? Exception { get; }
|
|
public ConfigurationErrorEventArgs(string errorMessage, Exception? exception)
|
{
|
ErrorMessage = errorMessage;
|
Exception = exception;
|
}
|
}
|
|
#endregion
|
|
#region 验证结果类
|
|
/// <summary>
|
/// 配置验证结果
|
/// </summary>
|
public class ConfigurationValidationResult
|
{
|
/// <summary>
|
/// 验证是否通过
|
/// </summary>
|
public bool IsValid { get; set; }
|
|
/// <summary>
|
/// 验证错误列表
|
/// </summary>
|
public List<string> Errors { get; set; } = new();
|
|
/// <summary>
|
/// 验证警告列表
|
/// </summary>
|
public List<string> Warnings { get; set; } = new();
|
}
|
|
#endregion
|
}
|