From 7d4386d710855440421b6bfc3bba91ac0f2dd91b Mon Sep 17 00:00:00 2001
From: root <295172551@qq.com>
Date: Sun, 24 Aug 2025 02:40:55 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84RanAPICommandHandler?=
=?UTF-8?q?=E6=9E=B6=E6=9E=84=20-=20=E5=BC=95=E5=85=A5=E6=8A=BD=E8=B1=A1?=
=?UTF-8?q?=E5=9F=BA=E7=B1=BB=E5=92=8C=E6=8E=A5=E5=8F=A3=E6=8B=86=E5=88=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 拆分IRanAPICommandHandler接口为多个专门接口
* IRanAPICommandHandler: 基础接口,包含日志配置功能
* IRanPowerControlHandler: 功率控制接口,包含发送/接收功率设置
* IRanGainControlHandler: 增益控制接口,包含发送/接收增益设置
- 新增RanAPICommandHandlerBase抽象基类
* 提供通用命令执行逻辑ExecuteCommandAsync
* 提供工具方法BuildCommandPrefix、ValidateRanEndPoint
* 统一异常处理和资源管理
- 优化RanAPICommandHandler实现类
* 继承抽象基类,实现所有接口
* 使用基类通用方法,减少重复代码
* 统一命令执行模式:await ExecuteCommandAsync(command, "操作名称")
* 统一参数验证:ValidateRanEndPoint(ranEndPoint)
- 新增功率控制功能
* SetTxPowerAsync/SetRxPowerAsync: 字典格式设置功率
* SetAllTxPowerAsync/SetAllRxPowerAsync: 数组格式批量设置功率
* 支持动态长度数组,按端口顺序排列
- 文件结构优化
* 按功能模块拆分接口文件
* 提高代码组织清晰度和可维护性
* 支持模块化依赖注入和独立测试
影响范围: RAN API命令处理器架构重构、接口设计模块化、代码复用和维护性提升
---
.../Network/IRanAPICommandHandler.cs | 85 +
.../Network/IRanGainControlHandler.cs | 42 +
.../Network/IRanPowerControlHandler.cs | 42 +
.../Network/ProtocolClientConfigFactory.cs | 5 +-
.../Services/Network/RanAPICommandHandler.cs | 564 ++
.../Models/ProtocolClientConfig.cs | 2 +
modify.md | 6545 ++++++++++++++++-
7 files changed, 7282 insertions(+), 3 deletions(-)
create mode 100644 CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs
create mode 100644 CoreAgent.Domain/Interfaces/Network/IRanGainControlHandler.cs
create mode 100644 CoreAgent.Domain/Interfaces/Network/IRanPowerControlHandler.cs
create mode 100644 CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs
diff --git a/CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs b/CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs
new file mode 100644
index 0000000..20299ef
--- /dev/null
+++ b/CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs
@@ -0,0 +1,85 @@
+using CoreAgent.Domain.Interfaces.System.Command;
+using CoreAgent.Domain.Models.Network;
+using CoreAgent.Domain.Models.System;
+
+namespace CoreAgent.Domain.Interfaces.Network;
+
+///
+/// RAN API 命令处理器基础接口
+/// 负责处理各种RAN相关的API操作命令
+///
+public interface IRanAPICommandHandler
+{
+ ///
+ /// 设置 RAN 日志配置
+ ///
+ /// RAN 端点信息
+ /// 是否启用BCCH日志
+ /// 是否成功设置配置
+ Task SetRanLogsConfigAsync(RanIPEndPoint ranEndPoint, bool enableBcch);
+}
+
+///
+/// RAN API 命令处理器抽象基类
+/// 提供通用的命令执行逻辑
+///
+public abstract class RanAPICommandHandlerBase : IRanAPICommandHandler
+{
+ protected readonly ISystemCommandExecutor _commandExecutor;
+ protected readonly AppSettings _appSettings;
+
+ protected RanAPICommandHandlerBase(ISystemCommandExecutor commandExecutor, AppSettings appSettings)
+ {
+ _commandExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor));
+ _appSettings = appSettings ?? throw new ArgumentNullException(nameof(appSettings));
+ }
+
+ ///
+ /// 执行命令的通用方法
+ ///
+ /// 要执行的命令
+ /// 操作名称,用于日志记录
+ /// 操作参数,用于日志记录
+ /// 命令执行结果
+ protected async Task ExecuteCommandAsync(string command, string operationName, object parameters = null)
+ {
+ try
+ {
+ var result = await _commandExecutor.ExecuteCommandAsync(command, new CancellationTokenSource());
+ return result;
+ }
+ catch (Exception ex)
+ {
+ // 这里可以添加通用的异常处理逻辑
+ throw new InvalidOperationException($"执行{operationName}命令时发生异常: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// 构建命令前缀
+ ///
+ /// RAN 端点信息
+ /// 命令前缀
+ protected string BuildCommandPrefix(RanIPEndPoint ranEndPoint)
+ {
+ return $"{_appSettings.WebSocketJsPath} {ranEndPoint.ComAddr}";
+ }
+
+ ///
+ /// 验证RAN端点
+ ///
+ /// RAN 端点信息
+ /// 是否有效
+ protected bool ValidateRanEndPoint(RanIPEndPoint ranEndPoint)
+ {
+ return ranEndPoint != null;
+ }
+
+ ///
+ /// 设置 RAN 日志配置
+ ///
+ /// RAN 端点信息
+ /// 是否启用BCCH日志
+ /// 是否成功设置配置
+ public abstract Task SetRanLogsConfigAsync(RanIPEndPoint ranEndPoint, bool enableBcch);
+}
diff --git a/CoreAgent.Domain/Interfaces/Network/IRanGainControlHandler.cs b/CoreAgent.Domain/Interfaces/Network/IRanGainControlHandler.cs
new file mode 100644
index 0000000..27b59df
--- /dev/null
+++ b/CoreAgent.Domain/Interfaces/Network/IRanGainControlHandler.cs
@@ -0,0 +1,42 @@
+using CoreAgent.Domain.Models.Network;
+
+namespace CoreAgent.Domain.Interfaces.Network;
+
+///
+/// RAN API 增益控制接口
+/// 负责处理发送和接收增益相关的操作
+///
+public interface IRanGainControlHandler
+{
+ ///
+ /// 设置发送增益
+ ///
+ /// RAN 端点信息
+ /// 增益设置字典,key为端口号(int),value为增益值(double)
+ /// 是否成功设置增益
+ Task SetTxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings);
+
+ ///
+ /// 设置接收增益
+ ///
+ /// RAN 端点信息
+ /// 增益设置字典,key为端口号(int),value为增益值(double)
+ /// 是否成功设置增益
+ Task SetRxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings);
+
+ ///
+ /// 设置所有端口的发送增益
+ ///
+ /// RAN 端点信息
+ /// 所有端口的增益值数组,按端口顺序排列
+ /// 是否成功设置增益
+ Task SetAllTxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues);
+
+ ///
+ /// 设置所有端口的接收增益
+ ///
+ /// RAN 端点信息
+ /// 所有端口的增益值数组,按端口顺序排列
+ /// 是否成功设置增益
+ Task SetAllRxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues);
+}
diff --git a/CoreAgent.Domain/Interfaces/Network/IRanPowerControlHandler.cs b/CoreAgent.Domain/Interfaces/Network/IRanPowerControlHandler.cs
new file mode 100644
index 0000000..2d66e6b
--- /dev/null
+++ b/CoreAgent.Domain/Interfaces/Network/IRanPowerControlHandler.cs
@@ -0,0 +1,42 @@
+using CoreAgent.Domain.Models.Network;
+
+namespace CoreAgent.Domain.Interfaces.Network;
+
+///
+/// RAN API 功率控制接口
+/// 负责处理发送和接收功率相关的操作
+///
+public interface IRanPowerControlHandler
+{
+ ///
+ /// 设置发送功率
+ ///
+ /// RAN 端点信息
+ /// 功率设置字典,key为端口号(int),value为功率值(double)
+ /// 是否成功设置功率
+ Task SetTxPowerAsync(RanIPEndPoint ranEndPoint, Dictionary powerSettings);
+
+ ///
+ /// 设置接收功率
+ ///
+ /// RAN 端点信息
+ /// 功率设置字典,key为端口号(int),value为功率值(double)
+ /// 是否成功设置功率
+ Task SetRxPowerAsync(RanIPEndPoint ranEndPoint, Dictionary powerSettings);
+
+ ///
+ /// 设置所有端口的发送功率
+ ///
+ /// RAN 端点信息
+ /// 所有端口的功率值数组,按端口顺序排列
+ /// 是否成功设置功率
+ Task SetAllTxPowerAsync(RanIPEndPoint ranEndPoint, double[] powerValues);
+
+ ///
+ /// 设置所有端口的接收功率
+ ///
+ /// RAN 端点信息
+ /// 所有端口的功率值数组,按端口顺序排列
+ /// 是否成功设置功率
+ Task SetAllRxPowerAsync(RanIPEndPoint ranEndPoint, double[] powerValues);
+}
diff --git a/CoreAgent.Infrastructure/Services/Network/ProtocolClientConfigFactory.cs b/CoreAgent.Infrastructure/Services/Network/ProtocolClientConfigFactory.cs
index 33abc39..9e1270c 100644
--- a/CoreAgent.Infrastructure/Services/Network/ProtocolClientConfigFactory.cs
+++ b/CoreAgent.Infrastructure/Services/Network/ProtocolClientConfigFactory.cs
@@ -150,8 +150,9 @@ namespace CoreAgent.Infrastructure.Services.Network
Ssl = ssl,
Logs = new ProtocolClientLogsConfig
{
- Signal = false,
- Cch = false,
+ Signal = model == "RAN" ? true : null,
+ Cch = model == "RAN" ? true : null,
+ Bcch = model == "RAN" ? false : null,
Layers = getLayers()
},
Model = model
diff --git a/CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs b/CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs
new file mode 100644
index 0000000..9912422
--- /dev/null
+++ b/CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs
@@ -0,0 +1,564 @@
+using CoreAgent.Domain.Interfaces.Network;
+using CoreAgent.Domain.Interfaces.System.Command;
+using CoreAgent.Domain.Models.Network;
+using CoreAgent.Domain.Models.System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace CoreAgent.Infrastructure.Services.Network;
+
+///
+/// RAN API 命令处理器实现
+/// 负责处理各种RAN相关的API操作命令
+///
+public class RanAPICommandHandler : RanAPICommandHandlerBase, IRanPowerControlHandler, IRanGainControlHandler
+{
+ private readonly ILogger _logger;
+
+ public RanAPICommandHandler(
+ ILogger logger,
+ ISystemCommandExecutor commandExecutor,
+ IOptions appSettings)
+ : base(commandExecutor, appSettings.Value)
+ {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ /// 设置 RAN 日志配置
+ ///
+ /// RAN 端点信息
+ /// 是否启用BCCH日志
+ /// 是否成功设置配置
+ public override async Task SetRanLogsConfigAsync(RanIPEndPoint ranEndPoint, bool enableBcch)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ try
+ {
+ var configCommand = $@"{{""message"":""config_set"",""logs"":{{""bcch"":{enableBcch.ToString().ToLower()}}}}}";
+ var command = $"{BuildCommandPrefix(ranEndPoint)} '{configCommand}'";
+
+ _logger.LogInformation("开始设置RAN日志配置,地址: {ComAddr}, 启用BCCH: {EnableBcch}", ranEndPoint.ComAddr, enableBcch);
+
+ var result = await ExecuteCommandAsync(command, "RAN日志配置设置");
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("RAN日志配置设置成功,地址: {ComAddr}, 启用BCCH: {EnableBcch}", ranEndPoint.ComAddr, enableBcch);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("RAN日志配置设置失败,地址: {ComAddr}, 启用BCCH: {EnableBcch}, 错误: {Error}",
+ ranEndPoint.ComAddr, enableBcch, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置RAN日志配置时发生异常,地址: {ComAddr}, 启用BCCH: {EnableBcch}", ranEndPoint.ComAddr, enableBcch);
+ return false;
+ }
+ }
+
+ #region 功率控制方法
+
+ ///
+ /// 设置发送功率
+ ///
+ /// RAN 端点信息
+ /// 功率设置字典,key为端口号(int),value为功率值(double)
+ /// 是否成功设置功率
+ public async Task SetTxPowerAsync(RanIPEndPoint ranEndPoint, Dictionary powerSettings)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (powerSettings == null || powerSettings.Count == 0)
+ {
+ _logger.LogWarning("功率设置字典为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var successCount = 0;
+ var totalCount = powerSettings.Count;
+
+ _logger.LogInformation("开始批量设置发送功率,地址: {ComAddr}, 端口数量: {PortCount}", ranEndPoint.ComAddr, totalCount);
+
+ foreach (var setting in powerSettings)
+ {
+ var port = setting.Key;
+ var setPower = setting.Value;
+
+ var cmd_setPower = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""tx_power"":{setPower},""tx_channel_index"":{port}}}'";
+
+ _logger.LogDebug("设置端口 {Port} 的发送功率值为 {SetPower}", port, setPower);
+
+ var result = await ExecuteCommandAsync(cmd_setPower, "发送功率设置");
+
+ if (result.IsSuccess)
+ {
+ successCount++;
+ _logger.LogDebug("端口 {Port} 发送功率设置成功,功率值: {SetPower}", port, setPower);
+ }
+ else
+ {
+ _logger.LogWarning("端口 {Port} 发送功率设置失败,功率值: {SetPower}, 错误: {Error}",
+ port, setPower, result.Error);
+ }
+ }
+
+ var allSuccess = successCount == totalCount;
+ if (allSuccess)
+ {
+ _logger.LogInformation("所有端口发送功率设置成功,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+ else
+ {
+ _logger.LogWarning("部分端口发送功率设置失败,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+
+ return allSuccess;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "批量设置发送功率时发生异常,地址: {ComAddr}", ranEndPoint.ComAddr);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置接收功率
+ ///
+ /// RAN 端点信息
+ /// 功率设置字典,key为端口号(int),value为功率值(double)
+ /// 是否成功设置功率
+ public async Task SetRxPowerAsync(RanIPEndPoint ranEndPoint, Dictionary powerSettings)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (powerSettings == null || powerSettings.Count == 0)
+ {
+ _logger.LogWarning("功率设置字典为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var successCount = 0;
+ var totalCount = powerSettings.Count;
+
+ _logger.LogInformation("开始批量设置接收功率,地址: {ComAddr}, 端口数量: {PortCount}", ranEndPoint.ComAddr, totalCount);
+
+ foreach (var setting in powerSettings)
+ {
+ var port = setting.Key;
+ var setPower = setting.Value;
+
+ var cmd_setPower = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""rx_power"":{setPower},""rx_channel_index"":{port}}}'";
+
+ _logger.LogDebug("设置端口 {Port} 的接收功率值为 {SetPower}", port, setPower);
+
+ var result = await ExecuteCommandAsync(cmd_setPower, "接收功率设置");
+
+ if (result.IsSuccess)
+ {
+ successCount++;
+ _logger.LogDebug("端口 {Port} 接收功率设置成功,功率值: {SetPower}", port, setPower);
+ }
+ else
+ {
+ _logger.LogWarning("端口 {Port} 接收功率设置失败,功率值: {SetPower}, 错误: {Error}",
+ port, setPower, result.Error);
+ }
+ }
+
+ var allSuccess = successCount == totalCount;
+ if (allSuccess)
+ {
+ _logger.LogInformation("所有端口接收功率设置成功,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+ else
+ {
+ _logger.LogWarning("部分端口接收功率设置失败,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+
+ return allSuccess;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "批量设置接收功率时发生异常,地址: {ComAddr}", ranEndPoint.ComAddr);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置所有端口的发送功率
+ ///
+ /// RAN 端点信息
+ /// 所有端口的功率值数组,按端口顺序排列
+ /// 是否成功设置功率
+ public async Task SetAllTxPowerAsync(RanIPEndPoint ranEndPoint, double[] powerValues)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (powerValues == null || powerValues.Length == 0)
+ {
+ _logger.LogWarning("功率值数组为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var powerArrayJson = $"[{string.Join(",", powerValues)}]";
+ var cmd_setAllPower = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""tx_power"":{powerArrayJson}}}'";
+
+ _logger.LogInformation("开始设置所有端口发送功率,地址: {ComAddr}, 端口数量: {PortCount}, 功率值: {PowerValues}",
+ ranEndPoint.ComAddr, powerValues.Length, string.Join(",", powerValues));
+
+ var result = await ExecuteCommandAsync(cmd_setAllPower, "所有端口发送功率设置");
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("所有端口发送功率设置成功,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, powerValues.Length);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("所有端口发送功率设置失败,地址: {ComAddr}, 端口数量: {PortCount}, 错误: {Error}",
+ ranEndPoint.ComAddr, powerValues.Length, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置所有端口发送功率时发生异常,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, powerValues?.Length ?? 0);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置所有端口的接收功率
+ ///
+ /// RAN 端点信息
+ /// 所有端口的功率值数组,按端口顺序排列
+ /// 是否成功设置功率
+ public async Task SetAllRxPowerAsync(RanIPEndPoint ranEndPoint, double[] powerValues)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (powerValues == null || powerValues.Length == 0)
+ {
+ _logger.LogWarning("功率值数组为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var powerArrayJson = $"[{string.Join(",", powerValues)}]";
+ var cmd_setAllPower = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""rx_power"":{powerArrayJson}}}'";
+
+ _logger.LogInformation("开始设置所有端口接收功率,地址: {ComAddr}, 端口数量: {PortCount}, 功率值: {PowerValues}",
+ ranEndPoint.ComAddr, powerValues.Length, string.Join(",", powerValues));
+
+ var result = await ExecuteCommandAsync(cmd_setAllPower, "所有端口接收功率设置");
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("所有端口接收功率设置成功,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, powerValues.Length);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("所有端口接收功率设置失败,地址: {ComAddr}, 端口数量: {PortCount}, 错误: {Error}",
+ ranEndPoint.ComAddr, powerValues.Length, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置所有端口接收功率时发生异常,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, powerValues?.Length ?? 0);
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region 增益控制方法
+
+ ///
+ /// 设置发送增益
+ ///
+ /// RAN 端点信息
+ /// 增益设置字典,key为端口号(int),value为增益值(double)
+ /// 是否成功设置增益
+ public async Task SetTxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (gainSettings == null || gainSettings.Count == 0)
+ {
+ _logger.LogWarning("增益设置字典为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var successCount = 0;
+ var totalCount = gainSettings.Count;
+
+ _logger.LogInformation("开始批量设置发送增益,地址: {ComAddr}, 端口数量: {PortCount}", ranEndPoint.ComAddr, totalCount);
+
+ foreach (var setting in gainSettings)
+ {
+ var port = setting.Key;
+ var setGain = setting.Value;
+
+ var cmd_setGain = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""tx_gain"":{setGain},""tx_channel_index"":{port}}}'";
+
+ _logger.LogDebug("设置端口 {Port} 的发送增益值为 {SetGain}", port, setGain);
+
+ var result = await ExecuteCommandAsync(cmd_setGain, "发送增益设置");
+
+ if (result.IsSuccess)
+ {
+ successCount++;
+ _logger.LogDebug("端口 {Port} 发送增益设置成功,增益值: {SetGain}", port, setGain);
+ }
+ else
+ {
+ _logger.LogWarning("端口 {Port} 发送增益设置失败,增益值: {SetGain}, 错误: {Error}",
+ port, setGain, result.Error);
+ }
+ }
+
+ var allSuccess = successCount == totalCount;
+ if (allSuccess)
+ {
+ _logger.LogInformation("所有端口发送增益设置成功,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+ else
+ {
+ _logger.LogWarning("部分端口发送增益设置失败,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+
+ return allSuccess;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "批量设置发送增益时发生异常,地址: {ComAddr}", ranEndPoint.ComAddr);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置接收增益
+ ///
+ /// RAN 端点信息
+ /// 增益设置字典,key为端口号(int),value为增益值(double)
+ /// 是否成功设置增益
+ public async Task SetRxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (gainSettings == null || gainSettings.Count == 0)
+ {
+ _logger.LogWarning("增益设置字典为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var successCount = 0;
+ var totalCount = gainSettings.Count;
+
+ _logger.LogInformation("开始批量设置接收增益,地址: {ComAddr}, 端口数量: {PortCount}", ranEndPoint.ComAddr, totalCount);
+
+ foreach (var setting in gainSettings)
+ {
+ var port = setting.Key;
+ var setGain = setting.Value;
+
+ var cmd_setGain = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""rx_gain"":{setGain},""rx_channel_index"":{port}}}'";
+
+ _logger.LogDebug("设置端口 {Port} 的接收增益值为 {SetGain}", port, setGain);
+
+ var result = await ExecuteCommandAsync(cmd_setGain, "接收增益设置");
+
+ if (result.IsSuccess)
+ {
+ successCount++;
+ _logger.LogDebug("端口 {Port} 接收增益设置成功,增益值: {SetGain}", port, setGain);
+ }
+ else
+ {
+ _logger.LogWarning("端口 {Port} 接收增益设置失败,增益值: {SetGain}, 错误: {Error}",
+ port, setGain, result.Error);
+ }
+ }
+
+ var allSuccess = successCount == totalCount;
+ if (allSuccess)
+ {
+ _logger.LogInformation("所有端口接收增益设置成功,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+ else
+ {
+ _logger.LogWarning("部分端口接收增益设置失败,地址: {ComAddr}, 成功数量: {SuccessCount}/{TotalCount}",
+ ranEndPoint.ComAddr, successCount, totalCount);
+ }
+
+ return allSuccess;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "批量设置接收增益时发生异常,地址: {ComAddr}", ranEndPoint.ComAddr);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置所有端口的发送增益
+ ///
+ /// RAN 端点信息
+ /// 所有端口的增益值数组,按端口顺序排列
+ /// 是否成功设置增益
+ public async Task SetAllTxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (gainValues == null || gainValues.Length == 0)
+ {
+ _logger.LogWarning("增益值数组为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var gainArrayJson = $"[{string.Join(",", gainValues)}]";
+ var cmd_setAllGain = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""tx_gain"":{gainArrayJson}}}'";
+
+ _logger.LogInformation("开始设置所有端口发送增益,地址: {ComAddr}, 端口数量: {PortCount}, 增益值: {GainValues}",
+ ranEndPoint.ComAddr, gainValues.Length, string.Join(",", gainValues));
+
+ var result = await ExecuteCommandAsync(cmd_setAllGain, "所有端口发送增益设置");
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("所有端口发送增益设置成功,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, gainValues.Length);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("所有端口发送增益设置失败,地址: {ComAddr}, 端口数量: {PortCount}, 错误: {Error}",
+ ranEndPoint.ComAddr, gainValues.Length, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置所有端口发送增益时发生异常,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, gainValues?.Length ?? 0);
+ return false;
+ }
+ }
+
+ ///
+ /// 设置所有端口的接收增益
+ ///
+ /// RAN 端点信息
+ /// 所有端口的增益值数组,按端口顺序排列
+ /// 是否成功设置增益
+ public async Task SetAllRxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues)
+ {
+ if (!ValidateRanEndPoint(ranEndPoint))
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ if (gainValues == null || gainValues.Length == 0)
+ {
+ _logger.LogWarning("增益值数组为空或无效");
+ return false;
+ }
+
+ try
+ {
+ var gainArrayJson = $"[{string.Join(",", gainValues)}]";
+ var cmd_setAllGain = $"{BuildCommandPrefix(ranEndPoint)} '{{""message"":""rf"",""rx_gain"":{gainArrayJson}}}'";
+
+ _logger.LogInformation("开始设置所有端口接收增益,地址: {ComAddr}, 端口数量: {PortCount}, 增益值: {GainValues}",
+ ranEndPoint.ComAddr, gainValues.Length, string.Join(",", gainValues));
+
+ var result = await ExecuteCommandAsync(cmd_setAllGain, "所有端口接收增益设置");
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("所有端口接收增益设置成功,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, gainValues.Length);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("所有端口接收增益设置失败,地址: {ComAddr}, 端口数量: {PortCount}, 错误: {Error}",
+ ranEndPoint.ComAddr, gainValues.Length, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置所有端口接收增益时发生异常,地址: {ComAddr}, 端口数量: {PortCount}",
+ ranEndPoint.ComAddr, gainValues?.Length ?? 0);
+ return false;
+ }
+ }
+
+ #endregion
+}
diff --git a/CoreAgent.ProtocolClient/Models/ProtocolClientConfig.cs b/CoreAgent.ProtocolClient/Models/ProtocolClientConfig.cs
index da0c20c..b3310ae 100644
--- a/CoreAgent.ProtocolClient/Models/ProtocolClientConfig.cs
+++ b/CoreAgent.ProtocolClient/Models/ProtocolClientConfig.cs
@@ -79,6 +79,8 @@ namespace CoreAgent.ProtocolClient.Models
/// 是否启用控制信道日志
///
public bool? Cch { get; set; }
+
+ public bool? Bcch { get; set; }
}
diff --git a/modify.md b/modify.md
index 53dac70..87b2d1d 100644
--- a/modify.md
+++ b/modify.md
@@ -1,5 +1,6476 @@
+## 2025-01-02
+
+### 优化RanAPICommandHandler SetTxGainAsync方法 - 使用字典结构
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs`
+- `CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs`
+
+**修改内容**:
+
+1. **接口优化**:
+ - 将 `SetTxGainAsync` 方法参数从 `(RanIPEndPoint ranEndPoint, int setGain, int port)` 改为 `(RanIPEndPoint ranEndPoint, Dictionary gainSettings)`
+ - 使用字典结构,key为端口号(int),value为增益值(double)
+ - 支持批量设置多个端口的增益值
+
+2. **实现优化**:
+ - **参数验证增强**: 添加对 `gainSettings` 字典的空值和空集合检查
+ - **批量处理**: 支持一次性设置多个端口的增益值
+ - **详细日志**: 为每个端口的设置操作添加详细的日志记录
+ - **成功统计**: 统计成功设置的端口数量,只有当所有端口都设置成功时才返回true
+ - **错误处理**: 单个端口设置失败不影响其他端口的设置
+
+3. **具体改进**:
+ ```csharp
+ // 修改前
+ public async Task SetTxGainAsync(RanIPEndPoint ranEndPoint, int setGain, int port)
+
+ // 修改后
+ public async Task SetTxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings)
+ ```
+
+4. **使用示例**:
+ ```csharp
+ // 设置单个端口
+ var singleGainSettings = new Dictionary { { 1, 10.5 } };
+ await ranAPICommandHandler.SetTxGainAsync(ranEndPoint, singleGainSettings);
+
+ // 批量设置多个端口
+ var multiGainSettings = new Dictionary
+ {
+ { 1, 10.5 },
+ { 2, 12.0 },
+ { 3, 8.5 }
+ };
+ await ranAPICommandHandler.SetTxGainAsync(ranEndPoint, multiGainSettings);
+ ```
+
+5. **设计优势**:
+ - **灵活性**: 支持单个或多个端口的增益设置
+ - **效率**: 批量处理减少方法调用次数
+ - **可扩展性**: 字典结构便于后续扩展更多参数
+ - **类型安全**: 使用强类型的字典结构
+ - **详细反馈**: 提供详细的成功/失败统计信息
+
+**影响范围**:
+- RAN API命令处理器的增益设置功能
+- 支持批量操作的网络配置管理
+- 提高系统配置效率
+
+## 2025-01-02
+
+### 添加SetRxGainAsync方法 - 实现接收增益设置功能
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs`
+- `CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs`
+
+**修改内容**:
+
+1. **接口扩展**:
+ - 在 `IRanAPICommandHandler` 接口中添加 `SetRxGainAsync` 方法
+ - 方法签名:`Task SetRxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings)`
+ - 与 `SetTxGainAsync` 方法保持一致的参数结构
+
+2. **实现方法**:
+ ```csharp
+ public async Task SetRxGainAsync(RanIPEndPoint ranEndPoint, Dictionary gainSettings)
+ {
+ // 参数验证和批量处理逻辑与SetTxGainAsync相同
+ // 命令格式:'{"message":"rf","rx_gain":{setGain},"rx_channel_index":{port}}'
+ }
+ ```
+
+3. **命令格式差异**:
+ - **发送增益**: `"tx_gain"` 和 `"tx_channel_index"`
+ - **接收增益**: `"rx_gain"` 和 `"rx_channel_index"`
+ - 其他逻辑保持一致
+
+4. **功能特点**:
+ - **批量处理**: 支持一次性设置多个端口的接收增益值
+ - **参数验证**: 检查RAN端点和增益设置字典的有效性
+ - **详细日志**: 为每个端口的设置操作提供详细的日志记录
+ - **成功统计**: 统计成功设置的端口数量
+ - **错误隔离**: 单个端口设置失败不影响其他端口
+
+5. **使用示例**:
+ ```csharp
+ // 设置单个端口的接收增益
+ var singleRxGainSettings = new Dictionary { { 1, 15.5 } };
+ await ranAPICommandHandler.SetRxGainAsync(ranEndPoint, singleRxGainSettings);
+
+ // 批量设置多个端口的接收增益
+ var multiRxGainSettings = new Dictionary
+ {
+ { 1, 15.5 },
+ { 2, 18.0 },
+ { 3, 12.5 }
+ };
+ await ranAPICommandHandler.SetRxGainAsync(ranEndPoint, multiRxGainSettings);
+ ```
+
+6. **设计一致性**:
+ - 与 `SetTxGainAsync` 方法保持相同的设计模式和错误处理逻辑
+ - 统一的日志格式和命名规范
+ - 相同的批量处理机制和成功统计方式
+
+**影响范围**:
+- RAN API命令处理器的接收增益设置功能
+- 完整的发送/接收增益配置支持
+- 网络配置管理的功能完整性
+
+## 2025-01-02
+
+### 添加SetAllTxGainAsync和SetAllRxGainAsync方法 - 支持数组格式批量设置所有端口增益
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs`
+- `CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs`
+
+**修改内容**:
+
+1. **接口扩展**:
+ - 添加 `SetAllTxGainAsync` 方法:`Task SetAllTxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues)`
+ - 添加 `SetAllRxGainAsync` 方法:`Task SetAllRxGainAsync(RanIPEndPoint ranEndPoint, double[] gainValues)`
+ - 使用数组格式,支持动态长度的端口增益设置
+
+2. **命令格式**:
+ - **发送增益**: `{"message":"rf","tx_gain":[1,2,3,4,5,6]}`
+ - **接收增益**: `{"message":"rf","rx_gain":[1,2,3,4,5,6]}`
+ - 数组长度动态,按端口顺序排列
+
+3. **实现特点**:
+ - **数组处理**: 使用 `string.Join(",", gainValues)` 将数组转换为JSON格式
+ - **单次调用**: 一次命令设置所有端口的增益值,提高效率
+ - **参数验证**: 检查数组的有效性和长度
+ - **详细日志**: 记录端口数量和具体的增益值
+
+4. **使用示例**:
+ ```csharp
+ // 设置6个端口的发送增益
+ var txGainValues = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
+ await ranAPICommandHandler.SetAllTxGainAsync(ranEndPoint, txGainValues);
+
+ // 设置4个端口的接收增益
+ var rxGainValues = new double[] { 10.5, 12.0, 8.5, 15.2 };
+ await ranAPICommandHandler.SetAllRxGainAsync(ranEndPoint, rxGainValues);
+
+ // 动态长度数组
+ var dynamicGainValues = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ await ranAPICommandHandler.SetAllTxGainAsync(ranEndPoint, dynamicGainValues);
+ ```
+
+5. **设计优势**:
+ - **高效性**: 一次命令设置所有端口,减少网络通信次数
+ - **灵活性**: 支持任意长度的端口数组
+ - **简洁性**: 数组格式比字典格式更简洁
+ - **一致性**: 与现有方法保持相同的错误处理和日志记录模式
+
+6. **命令生成逻辑**:
+ ```csharp
+ var gainArrayJson = $"[{string.Join(",", gainValues)}]";
+ var cmd_setAllGain = cmd_prefix + $@" '{{""message"":""rf"",""tx_gain"":{gainArrayJson}}}'";
+ ```
+
+**影响范围**:
+- RAN API命令处理器的批量增益设置功能
+- 支持数组格式的高效端口配置
+- 网络配置管理的性能优化
+
+## 2025-01-02
+
+### 重构RanAPICommandHandler - 引入抽象基类和接口拆分
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs`
+- `CoreAgent.Domain/Interfaces/Network/IRanPowerControlHandler.cs` (新建)
+- `CoreAgent.Domain/Interfaces/Network/IRanGainControlHandler.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs`
+
+**修改内容**:
+
+1. **接口拆分**:
+ - **IRanAPICommandHandler**: 基础接口,包含日志配置功能
+ - **IRanPowerControlHandler**: 功率控制接口,包含发送/接收功率设置
+ - **IRanGainControlHandler**: 增益控制接口,包含发送/接收增益设置
+
+2. **抽象基类设计**:
+ - **RanAPICommandHandlerBase**: 抽象基类,继承IRanAPICommandHandler
+ - 提供通用的命令执行逻辑:`ExecuteCommandAsync`
+ - 提供通用的工具方法:`BuildCommandPrefix`、`ValidateRanEndPoint`
+ - 统一的异常处理和资源管理
+
+3. **实现类优化**:
+ - **RanAPICommandHandler**: 继承抽象基类,实现所有接口
+ - 使用基类提供的通用方法,减少重复代码
+ - 统一的命令执行模式:`await ExecuteCommandAsync(command, "操作名称")`
+ - 统一的参数验证:`ValidateRanEndPoint(ranEndPoint)`
+
+4. **文件结构**:
+ ```
+ CoreAgent.Domain/Interfaces/Network/
+ ├── IRanAPICommandHandler.cs # 基础接口 + 抽象基类
+ ├── IRanPowerControlHandler.cs # 功率控制接口
+ └── IRanGainControlHandler.cs # 增益控制接口
+ ```
+
+5. **设计优势**:
+ - **单一职责**: 每个接口专注于特定功能
+ - **代码复用**: 抽象基类提供通用逻辑
+ - **易于扩展**: 可以独立实现特定接口
+ - **结构清晰**: 文件拆分使代码组织更清晰
+ - **统一模式**: 所有命令执行使用相同的模式
+
+6. **使用示例**:
+ ```csharp
+ // 依赖注入时可以注入特定接口
+ public class SomeService
+ {
+ private readonly IRanPowerControlHandler _powerHandler;
+ private readonly IRanGainControlHandler _gainHandler;
+
+ public SomeService(IRanPowerControlHandler powerHandler, IRanGainControlHandler gainHandler)
+ {
+ _powerHandler = powerHandler;
+ _gainHandler = gainHandler;
+ }
+ }
+ ```
+
+**影响范围**:
+- RAN API命令处理器的架构重构
+- 接口设计的模块化和可扩展性
+- 代码复用和维护性的提升
+
+## 2025-01-02
+
+### 日志规范修改 - GeneralCellularNetworkService和CellularNetworkContext
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+- `CoreAgent.Infrastructure/Contexts/CellularNetworkContext.cs`
+
+**修改内容**:
+
+1. **GeneralCellularNetworkService.cs 日志规范**:
+ - **第99行日志级别修正**: 将 `LogWarning` 改为 `LogInformation`,因为这是正常的初始化操作
+ - **日志格式统一**: 将字符串插值改为结构化日志格式
+ ```csharp
+ // 修改前
+ _logger.LogWarning($"初始化网络上下文 运行编码 {key}");
+
+ // 修改后
+ _logger.LogInformation("开始初始化网络上下文,运行编码: {RuntimeCode}", key);
+ ```
+ - **添加SetDeviceCode日志**: 在 `SetDeviceCode` 调用前添加日志记录
+ ```csharp
+ _logger.LogInformation("设置设备代码: {DeviceCode}", cellular.DeviceCode);
+ ```
+
+2. **CellularNetworkContext.cs 日志增强**:
+ - **Initialize方法日志**: 添加初始化和完成日志
+ ```csharp
+ _logger.LogInformation("开始初始化蜂窝网络上下文,配置键: {ConfigKey}", neConfigKey);
+ _logger.LogInformation("蜂窝网络上下文初始化完成,配置键: {ConfigKey}", neConfigKey);
+ ```
+ - **Initialize方法逻辑修复**: 移除被注释掉的 `_isInitialized` 检查,允许重新初始化以支持配置键更新
+ ```csharp
+ // 移除了以下被注释的代码,允许重新初始化
+ //if (_isInitialized)
+ //{
+ // return;
+ //}
+ ```
+ - **SetDeviceCode方法日志规范**: 将字符串插值改为结构化日志
+ ```csharp
+ // 修改前
+ _logger.LogInformation($"设备代码已设置为: {deviceCode}");
+
+ // 修改后
+ _logger.LogInformation("设备代码已设置为: {DeviceCode}", deviceCode);
+ ```
+ - **UpdateNetworkConfigType方法日志**: 添加配置类型更新日志
+ ```csharp
+ _logger.LogInformation("网络配置类型已更新: {PreviousConfigType} -> {NewConfigType}", previousConfigType, configType);
+ ```
+
+3. **修改原则**:
+ - **日志级别规范**: 正常操作使用 `LogInformation`,警告使用 `LogWarning`,错误使用 `LogError`
+ - **结构化日志**: 使用占位符格式 `{ParameterName}` 而不是字符串插值
+ - **日志完整性**: 为关键操作添加开始和完成的日志记录
+ - **日志一致性**: 保持日志格式和命名规范的一致性
+
+4. **影响范围**:
+ - 蜂窝网络启动流程的日志记录更加规范和详细
+ - 网络上下文初始化和状态变更的日志更加清晰
+ - 便于问题排查和系统监控
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StopAllClients方法移除_clients条目
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopAllClients` 方法停止所有协议客户端后,`_clients` 字典中仍然保留客户端条目
+ - 导致下次调用 `StartAllClients` 时会检查到已存在的客户端,不会创建新的 `ProtocolWsClient` 实例
+ - 需要确保每次 `StartAllClients` 都创建新的客户端实例
+
+2. **修复方案**:
+ - 在 `StopAllClients` 方法中添加 `_clients.Clear()` 调用
+ - 移除所有客户端条目,确保下次启动时创建新实例
+ - 修改返回值计算逻辑,使用 `stoppedCount` 而不是 `_clients.Count`
+ - 添加日志记录,说明已清空客户端列表
+
+3. **具体修改**:
+ ```csharp
+ // 修改前
+ var allDisconnected = disconnectedCount == _clients.Count;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+
+ // 修改后
+ // 移除所有客户端条目,确保下次StartAllClients会创建新实例
+ _clients.Clear();
+
+ var allDisconnected = disconnectedCount == stoppedCount;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}, 已清空客户端列表",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+ ```
+
+4. **设计优势**:
+ - **实例隔离**: 确保每次启动都创建新的协议客户端实例
+ - **资源清理**: 完全清理客户端引用,避免内存泄漏
+ - **状态重置**: 确保客户端管理器状态完全重置
+ - **日志完整**: 记录清空操作,便于调试和监控
+ - **逻辑正确**: 返回值计算基于实际停止的客户端数量
+
+5. **修复的关键问题**:
+ - **实例复用**: 避免复用已停止的客户端实例
+ - **状态污染**: 防止旧实例状态影响新启动的客户端
+ - **资源管理**: 确保客户端资源完全释放
+ - **启动逻辑**: 保证 `StartAllClients` 每次都创建新实例
+
+**影响范围**:
+- 协议客户端管理器的生命周期管理
+- 客户端实例的创建和销毁逻辑
+- 资源清理和内存管理
+- 网络启动流程的稳定性
+
+## 2025-01-02
+
+### WebSocketConnection.CloseAsync方法添加跟踪日志
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Services/WebSocketConnection.cs`
+
+**修改内容**:
+
+1. **为CloseAsync方法添加详细的跟踪日志信息**
+ - 在方法开始时记录关闭操作的详细信息,包括关闭状态、状态描述和当前连接状态
+ - 在WebSocket连接处于打开状态时,添加Debug级别的日志记录正常关闭流程
+ - 在关闭成功时记录成功信息,包括关闭状态和状态描述
+ - 在关闭失败时记录错误信息,包括异常详情和关闭参数
+ - 在WebSocket连接未处于打开状态时,记录警告信息并跳过关闭操作
+
+2. **具体实现**:
+ ```csharp
+ public async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("开始关闭 WebSocket 连接 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 当前连接状态: {CurrentState}",
+ closeStatus, statusDescription, _webSocket.State);
+
+ if (_webSocket.State == WebSocketState.Open)
+ {
+ _logger.LogDebug("WebSocket 连接处于打开状态,执行正常关闭流程");
+ try
+ {
+ await _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
+ _logger.LogInformation("WebSocket 连接关闭成功 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}",
+ closeStatus, statusDescription);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "WebSocket 连接关闭失败 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 错误信息: {ErrorMessage}",
+ closeStatus, statusDescription, ex.Message);
+ throw;
+ }
+ }
+ else
+ {
+ _logger.LogWarning("WebSocket 连接未处于打开状态,跳过关闭操作 - 当前状态: {CurrentState}", _webSocket.State);
+ }
+ }
+ ```
+
+3. **设计优势**:
+ - **详细跟踪**: 提供完整的关闭操作跟踪信息,便于调试和问题排查
+ - **状态监控**: 记录当前连接状态,帮助理解关闭操作的上下文
+ - **错误处理**: 完善的异常处理和错误日志记录
+ - **性能监控**: 可以跟踪关闭操作的执行情况
+ - **调试友好**: 提供足够的调试信息,便于问题定位
+ - **日志分级**: 使用不同级别的日志,避免生产环境日志过多
+
+4. **日志级别说明**:
+ - **Information**: 记录关闭操作的开始和成功完成
+ - **Debug**: 记录正常关闭流程的执行
+ - **Warning**: 记录跳过关闭操作的情况
+ - **Error**: 记录关闭失败和异常信息
+
+**影响范围**:
+- WebSocket连接关闭操作的日志记录
+- 调试和问题排查能力
+- 连接状态监控
+- 错误处理和异常跟踪
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StartAllClients方法启动和连接状态检查分离
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StartAllClients` 方法中启动客户端和检查连接状态混在一起
+ - 方法职责不够清晰,同时处理启动和连接状态检查
+ - 需要将启动和连接状态检查分离,提高代码的可读性和可维护性
+ - `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法需要适配新的接口
+
+2. **修复方案**:
+ - **分离启动和连接状态检查**:将 `StartAllClients` 方法中的连接状态检查逻辑分离出来
+ - **新增连接状态检查方法**:创建 `CheckAllClientsConnection()` 方法专门用于检查连接状态
+ - **简化启动方法**:`StartAllClients` 方法只负责启动客户端,不检查连接状态
+ - **更新接口定义**:在 `IProtocolWsClientManager` 接口中添加新的方法定义
+ - **优化返回值**:`StartAllClients` 返回是否所有客户端都成功启动,`CheckAllClientsConnection` 返回是否所有客户端都已连接
+ - **修复调用方代码**:更新 `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法以适配新的接口
+
+3. **具体修改**:
+ **StartAllClients方法优化**:
+ ```csharp
+ // 修改前 - 启动和连接状态检查混在一起
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected; // 返回连接状态
+ }
+
+ // 修改后 - 只负责启动客户端
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ var allStarted = startedCount == configs.Length;
+ return allStarted; // 返回启动状态
+ }
+ ```
+
+ **新增CheckAllClientsConnection方法**:
+ ```csharp
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ public bool CheckAllClientsConnection(int timeoutSeconds = 10)
+ {
+ ThrowIfDisposed();
+
+ if (timeoutSeconds <= 0)
+ {
+ _logger.LogWarning("超时时间必须大于0,使用默认值10秒");
+ timeoutSeconds = 10;
+ }
+
+ lock (_lock)
+ {
+ if (_clients.Count == 0)
+ {
+ _logger.LogWarning("没有运行中的协议客户端");
+ return false;
+ }
+
+ _logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
+ _clients.Count, timeoutSeconds);
+
+ var startTime = DateTime.UtcNow;
+ var timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ var connectedCount = 0;
+ var maxAttempts = 10; // 最大尝试次数
+ var attempt = 0;
+
+ while (attempt < maxAttempts)
+ {
+ attempt++;
+ connectedCount = 0;
+
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ if (client.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ _logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
+ attempt, kvp.Key, client.IsConnected);
+ }
+
+ var allConnected = connectedCount == _clients.Count;
+
+ if (allConnected)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
+ return true;
+ }
+
+ // 检查是否超时
+ if (DateTime.UtcNow - startTime > timeout)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
+ connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
+ return false;
+ }
+
+ // 等待一段时间后重试
+ if (attempt < maxAttempts)
+ {
+ var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
+ Thread.Sleep(waitTime);
+ }
+ }
+
+ var finalElapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
+
+ return false;
+ }
+ }
+ ```
+
+ **GeneralCellularNetworkService.StartAllProtocolClientsAsync方法修复**:
+ ```csharp
+ // 修改前 - 只检查启动状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+ _logger.LogInformation("所有协议客户端启动完成");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+
+ // 修改后 - 分别检查启动状态和连接状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ _logger.LogInformation("开始启动所有协议客户端");
+
+ // 获取协议客户端配置
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ if (protocolConfigs == null || protocolConfigs.Length == 0)
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
+ }
+
+ _logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
+
+ // 启动所有协议客户端
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+
+ _logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
+
+ // 检查连接状态(使用默认10秒超时)
+ var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
+ if (!connectionResult)
+ {
+ _logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
+ }
+
+ _logger.LogInformation("所有协议客户端启动并连接成功");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+ ```
+
+ **接口定义更新**:
+ ```csharp
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ ///
+ /// 启动所有协议客户端
+ ///
+ /// 协议客户端配置数组
+ /// 是否所有客户端都成功启动
+ bool StartAllClients(ProtocolClientConfig[] configs);
+
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ bool CheckAllClientsConnection(int timeoutSeconds = 10);
+
+ ///
+ /// 停止所有协议客户端
+ ///
+ /// 是否所有客户端都成功停止并断开连接
+ bool StopAllClients();
+ }
+ ```
+
+4. **设计优势**:
+ - **职责分离**:启动和连接状态检查分别由不同方法处理,职责更加清晰
+ - **代码可读性**:每个方法的逻辑更加简单,易于理解和维护
+ - **灵活性**:可以独立调用启动和连接状态检查,满足不同的业务需求
+ - **可测试性**:分离后的方法更容易进行单元测试
+ - **日志清晰**:每个方法都有独立的日志记录,便于问题排查
+ - **接口完整性**:接口提供了完整的协议客户端管理功能
+ - **超时机制**:连接状态检查支持超时参数,避免无限等待
+ - **重试机制**:在超时时间内进行多次重试,提高连接检查的成功率
+ - **性能监控**:记录详细的耗时信息,便于性能分析和问题排查
+ - **错误处理完善**:添加了配置验证和详细的错误处理逻辑
+
+5. **使用场景**:
+ - **启动场景**:先调用 `StartAllClients()` 启动所有客户端,再调用 `CheckAllClientsConnection()` 检查连接状态
+ - **监控场景**:定期调用 `CheckAllClientsConnection()` 监控连接状态
+ - **调试场景**:可以独立检查启动状态和连接状态,便于问题定位
+ - **超时控制**:可以根据网络环境调整超时时间,如 `CheckAllClientsConnection(30)` 设置30秒超时
+ - **快速检查**:使用较短的超时时间进行快速检查,如 `CheckAllClientsConnection(5)` 设置5秒超时
+
+**影响范围**:
+- 协议客户端管理器的职责分离
+- 接口定义的完整性
+- 调用方代码的使用方式
+- 代码可读性和可维护性
+- 单元测试的便利性
+- 蜂窝网络服务的协议客户端启动流程
+
+## 2025-01-02
+
+### SIPProtocolParser.GeneralParse方法严谨性修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/BuildProtocolParser/SIPProtocolParser.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - 变量名错误:`regisMccLine` 实际匹配的是MNC,`regisMncLine` 实际匹配的是MCC
+ - 空引用风险:使用了 `!` 操作符但没有进行空值检查
+ - 重复赋值:对 `log.SIP.Plmn` 进行了两次赋值,第二次会覆盖第一次
+ - 正则表达式不够严谨:缺少编译选项
+ - 返回值检查错误:`StringToId` 返回0表示空字符串,不是-1
+
+2. **修复方案**:
+ - **修复变量名错误**:将变量名与实际匹配内容对应
+ - **增强空值检查**:添加参数验证和空值检查
+ - **修复重复赋值**:分别设置PLMN和IMSI属性
+ - **优化正则表达式**:添加 `RegexOptions.Compiled` 提高性能
+ - **修复返回值检查**:使用正确的返回值判断逻辑
+ - **添加参数验证**:验证Groups数量和参数有效性
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 变量名错误
+ var regisMccLine = log!.Data.Select(line => _regMnc.Match(line))... // 实际匹配MNC
+ var regisMncLine = log!.Data.Select(line => _regMcc.Match(line))... // 实际匹配MCC
+
+ // 修复后 - 变量名正确
+ var mncValue = log.Data?.Select(line => _regMnc.Match(line))... // 匹配MNC
+ var mccValue = log.Data?.Select(line => _regMcc.Match(line))... // 匹配MCC
+
+ // 修复前 - 重复赋值
+ log.SIP.Plmn = $"{regisMncLine}{regisMccLine!.Substring(1)}";
+ log.SIP.Plmn = regisIMSILine; // 覆盖了上面的赋值
+
+ // 修复后 - 分别设置不同属性
+ if (!string.IsNullOrEmpty(mccValue) && !string.IsNullOrEmpty(mncValue))
+ {
+ log.SIP.Plmn = $"{mccValue}{mncValue}";
+ }
+ if (!string.IsNullOrEmpty(imsiValue))
+ {
+ log.SIP.IMSI = imsiValue; // 使用正确的IMSI属性
+ }
+
+ // 修复前 - 返回值检查错误
+ if (info == -1) return;
+
+ // 修复后 - 正确的返回值检查
+ if (info == 0) return; // StringToId返回0表示空字符串
+ ```
+
+4. **设计优势**:
+ - **逻辑正确性**:修复了变量名错误和重复赋值问题
+ - **空值安全**:添加了完整的空值检查和参数验证
+ - **性能优化**:正则表达式使用编译选项提高性能
+ - **代码严谨性**:添加了Groups数量验证和参数有效性检查
+ - **错误预防**:使用空条件操作符避免空引用异常
+ - **属性正确性**:使用正确的SIP属性设置PLMN和IMSI
+
+5. **修复的关键问题**:
+ - **变量命名混乱**:MCC和MNC变量名与实际匹配内容不匹配
+ - **空引用风险**:使用 `!` 操作符但没有验证对象不为空
+ - **数据覆盖**:对同一属性进行两次赋值导致数据丢失
+ - **返回值误解**:对 `StringToId` 方法返回值理解错误
+ - **正则表达式性能**:缺少编译选项影响性能
+ - **参数验证缺失**:没有验证正则匹配结果的Groups数量
+ - **日志跟踪缺失**:缺少详细的错误日志记录,难以调试问题
+
+6. **日志跟踪增强**:
+ - **添加ILogger支持**:在构造函数中创建类型安全的Logger实例
+ - **参数验证日志**:记录BuildProtocolLog参数为空的情况
+ - **StringToId失败日志**:记录StringToId返回0(空字符串)的情况
+ - **Groups数量验证日志**:记录正则匹配Groups数量不足的详细信息
+ - **性能优化**:使用私有Logger字段避免重复创建Logger实例
+
+**影响范围**:
+- SIP协议解析的准确性
+- 协议日志数据的完整性
+- 代码的稳定性和可靠性
+- 性能优化和错误预防
+- 代码可读性和维护性
+- 调试和问题排查能力显著提升
+
+### TimeStampHelper时区初始化异常修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `TimeStampHelper`类在静态初始化时抛出`System.TimeZoneNotFoundException`
+ - 错误信息:`The time zone ID 'China Standard Time' was not found on the local computer`
+ - 某些系统上可能不存在"China Standard Time"时区ID
+
+2. **修复方案**:
+ - **多时区ID支持**:尝试多种可能的时区ID,包括Windows和Linux/macOS格式
+ - **回退机制**:如果系统时区ID都不可用,创建自定义UTC+8时区
+ - **最终回退**:如果自定义时区创建失败,使用UTC时区作为最后回退
+ - **调试支持**:添加时区信息查询和验证方法
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 直接使用单一时区ID
+ private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
+
+ // 修复后 - 支持多种时区ID和回退机制
+ private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
+
+ private static TimeZoneInfo GetChinaStandardTime()
+ {
+ // 尝试多种可能的时区ID
+ string[] timeZoneIds = {
+ "China Standard Time", // Windows
+ "Asia/Shanghai", // Linux/macOS
+ "Asia/Chongqing", // 备选
+ "Asia/Harbin", // 备选
+ "Asia/Urumqi" // 备选
+ };
+
+ foreach (string timeZoneId in timeZoneIds)
+ {
+ try
+ {
+ return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
+ }
+ catch (TimeZoneNotFoundException)
+ {
+ continue;
+ }
+ }
+
+ // 创建自定义UTC+8时区
+ try
+ {
+ return TimeZoneInfo.CreateCustomTimeZone(
+ "China Standard Time",
+ TimeSpan.FromHours(8),
+ "China Standard Time",
+ "China Standard Time");
+ }
+ catch
+ {
+ return TimeZoneInfo.Utc; // 最终回退
+ }
+ }
+ ```
+
+4. **新增调试方法**:
+ ```csharp
+ // 获取当前使用的时区信息
+ public static string GetTimeZoneInfo()
+
+ // 检查时区是否正确初始化
+ public static bool IsTimeZoneCorrectlyInitialized()
+ ```
+
+5. **设计优势**:
+ - **跨平台兼容**:支持Windows、Linux、macOS等不同操作系统
+ - **健壮性**:多层回退机制确保在任何环境下都能正常工作
+ - **调试友好**:提供时区信息查询方法便于问题排查
+ - **向后兼容**:保持原有API不变,不影响现有代码
+ - **性能优化**:静态初始化,避免重复计算
+
+6. **修复的关键问题**:
+ - **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
+ - **跨平台兼容性**:不同操作系统使用不同的时区ID格式
+ - **初始化失败**:静态构造函数异常导致整个类无法使用
+ - **调试困难**:缺少时区状态查询方法
+
+**影响范围**:
+- 时间戳工具类的跨平台兼容性
+- 系统启动时的稳定性
+- 时间转换功能的可靠性
+- 调试和问题排查能力
+
+**后续优化**:
+- **简化时区选择逻辑**:优先使用`Asia/Shanghai`时区(Linux标准格式)
+- **提高性能**:减少不必要的时区ID尝试,直接使用最常用的时区
+- **代码简洁性**:简化回退逻辑,提高代码可读性
+- **移除Windows依赖**:完全移除`China Standard Time`时区ID,专注于Linux系统优化
+
+## 2024年修改记录
+
+### 修复NetworkProtocolLogObserver中的StopChannelManager方法并支持重新创建
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs`
+- `CoreAgent.WebSocketTransport/Interfaces/IMessageChannelManager.cs`
+- `CoreAgent.WebSocketTransport/Services/MessageChannelManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopChannelManager()` 方法直接调用 `_ChannelManager.Dispose()` 可能导致资源过早释放
+ - `IMessageChannelManager` 接口已实现 `IDisposable`,生命周期应由DI容器管理
+ - 直接调用 `Dispose()` 可能影响其他使用该实例的组件
+
+2. **修复方案**:
+ - 修改 `MessageChannelManager` 设计,不在构造函数中创建通道
+ - 移除构造函数中的默认参数,要求必须传入容量参数
+ - 添加容量参数验证,确保参数有效性
+ - 扩展 `WebSocketConfig` 配置类,添加分别的通道容量配置
+ - 更新依赖注入注册,从配置中读取不同的通道容量
+ - 优化通道管理方法,职责更加清晰:
+ - `CreateChannels()` - 安全创建(已存在则跳过)
+ - `ClearAllChannels()` - 清空通道消息(保持通道可用)
+ - `CompleteAllChannels()` - 完成通道(标记不再接受新消息,但保持可读)
+ - `ReleaseChannels()` - 完全释放通道资源
+
+ - 将 `StopChannelManager()` 改为调用 `ReleaseChannels()`
+ - 将 `RecreateChannelManager()` 改为调用 `CreateChannels()`
+ - **优化通道创建逻辑**:
+ - 修改 `CreateChannels()` 方法,如果通道已存在则先释放再创建
+ - 移除 `ResetChannels()` 方法,避免功能重复
+ - 简化接口设计,减少方法数量
+ - **WebSocketTransport 集成通道管理**:
+ - 重构 `ConnectInternalAsync()` 方法,提取为多个独立方法:
+ - `CreateMessageChannels()` - 先创建消息通道(同步方法)
+ - `EstablishWebSocketConnectionAsync()` - 再建立WebSocket连接(异步方法)
+ - `StartBackgroundTasks()` - 最后启动后台任务(同步方法)
+ - 在 `CloseAsync()` 中调用 `_channelManager.ReleaseChannels()` 释放通道
+ - 在重连失败时也释放通道,确保资源正确清理
+ - 通道生命周期与WebSocket连接生命周期完全同步
+ - **添加自动重连配置选项**:
+ - 在 `WebSocketConfig` 中添加 `EnableAutoReconnect` 配置项
+ - 在 `TriggerReconnect()` 方法中添加配置检查
+ - 支持通过配置文件控制是否启用自动重连功能
+ - **修复WebSocket配置文件**:
+ - 更新 `websocket.json` 和 `websocket.Development.json` 配置文件
+ - 添加所有新增的配置项:`EnableAutoReconnect`、`SendChannelCapacity`、`ReceiveChannelCapacity`、`PriorityChannelCapacity`、`MaxChunkSize`、`ChunkDelayMs`
+ - 为开发环境和生产环境提供不同的配置值
+ - 添加重复调用检查,避免二次调用导致异常
+ - 添加异常处理和日志记录
+ - 保持方法功能的同时避免资源管理问题
+
+3. **具体修改**:
+ **IMessageChannelManager 接口新增方法**:
+ ```csharp
+ ///
+ /// 创建所有通道
+ ///
+ void CreateChannels();
+
+ ///
+ /// 释放所有通道
+ ///
+ void ReleaseChannels();
+ ```
+
+ **MessageChannelManager 实现**:
+ ```csharp
+ // 构造函数要求必须传入容量参数,并验证参数有效性
+ public MessageChannelManager(ILogger logger, int sendChannelCapacity, int receiveChannelCapacity, int priorityChannelCapacity)
+ {
+ // 验证容量参数
+ if (sendChannelCapacity <= 0) throw new ArgumentOutOfRangeException(...);
+ // 保存容量配置,不创建通道
+ }
+
+ // 安全创建通道(如果已存在则跳过)
+ public void CreateChannels()
+ {
+ // 检查通道是否已存在,如果存在则跳过创建
+ }
+
+ // 清空通道消息(保持通道可用)
+ public void ClearAllChannels()
+ {
+ // 清空所有通道中的消息,但保持通道结构
+ }
+
+ // 完成通道(标记不再接受新消息,但保持可读)
+ public void CompleteAllChannels()
+ {
+ // 标记通道完成,不再接受新消息,但可以继续读取
+ }
+
+ // 完全释放通道资源
+ public void ReleaseChannels()
+ {
+ // 完成通道,释放资源,清空引用
+ }
+
+
+ ```
+
+ **WebSocketConfig 配置扩展**:
+ ```csharp
+ public class WebSocketConfig
+ {
+ // 是否启用自动重连功能
+ public bool EnableAutoReconnect { get; set; } = true;
+
+ // 最大重连尝试次数
+ public int MaxReconnectAttempts { get; set; } = 5;
+
+ // 发送通道容量
+ public int SendChannelCapacity { get; set; } = 1000;
+
+ // 接收通道容量
+ public int ReceiveChannelCapacity { get; set; } = 1000;
+
+ // 优先级通道容量
+ public int PriorityChannelCapacity { get; set; } = 100;
+ }
+ ```
+
+ **依赖注入注册更新**:
+ ```csharp
+ services.AddSingleton(provider =>
+ {
+ var config = provider.GetRequiredService>().Value;
+ return new MessageChannelManager(logger, config.SendChannelCapacity, config.ReceiveChannelCapacity, config.PriorityChannelCapacity);
+ });
+ ```
+
+ **NetworkProtocolLogObserver 修改**:
+ ```csharp
+ public void StopChannelManager()
+ {
+ // 调用 ReleaseChannels() 释放通道
+ _ChannelManager.ReleaseChannels();
+ }
+
+ public void RecreateChannelManager()
+ {
+ // 调用 CreateChannels() 重新创建通道(会自动释放现有通道)
+ _ChannelManager.CreateChannels();
+ }
+ ```
+
+ **WebSocketTransport 重构**:
+ ```csharp
+ // 连接时先创建通道,再建立连接
+ private async Task ConnectInternalAsync(CancellationToken cancellationToken)
+ {
+ // 1. 先创建消息通道
+ await CreateMessageChannelsAsync();
+
+ // 2. 再建立 WebSocket 连接
+ await EstablishWebSocketConnectionAsync(cancellationToken);
+
+ // 3. 最后启动后台任务
+ await StartBackgroundTasksAsync();
+ }
+
+ // 提取的独立方法
+ private void CreateMessageChannels()
+ {
+ _channelManager.CreateChannels();
+ }
+
+ private async Task EstablishWebSocketConnectionAsync(CancellationToken cancellationToken)
+ {
+ await _connection.ConnectAsync(...);
+ }
+
+ private void StartBackgroundTasks()
+ {
+ // 启动发送、接收、心跳任务
+ }
+ ```
+
+4. **修复优势**:
+ - **配置灵活性**: 支持通过配置文件分别设置不同通道的容量,更加灵活
+ - **参数验证**: 构造函数中验证容量参数,确保参数有效性
+ - **方法职责清晰**: 每个方法职责明确,避免功能重叠
+ - **生命周期控制**: 通道的创建和释放完全由用户控制,更加灵活
+ - **资源管理**: 避免在构造函数中创建资源,符合延迟初始化原则
+ - **重复使用**: 支持多次创建和释放,满足业务需求
+ - **重复调用保护**: 防止二次调用导致异常,提高系统稳定性
+ - **异常处理**: 添加了完整的异常处理和日志记录,但不影响主程序运行
+ - **业务连续性**: 异常被捕获并记录,但不会中断主程序流程
+ - **功能保持**: 仍然能够正确停止和重新创建通道管理器
+ - **日志完善**: 提供了详细的调试和错误日志信息
+
+### 创建蜂窝网络配置实体类
+
+### 创建蜂窝网络配置实体类
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs` (新建)
+
+**修改内容**:
+
+1. **创建CellularNetworkConfiguration实体类**
+ - 包含设备代码(DeviceCode)字符串属性
+ - 包含运行时代码(RuntimeCode)字符串属性
+ - 包含无线接入网配置(RadioAccessNetworkConfiguration)字符串属性
+ - 包含核心网IMS配置集合(List)属性
+
+2. **创建CoreNetworkImsConfiguration实体类**
+ - 包含索引(Index)整数属性
+ - 包含PLMN标识(Plmn)字符串属性
+ - 包含核心网配置(CoreNetworkConfiguration)字符串属性
+ - 包含IMS服务配置(ImsServiceConfiguration)字符串属性
+
+3. **具体实现**:
+ ```csharp
+ ///
+ /// 蜂窝网络配置实体
+ ///
+ public class CellularNetworkConfiguration
+ {
+ ///
+ /// 设备代码
+ ///
+ public string DeviceCode { get; set; }
+
+ ///
+ /// 运行时代码
+ ///
+ public string RuntimeCode { get; set; }
+
+ ///
+ /// 无线接入网配置
+ ///
+ public string RadioAccessNetworkConfiguration { get; set; }
+
+ ///
+ /// 核心网IMS配置集合
+ ///
+ public List CoreNetworkImsConfigurations { get; set; } = new List();
+ }
+
+ ///
+ /// 核心网IMS配置对象
+ ///
+ public class CoreNetworkImsConfiguration
+ {
+ ///
+ /// 索引
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// PLMN标识
+ ///
+ public string Plmn { get; set; }
+
+ ///
+ /// 核心网配置
+ ///
+ public string CoreNetworkConfiguration { get; set; }
+
+ ///
+ /// IMS服务配置
+ ///
+ public string ImsServiceConfiguration { get; set; }
+ }
+ ```
+
+4. **设计优势**:
+ - **命名规范**:遵循C#命名约定,使用PascalCase
+ - **命名清晰**:类名明确表达业务含义,提高代码可读性
+ - **属性专业**:使用完整的专业术语,避免缩写和模糊命名
+ - **类型安全**:使用强类型属性,避免类型错误
+ - **文档完整**:每个属性都有详细的XML文档注释
+ - **集合初始化**:使用集合初始化器确保集合不为null
+ - **职责清晰**:每个类都有明确的职责和用途
+ - **易于扩展**:结构清晰,便于后续添加新属性
+ - **业务导向**:类名直接反映业务领域概念
+ - **专业术语**:使用标准的电信网络术语,提高代码专业性
+
+### 添加蜂窝网络配置数据校验功能
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs`
+
+**修改内容**:
+
+1. **添加数据校验功能**
+ - 为 `DeviceCode` 和 `RuntimeCode` 添加 `[Required]` 特性,确保不能为空
+ - 为 `Plmn` 添加 `[Required]` 特性,确保不能为空
+ - 添加 `ValidateConfiguration()` 方法进行业务逻辑校验
+
+2. **业务规则校验**
+ - 验证 `DeviceCode` 和 `RuntimeCode` 不能为空
+ - 验证 `RadioAccessNetworkConfiguration` 和 `CoreNetworkImsConfigurations` 至少有一个有数据
+ - 验证 `CoreNetworkImsConfiguration` 中的 `CoreNetworkConfiguration` 和 `ImsServiceConfiguration` 至少有一个有数据
+
+3. **创建ValidationResult类**
+ - 提供统一的验证结果返回格式
+ - 支持成功和失败两种状态
+ - 提供详细的错误消息
+ - 支持隐式转换操作符
+
+4. **具体实现**:
+ ```csharp
+ // 必填字段校验
+ [Required(ErrorMessage = "设备代码不能为空")]
+ public string DeviceCode { get; set; }
+
+ [Required(ErrorMessage = "运行时代码不能为空")]
+ public string RuntimeCode { get; set; }
+
+ // 业务逻辑校验
+ public ValidationResult ValidateConfiguration()
+ {
+ // 验证必填字段
+ if (string.IsNullOrWhiteSpace(DeviceCode))
+ {
+ return new ValidationResult("设备代码不能为空");
+ }
+
+ // 验证无线接入网配置和核心网IMS配置至少有一个有数据
+ var hasRadioAccessConfig = !string.IsNullOrWhiteSpace(RadioAccessNetworkConfiguration);
+ var hasCoreNetworkConfigs = CoreNetworkImsConfigurations?.Any() == true;
+
+ if (!hasRadioAccessConfig && !hasCoreNetworkConfigs)
+ {
+ return new ValidationResult("无线接入网配置和核心网IMS配置至少需要配置其中一项");
+ }
+
+ return ValidationResult.Success;
+ }
+ ```
+
+5. **设计优势**:
+ - **数据完整性**:确保必填字段不为空
+ - **业务规则校验**:验证业务逻辑的正确性
+ - **统一验证接口**:提供一致的验证方法
+ - **详细错误信息**:提供具体的错误描述
+ - **分层校验**:支持嵌套对象的校验
+ - **易于扩展**:可以轻松添加新的校验规则
+
+**影响范围**:
+- 蜂窝网络配置数据模型定义
+- 核心网IMS配置管理
+- 数据实体结构标准化
+- 领域模型完整性
+- 代码可读性和维护性提升
+- 数据校验和业务规则验证
+- 错误处理和用户反馈
+
+### 创建MessageTransferProtocolLog模型解决命名冲突
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Models/MessageTransferProtocolLog.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs`
+
+**修改内容**:
+
+1. **创建MessageTransferProtocolLog模型**
+ - 在 `CoreAgent.WebSocketTransport` 项目中创建新的协议日志模型
+ - 与 `CoreAgent.ProtocolClient` 中的 `TransferProtocolLog` 区分开
+ - 保持相同的字段结构,避免命名冲突
+ - 专门用于WebSocket传输层的协议日志数据传输
+
+2. **修改NetworkProtocolLogObserver转换逻辑**
+ - 在 `OnProtocolLogsReceived` 方法中添加类型转换
+ - 将 `CoreAgent.ProtocolClient.TransferProtocolLog` 转换为 `CoreAgent.WebSocketTransport.MessageTransferProtocolLog`
+ - 保持所有字段的完整映射
+ - 添加必要的using语句引用
+
+3. **具体实现**:
+ ```csharp
+ // 时间跟踪和性能监控
+ var startTime = DateTime.UtcNow;
+
+ // 空值检查
+ if (logDetails == null)
+ {
+ _logger.LogWarning("接收到的协议日志为空");
+ return;
+ }
+
+ // 转换为列表以避免多次枚举
+ var logList = logDetails.ToList();
+ var logCount = logList.Count;
+
+ // 空集合检查
+ if (logCount == 0)
+ {
+ _logger.LogDebug("接收到的协议日志集合为空,跳过处理");
+ return;
+ }
+
+ // 类型转换逻辑
+ var webSocketLogs = logList.Select(log => new MessageTransferProtocolLog
+ {
+ Id = log.Id,
+ LayerType = log.LayerType.ToString(),
+ MessageDetailJson = log.MessageDetailJson,
+ CellID = log.CellID,
+ IMSI = log.IMSI,
+ Direction = log.Direction,
+ UEID = log.UEID,
+ PLMN = log.PLMN,
+ TimeMs = log.TimeMs,
+ Timestamp = log.Timestamp,
+ Info = log.Info,
+ Message = log.Message
+ });
+
+ // 通道写入状态监控
+ var writeSuccess = _ChannelManager.SendChannel.TryWrite(webSocketLogs);
+ var processingTime = DateTime.UtcNow - startTime;
+
+ if (writeSuccess)
+ {
+ _logger.LogDebug("协议日志处理成功,数量: {LogCount}, 处理时间: {ProcessingTime}ms",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ else
+ {
+ _logger.LogWarning("协议日志写入通道失败,数量: {LogCount}, 处理时间: {ProcessingTime}ms, 通道可能已满或已关闭",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ ```
+
+4. **设计优势**:
+ - **命名清晰**:`MessageTransferProtocolLog` 明确表示用于消息传输
+ - **避免冲突**:与原始 `TransferProtocolLog` 有明确区分
+ - **职责分离**:WebSocket传输层有独立的协议日志模型
+ - **类型安全**:通过显式转换确保类型安全
+ - **易于维护**:清晰的命名约定便于理解和维护
+ - **性能监控**:添加时间跟踪和通道写入状态监控
+ - **错误处理**:完善的异常处理和日志记录
+ - **Bug修复**:修复空引用检查和多次枚举的性能问题
+ - **边界处理**:添加空集合检查,避免处理空集合
+ - **代码规范**:优化ProtocolMessage模型注释,提高代码可读性
+
+**影响范围**:
+- WebSocket传输层协议日志处理
+- 协议日志观察者模式实现
+- 跨项目类型转换逻辑
+- 协议消息模型注释优化
+
+### CellularNetworkService.StartNetworkAsync 方法添加协议客户端配置创建
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **调整执行步骤顺序**
+ - 将原来的第5.5步改为第6步(创建协议客户端配置)
+ - 将WebSocket传输连接移到第7步(在网络配置启动之前)
+ - 重新编号后续步骤(8-11步)
+ - **优化执行顺序**:确保WebSocket连接在网络配置启动之前完成
+
+2. **添加第7步WebSocket传输连接**
+ - 在 `StartNetworkAsync` 方法的第6步(创建协议客户端配置)之后添加第7步
+ - 注入 `IWebSocketTransport` 依赖
+ - 创建独立的 `StartWebSocketTransportAsync()` 方法处理连接逻辑
+ - 返回 `bool` 值表示连接是否成功
+ - 添加连接状态检查和错误处理
+ - **严格检查**:WebSocket连接失败时立即返回失败结果,提示服务端可能未启动
+
+3. **修复Logger类型问题**
+ - 添加 `ILoggerFactory` 依赖注入到构造函数
+ - 使用 `_loggerFactory.CreateLogger()` 创建正确类型的Logger
+ - 确保 `ProtocolClientConfigFactory` 获得正确的Logger实例
+
+3. **具体实现**:
+ ```csharp
+ // 6. 创建协议客户端配置
+ var protocolConfigFactory = new ProtocolClientConfigFactory(_loggerFactory.CreateLogger(), _context);
+ var configCreated = protocolConfigFactory.CreateFromEntities();
+ if (configCreated)
+ {
+ _logger.LogInformation("协议客户端配置创建成功,共创建 {ConfigCount} 个配置", protocolConfigFactory.ConfigCount);
+ }
+ else
+ {
+ _logger.LogWarning("协议客户端配置创建失败");
+ return CellularNetworkOperationResult.Failure("协议客户端配置创建失败");
+ }
+
+ // 7. 启动 WebSocket 传输连接
+ var webSocketConnected = await StartWebSocketTransportAsync();
+ if (!webSocketConnected)
+ {
+ _logger.LogError("WebSocket 传输连接启动失败,服务端可能未启动");
+ return CellularNetworkOperationResult.Failure("WebSocket 传输连接启动失败,服务端可能未启动");
+ }
+ _logger.LogInformation("WebSocket 传输连接启动成功");
+ ```
+
+4. **添加必要的依赖注入**
+ - 添加 `ILoggerFactory loggerFactory` 参数到构造函数
+ - 添加 `IWebSocketTransport webSocketTransport` 参数到构造函数
+ - 添加 `using CoreAgent.Infrastructure.Services.Network;` 以支持 `ProtocolClientConfigFactory`
+ - 添加 `using CoreAgent.WebSocketTransport.Interfaces;` 以支持 `IWebSocketTransport`
+
+5. **设计优势**:
+ - 在IP端点信息准备完成后立即创建协议客户端配置
+ - 不依赖网络启动结果,确保配置创建的独立性
+ - 在网络配置启动之前启动WebSocket传输连接
+ - 提供详细的日志记录便于调试
+ - 保持代码的简洁性和可维护性
+ - 正确处理Logger类型,避免类型不匹配问题
+ - 优化执行顺序,提高错误隔离能力
+ - 完善的错误处理机制,确保配置创建失败时及时停止
+ - 严格检查机制,WebSocket连接失败时立即停止网络启动流程
+ - 方法职责单一,WebSocket连接逻辑独立封装
+
+**影响范围**:
+- 蜂窝网络启动流程
+- 协议客户端配置管理
+- WebSocket传输服务集成
+- 网络状态监控
+- 依赖注入配置(需要更新服务注册)
+
+### CellularNetworkService构造函数添加IProtocolLogObserver依赖
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加IProtocolLogObserver依赖注入**
+ - 在构造函数中添加 `IProtocolLogObserver protocolLogObserver` 参数
+ - 添加私有字段 `_protocolLogObserver` 存储依赖
+ - 添加空值检查和异常抛出
+
+2. **添加必要的using语句**
+ - 添加 `using CoreAgent.ProtocolClient.ProtocolEngineCore;` 以支持 `IProtocolLogObserver`
+
+3. **具体实现**:
+ ```csharp
+ // 构造函数参数
+ public CellularNetworkService(
+ // ... 其他参数
+ IWebSocketTransport webSocketTransport,
+ IProtocolLogObserver protocolLogObserver)
+
+ // 私有字段
+ private readonly IProtocolLogObserver _protocolLogObserver;
+
+ // 构造函数初始化
+ _protocolLogObserver = protocolLogObserver ?? throw new ArgumentNullException(nameof(protocolLogObserver));
+ ```
+
+4. **设计优势**:
+ - 为后续协议客户端管理提供必要的依赖
+ - 保持依赖注入的一致性
+ - 提供空值检查确保服务稳定性
+ - 为协议日志观察者模式提供支持
+
+**影响范围**:
+- 蜂窝网络服务依赖注入配置
+- 协议客户端日志观察者集成
+- 服务注册配置更新
+
+### StartNetworkAsync方法添加时间跟踪记录
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加整体时间跟踪**
+ - 在方法开始时记录开始时间
+ - 在方法结束时计算总耗时并记录
+ - 使用UTC时间确保时间一致性
+
+2. **为每个步骤添加详细时间跟踪**
+ - 为11个步骤中的每个步骤添加开始和结束时间记录
+ - 使用 `LogDebug` 级别记录每个步骤的耗时
+ - 保持原有的 `LogInformation` 和 `LogError` 级别日志不变
+
+3. **具体实现**:
+ ```csharp
+ // 方法开始时间跟踪
+ var startTime = DateTime.UtcNow;
+ _logger.LogInformation("开始启动网络配置 {ConfigKey},开始时间: {StartTime}", key, startTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
+
+ // 每个步骤的时间跟踪
+ var stepXStart = DateTime.UtcNow;
+ _logger.LogDebug("步骤X开始:[步骤描述]");
+
+ // 步骤执行逻辑...
+
+ var stepXDuration = (DateTime.UtcNow - stepXStart).TotalMilliseconds;
+ _logger.LogDebug("步骤X完成:[步骤描述],耗时: {Duration}ms", stepXDuration.ToString("F2"));
+
+ // 方法结束时间跟踪
+ var endTime = DateTime.UtcNow;
+ var duration = endTime - startTime;
+ _logger.LogInformation("蜂窝网络配置 {ConfigKey} 启动成功,当前状态: {Status},总耗时: {Duration}ms",
+ key, state.CurrentStatus, duration.TotalMilliseconds.ToString("F2"));
+ ```
+
+4. **设计优势**:
+ - **性能监控**:可以识别网络启动过程中的性能瓶颈
+ - **调试支持**:详细的时间信息有助于问题定位和性能优化
+ - **日志分级**:使用Debug级别避免生产环境日志过多
+ - **时间精度**:使用毫秒级精度提供准确的性能数据
+ - **UTC时间**:确保时间记录的一致性和准确性
+ - **非侵入性**:不影响原有的业务逻辑和错误处理
+
+5. **跟踪的步骤**:
+ - 步骤1:获取并验证网络配置
+ - 步骤2:执行网络接口初始化命令
+ - 步骤3:复制配置值到临时目录
+ - 步骤4:获取并验证 IP 端点信息
+ - 步骤5:更新 IP 端点管理器
+ - 步骤6:创建协议客户端配置
+ - 步骤7:启动 WebSocket 传输连接
+ - 步骤8:启动网络配置
+ - 步骤9:更新网络配置类型
+ - 步骤10:检查网络端点连接状态
+ - 步骤11:更新网络状态
+
+**影响范围**:
+# 修改记录
+
+## 2025-08-21
+
+### CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs
+- **问题**: 第一次停止协议客户端成功后,第二次启动时 OnProtocolLogsReceived 方法似乎没有被调用
+- **修改**: 在 OnProtocolLogsReceived 和 ProcessLogsInternal 方法中添加了详细的跟踪日志
+ - 添加了方法开始执行的日志记录
+ - 添加了线程ID跟踪,便于调试多线程问题
+ - 添加了参数验证和数据处理各阶段的日志记录
+ - 添加了锁获取和释放的日志记录
+ - 添加了配置获取、数据转换、通道写入等关键步骤的日志记录
+ - 将部分 Debug 级别日志提升为 Information 级别,便于问题排查
+
+**修改目的**: 通过详细的日志跟踪,帮助诊断为什么在第二次启动协议客户端时 OnProtocolLogsReceived 方法没有被正确调用的问题。
+
+### CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs
+- **问题**: 第二次启动协议客户端时,虽然日志显示"成功通知协议日志观察者",但 OnProtocolLogsReceived 方法实际没有被处理
+- **修改**: 在通知观察者的代码段中添加了更详细的诊断日志
+ - 添加了观察者实例null检查
+ - 添加了观察者类型信息记录
+ - 添加了方法调用前后的详细日志
+ - 改进了异常处理中的观察者信息记录
+
+**修改目的**: 通过详细的观察者状态检查和方法调用跟踪,帮助诊断观察者实例状态问题,确定是观察者引用问题还是方法调用问题。
+
+### CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs
+- **问题**: 需要检查观察者传递和管理是否存在问题
+- **修改**: 在关键方法中添加了观察者状态检查和详细跟踪日志
+ - 在构造函数中添加了观察者类型信息记录
+ - 在 CreateProtocolWsClient 方法中添加了观察者null检查和类型信息
+ - 在 StartAllClients 方法中添加了观察者状态检查和详细启动日志
+ - 在 StopAllClients 方法中添加了更详细的停止过程日志
+ - 添加了客户端实例创建和复用的区分日志
+
+**修改目的**: 通过详细的观察者状态跟踪和客户端生命周期日志,帮助诊断观察者传递链路中的问题,确保观察者实例在整个生命周期中的正确性。
+
+## 2025-01-02
+
+### 日志规范修改 - GeneralCellularNetworkService和CellularNetworkContext
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+- `CoreAgent.Infrastructure/Contexts/CellularNetworkContext.cs`
+
+**修改内容**:
+
+1. **GeneralCellularNetworkService.cs 日志规范**:
+ - **第99行日志级别修正**: 将 `LogWarning` 改为 `LogInformation`,因为这是正常的初始化操作
+ - **日志格式统一**: 将字符串插值改为结构化日志格式
+ ```csharp
+ // 修改前
+ _logger.LogWarning($"初始化网络上下文 运行编码 {key}");
+
+ // 修改后
+ _logger.LogInformation("开始初始化网络上下文,运行编码: {RuntimeCode}", key);
+ ```
+ - **添加SetDeviceCode日志**: 在 `SetDeviceCode` 调用前添加日志记录
+ ```csharp
+ _logger.LogInformation("设置设备代码: {DeviceCode}", cellular.DeviceCode);
+ ```
+
+2. **CellularNetworkContext.cs 日志增强**:
+ - **Initialize方法日志**: 添加初始化和完成日志
+ ```csharp
+ _logger.LogInformation("开始初始化蜂窝网络上下文,配置键: {ConfigKey}", neConfigKey);
+ _logger.LogInformation("蜂窝网络上下文初始化完成,配置键: {ConfigKey}", neConfigKey);
+ ```
+ - **Initialize方法逻辑修复**: 移除被注释掉的 `_isInitialized` 检查,允许重新初始化以支持配置键更新
+ ```csharp
+ // 移除了以下被注释的代码,允许重新初始化
+ //if (_isInitialized)
+ //{
+ // return;
+ //}
+ ```
+ - **SetDeviceCode方法日志规范**: 将字符串插值改为结构化日志
+ ```csharp
+ // 修改前
+ _logger.LogInformation($"设备代码已设置为: {deviceCode}");
+
+ // 修改后
+ _logger.LogInformation("设备代码已设置为: {DeviceCode}", deviceCode);
+ ```
+ - **UpdateNetworkConfigType方法日志**: 添加配置类型更新日志
+ ```csharp
+ _logger.LogInformation("网络配置类型已更新: {PreviousConfigType} -> {NewConfigType}", previousConfigType, configType);
+ ```
+
+3. **修改原则**:
+ - **日志级别规范**: 正常操作使用 `LogInformation`,警告使用 `LogWarning`,错误使用 `LogError`
+ - **结构化日志**: 使用占位符格式 `{ParameterName}` 而不是字符串插值
+ - **日志完整性**: 为关键操作添加开始和完成的日志记录
+ - **日志一致性**: 保持日志格式和命名规范的一致性
+
+4. **影响范围**:
+ - 蜂窝网络启动流程的日志记录更加规范和详细
+ - 网络上下文初始化和状态变更的日志更加清晰
+ - 便于问题排查和系统监控
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StopAllClients方法移除_clients条目
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopAllClients` 方法停止所有协议客户端后,`_clients` 字典中仍然保留客户端条目
+ - 导致下次调用 `StartAllClients` 时会检查到已存在的客户端,不会创建新的 `ProtocolWsClient` 实例
+ - 需要确保每次 `StartAllClients` 都创建新的客户端实例
+
+2. **修复方案**:
+ - 在 `StopAllClients` 方法中添加 `_clients.Clear()` 调用
+ - 移除所有客户端条目,确保下次启动时创建新实例
+ - 修改返回值计算逻辑,使用 `stoppedCount` 而不是 `_clients.Count`
+ - 添加日志记录,说明已清空客户端列表
+
+3. **具体修改**:
+ ```csharp
+ // 修改前
+ var allDisconnected = disconnectedCount == _clients.Count;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+
+ // 修改后
+ // 移除所有客户端条目,确保下次StartAllClients会创建新实例
+ _clients.Clear();
+
+ var allDisconnected = disconnectedCount == stoppedCount;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}, 已清空客户端列表",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+ ```
+
+4. **设计优势**:
+ - **实例隔离**: 确保每次启动都创建新的协议客户端实例
+ - **资源清理**: 完全清理客户端引用,避免内存泄漏
+ - **状态重置**: 确保客户端管理器状态完全重置
+ - **日志完整**: 记录清空操作,便于调试和监控
+ - **逻辑正确**: 返回值计算基于实际停止的客户端数量
+
+5. **修复的关键问题**:
+ - **实例复用**: 避免复用已停止的客户端实例
+ - **状态污染**: 防止旧实例状态影响新启动的客户端
+ - **资源管理**: 确保客户端资源完全释放
+ - **启动逻辑**: 保证 `StartAllClients` 每次都创建新实例
+
+**影响范围**:
+- 协议客户端管理器的生命周期管理
+- 客户端实例的创建和销毁逻辑
+- 资源清理和内存管理
+- 网络启动流程的稳定性
+
+## 2025-01-02
+
+### WebSocketConnection.CloseAsync方法添加跟踪日志
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Services/WebSocketConnection.cs`
+
+**修改内容**:
+
+1. **为CloseAsync方法添加详细的跟踪日志信息**
+ - 在方法开始时记录关闭操作的详细信息,包括关闭状态、状态描述和当前连接状态
+ - 在WebSocket连接处于打开状态时,添加Debug级别的日志记录正常关闭流程
+ - 在关闭成功时记录成功信息,包括关闭状态和状态描述
+ - 在关闭失败时记录错误信息,包括异常详情和关闭参数
+ - 在WebSocket连接未处于打开状态时,记录警告信息并跳过关闭操作
+
+2. **具体实现**:
+ ```csharp
+ public async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("开始关闭 WebSocket 连接 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 当前连接状态: {CurrentState}",
+ closeStatus, statusDescription, _webSocket.State);
+
+ if (_webSocket.State == WebSocketState.Open)
+ {
+ _logger.LogDebug("WebSocket 连接处于打开状态,执行正常关闭流程");
+ try
+ {
+ await _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
+ _logger.LogInformation("WebSocket 连接关闭成功 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}",
+ closeStatus, statusDescription);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "WebSocket 连接关闭失败 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 错误信息: {ErrorMessage}",
+ closeStatus, statusDescription, ex.Message);
+ throw;
+ }
+ }
+ else
+ {
+ _logger.LogWarning("WebSocket 连接未处于打开状态,跳过关闭操作 - 当前状态: {CurrentState}", _webSocket.State);
+ }
+ }
+ ```
+
+3. **设计优势**:
+ - **详细跟踪**: 提供完整的关闭操作跟踪信息,便于调试和问题排查
+ - **状态监控**: 记录当前连接状态,帮助理解关闭操作的上下文
+ - **错误处理**: 完善的异常处理和错误日志记录
+ - **性能监控**: 可以跟踪关闭操作的执行情况
+ - **调试友好**: 提供足够的调试信息,便于问题定位
+ - **日志分级**: 使用不同级别的日志,避免生产环境日志过多
+
+4. **日志级别说明**:
+ - **Information**: 记录关闭操作的开始和成功完成
+ - **Debug**: 记录正常关闭流程的执行
+ - **Warning**: 记录跳过关闭操作的情况
+ - **Error**: 记录关闭失败和异常信息
+
+**影响范围**:
+- WebSocket连接关闭操作的日志记录
+- 调试和问题排查能力
+- 连接状态监控
+- 错误处理和异常跟踪
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StartAllClients方法启动和连接状态检查分离
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StartAllClients` 方法中启动客户端和检查连接状态混在一起
+ - 方法职责不够清晰,同时处理启动和连接状态检查
+ - 需要将启动和连接状态检查分离,提高代码的可读性和可维护性
+ - `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法需要适配新的接口
+
+2. **修复方案**:
+ - **分离启动和连接状态检查**:将 `StartAllClients` 方法中的连接状态检查逻辑分离出来
+ - **新增连接状态检查方法**:创建 `CheckAllClientsConnection()` 方法专门用于检查连接状态
+ - **简化启动方法**:`StartAllClients` 方法只负责启动客户端,不检查连接状态
+ - **更新接口定义**:在 `IProtocolWsClientManager` 接口中添加新的方法定义
+ - **优化返回值**:`StartAllClients` 返回是否所有客户端都成功启动,`CheckAllClientsConnection` 返回是否所有客户端都已连接
+ - **修复调用方代码**:更新 `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法以适配新的接口
+
+3. **具体修改**:
+ **StartAllClients方法优化**:
+ ```csharp
+ // 修改前 - 启动和连接状态检查混在一起
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected; // 返回连接状态
+ }
+
+ // 修改后 - 只负责启动客户端
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ var allStarted = startedCount == configs.Length;
+ return allStarted; // 返回启动状态
+ }
+ ```
+
+ **新增CheckAllClientsConnection方法**:
+ ```csharp
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ public bool CheckAllClientsConnection(int timeoutSeconds = 10)
+ {
+ ThrowIfDisposed();
+
+ if (timeoutSeconds <= 0)
+ {
+ _logger.LogWarning("超时时间必须大于0,使用默认值10秒");
+ timeoutSeconds = 10;
+ }
+
+ lock (_lock)
+ {
+ if (_clients.Count == 0)
+ {
+ _logger.LogWarning("没有运行中的协议客户端");
+ return false;
+ }
+
+ _logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
+ _clients.Count, timeoutSeconds);
+
+ var startTime = DateTime.UtcNow;
+ var timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ var connectedCount = 0;
+ var maxAttempts = 10; // 最大尝试次数
+ var attempt = 0;
+
+ while (attempt < maxAttempts)
+ {
+ attempt++;
+ connectedCount = 0;
+
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ if (client.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ _logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
+ attempt, kvp.Key, client.IsConnected);
+ }
+
+ var allConnected = connectedCount == _clients.Count;
+
+ if (allConnected)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
+ return true;
+ }
+
+ // 检查是否超时
+ if (DateTime.UtcNow - startTime > timeout)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
+ connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
+ return false;
+ }
+
+ // 等待一段时间后重试
+ if (attempt < maxAttempts)
+ {
+ var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
+ Thread.Sleep(waitTime);
+ }
+ }
+
+ var finalElapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
+
+ return false;
+ }
+ }
+ ```
+
+ **GeneralCellularNetworkService.StartAllProtocolClientsAsync方法修复**:
+ ```csharp
+ // 修改前 - 只检查启动状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+ _logger.LogInformation("所有协议客户端启动完成");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+
+ // 修改后 - 分别检查启动状态和连接状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ _logger.LogInformation("开始启动所有协议客户端");
+
+ // 获取协议客户端配置
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ if (protocolConfigs == null || protocolConfigs.Length == 0)
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
+ }
+
+ _logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
+
+ // 启动所有协议客户端
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+
+ _logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
+
+ // 检查连接状态(使用默认10秒超时)
+ var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
+ if (!connectionResult)
+ {
+ _logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
+ }
+
+ _logger.LogInformation("所有协议客户端启动并连接成功");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+ ```
+
+ **接口定义更新**:
+ ```csharp
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ ///
+ /// 启动所有协议客户端
+ ///
+ /// 协议客户端配置数组
+ /// 是否所有客户端都成功启动
+ bool StartAllClients(ProtocolClientConfig[] configs);
+
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ bool CheckAllClientsConnection(int timeoutSeconds = 10);
+
+ ///
+ /// 停止所有协议客户端
+ ///
+ /// 是否所有客户端都成功停止并断开连接
+ bool StopAllClients();
+ }
+ ```
+
+4. **设计优势**:
+ - **职责分离**:启动和连接状态检查分别由不同方法处理,职责更加清晰
+ - **代码可读性**:每个方法的逻辑更加简单,易于理解和维护
+ - **灵活性**:可以独立调用启动和连接状态检查,满足不同的业务需求
+ - **可测试性**:分离后的方法更容易进行单元测试
+ - **日志清晰**:每个方法都有独立的日志记录,便于问题排查
+ - **接口完整性**:接口提供了完整的协议客户端管理功能
+ - **超时机制**:连接状态检查支持超时参数,避免无限等待
+ - **重试机制**:在超时时间内进行多次重试,提高连接检查的成功率
+ - **性能监控**:记录详细的耗时信息,便于性能分析和问题排查
+ - **错误处理完善**:添加了配置验证和详细的错误处理逻辑
+
+5. **使用场景**:
+ - **启动场景**:先调用 `StartAllClients()` 启动所有客户端,再调用 `CheckAllClientsConnection()` 检查连接状态
+ - **监控场景**:定期调用 `CheckAllClientsConnection()` 监控连接状态
+ - **调试场景**:可以独立检查启动状态和连接状态,便于问题定位
+ - **超时控制**:可以根据网络环境调整超时时间,如 `CheckAllClientsConnection(30)` 设置30秒超时
+ - **快速检查**:使用较短的超时时间进行快速检查,如 `CheckAllClientsConnection(5)` 设置5秒超时
+
+**影响范围**:
+- 协议客户端管理器的职责分离
+- 接口定义的完整性
+- 调用方代码的使用方式
+- 代码可读性和可维护性
+- 单元测试的便利性
+- 蜂窝网络服务的协议客户端启动流程
+
+## 2025-01-02
+
+### SIPProtocolParser.GeneralParse方法严谨性修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/BuildProtocolParser/SIPProtocolParser.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - 变量名错误:`regisMccLine` 实际匹配的是MNC,`regisMncLine` 实际匹配的是MCC
+ - 空引用风险:使用了 `!` 操作符但没有进行空值检查
+ - 重复赋值:对 `log.SIP.Plmn` 进行了两次赋值,第二次会覆盖第一次
+ - 正则表达式不够严谨:缺少编译选项
+ - 返回值检查错误:`StringToId` 返回0表示空字符串,不是-1
+
+2. **修复方案**:
+ - **修复变量名错误**:将变量名与实际匹配内容对应
+ - **增强空值检查**:添加参数验证和空值检查
+ - **修复重复赋值**:分别设置PLMN和IMSI属性
+ - **优化正则表达式**:添加 `RegexOptions.Compiled` 提高性能
+ - **修复返回值检查**:使用正确的返回值判断逻辑
+ - **添加参数验证**:验证Groups数量和参数有效性
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 变量名错误
+ var regisMccLine = log!.Data.Select(line => _regMnc.Match(line))... // 实际匹配MNC
+ var regisMncLine = log!.Data.Select(line => _regMcc.Match(line))... // 实际匹配MCC
+
+ // 修复后 - 变量名正确
+ var mncValue = log.Data?.Select(line => _regMnc.Match(line))... // 匹配MNC
+ var mccValue = log.Data?.Select(line => _regMcc.Match(line))... // 匹配MCC
+
+ // 修复前 - 重复赋值
+ log.SIP.Plmn = $"{regisMncLine}{regisMccLine!.Substring(1)}";
+ log.SIP.Plmn = regisIMSILine; // 覆盖了上面的赋值
+
+ // 修复后 - 分别设置不同属性
+ if (!string.IsNullOrEmpty(mccValue) && !string.IsNullOrEmpty(mncValue))
+ {
+ log.SIP.Plmn = $"{mccValue}{mncValue}";
+ }
+ if (!string.IsNullOrEmpty(imsiValue))
+ {
+ log.SIP.IMSI = imsiValue; // 使用正确的IMSI属性
+ }
+
+ // 修复前 - 返回值检查错误
+ if (info == -1) return;
+
+ // 修复后 - 正确的返回值检查
+ if (info == 0) return; // StringToId返回0表示空字符串
+ ```
+
+4. **设计优势**:
+ - **逻辑正确性**:修复了变量名错误和重复赋值问题
+ - **空值安全**:添加了完整的空值检查和参数验证
+ - **性能优化**:正则表达式使用编译选项提高性能
+ - **代码严谨性**:添加了Groups数量验证和参数有效性检查
+ - **错误预防**:使用空条件操作符避免空引用异常
+ - **属性正确性**:使用正确的SIP属性设置PLMN和IMSI
+
+5. **修复的关键问题**:
+ - **变量命名混乱**:MCC和MNC变量名与实际匹配内容不匹配
+ - **空引用风险**:使用 `!` 操作符但没有验证对象不为空
+ - **数据覆盖**:对同一属性进行两次赋值导致数据丢失
+ - **返回值误解**:对 `StringToId` 方法返回值理解错误
+ - **正则表达式性能**:缺少编译选项影响性能
+ - **参数验证缺失**:没有验证正则匹配结果的Groups数量
+ - **日志跟踪缺失**:缺少详细的错误日志记录,难以调试问题
+
+6. **日志跟踪增强**:
+ - **添加ILogger支持**:在构造函数中创建类型安全的Logger实例
+ - **参数验证日志**:记录BuildProtocolLog参数为空的情况
+ - **StringToId失败日志**:记录StringToId返回0(空字符串)的情况
+ - **Groups数量验证日志**:记录正则匹配Groups数量不足的详细信息
+ - **性能优化**:使用私有Logger字段避免重复创建Logger实例
+
+**影响范围**:
+- SIP协议解析的准确性
+- 协议日志数据的完整性
+- 代码的稳定性和可靠性
+- 性能优化和错误预防
+- 代码可读性和维护性
+- 调试和问题排查能力显著提升
+
+### TimeStampHelper时区初始化异常修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `TimeStampHelper`类在静态初始化时抛出`System.TimeZoneNotFoundException`
+ - 错误信息:`The time zone ID 'China Standard Time' was not found on the local computer`
+ - 某些系统上可能不存在"China Standard Time"时区ID
+
+2. **修复方案**:
+ - **多时区ID支持**:尝试多种可能的时区ID,包括Windows和Linux/macOS格式
+ - **回退机制**:如果系统时区ID都不可用,创建自定义UTC+8时区
+ - **最终回退**:如果自定义时区创建失败,使用UTC时区作为最后回退
+ - **调试支持**:添加时区信息查询和验证方法
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 直接使用单一时区ID
+ private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
+
+ // 修复后 - 支持多种时区ID和回退机制
+ private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
+
+ private static TimeZoneInfo GetChinaStandardTime()
+ {
+ // 尝试多种可能的时区ID
+ string[] timeZoneIds = {
+ "China Standard Time", // Windows
+ "Asia/Shanghai", // Linux/macOS
+ "Asia/Chongqing", // 备选
+ "Asia/Harbin", // 备选
+ "Asia/Urumqi" // 备选
+ };
+
+ foreach (string timeZoneId in timeZoneIds)
+ {
+ try
+ {
+ return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
+ }
+ catch (TimeZoneNotFoundException)
+ {
+ continue;
+ }
+ }
+
+ // 创建自定义UTC+8时区
+ try
+ {
+ return TimeZoneInfo.CreateCustomTimeZone(
+ "China Standard Time",
+ TimeSpan.FromHours(8),
+ "China Standard Time",
+ "China Standard Time");
+ }
+ catch
+ {
+ return TimeZoneInfo.Utc; // 最终回退
+ }
+ }
+ ```
+
+4. **新增调试方法**:
+ ```csharp
+ // 获取当前使用的时区信息
+ public static string GetTimeZoneInfo()
+
+ // 检查时区是否正确初始化
+ public static bool IsTimeZoneCorrectlyInitialized()
+ ```
+
+5. **设计优势**:
+ - **跨平台兼容**:支持Windows、Linux、macOS等不同操作系统
+ - **健壮性**:多层回退机制确保在任何环境下都能正常工作
+ - **调试友好**:提供时区信息查询方法便于问题排查
+ - **向后兼容**:保持原有API不变,不影响现有代码
+ - **性能优化**:静态初始化,避免重复计算
+
+6. **修复的关键问题**:
+ - **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
+ - **跨平台兼容性**:不同操作系统使用不同的时区ID格式
+ - **初始化失败**:静态构造函数异常导致整个类无法使用
+ - **调试困难**:缺少时区状态查询方法
+
+**影响范围**:
+- 时间戳工具类的跨平台兼容性
+- 系统启动时的稳定性
+- 时间转换功能的可靠性
+- 调试和问题排查能力
+
+**后续优化**:
+- 网络启动性能监控
+- 调试和问题定位
+- 日志记录详细程度
+- 性能优化分析
+
+### ProtocolWsClientManager方法参数优化
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **简化构造函数**
+ - 移除 `ProtocolClientConfig[] configs` 参数
+ - 构造函数只保留必要的依赖:`ILogger`、`IProtocolLogObserver`、`ILoggerFactory`
+ - 移除私有字段 `_configs`,不再在构造函数中存储配置
+
+2. **修改StartAllClients方法签名**
+ - 添加 `ProtocolClientConfig[] configs` 参数
+ - 方法接收配置数组作为参数,而不是依赖构造函数中的配置
+ - 添加参数验证,检查 `configs` 是否为 null 或空数组
+
+3. **优化方法逻辑**
+ - 将配置验证移到方法开始处
+ - 使用传入的 `configs` 参数替代私有字段
+ - 保持原有的客户端创建和启动逻辑不变
+
+4. **具体实现**:
+ ```csharp
+ // 构造函数简化
+ public ProtocolWsClientManager(
+ ILogger logger,
+ IProtocolLogObserver protocolLogObserver,
+ ILoggerFactory loggerFactory) // 移除 configs 参数
+
+ // StartAllClients方法修改
+ public void StartAllClients(ProtocolClientConfig[] configs) // 添加参数
+ {
+ if (configs == null || configs.Length == 0) // 参数验证
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ return;
+ }
+
+ // 使用传入的 configs 参数
+ _logger.LogInformation("开始启动所有协议客户端,配置数量: {ConfigCount}", configs.Length);
+ foreach (var config in configs) // 遍历传入的配置
+ ```
+
+5. **设计优势**:
+ - **更灵活的使用方式**:可以在不同时间传入不同的配置
+ - **减少内存占用**:不需要在构造函数中存储配置数组
+ - **简化构造函数**:降低构造函数的复杂度
+ - **更好的测试性**:可以更容易地测试不同的配置组合
+ - **符合单一职责原则**:构造函数只负责初始化,方法负责执行具体操作
+
+**影响范围**:
+- 协议客户端管理器使用方式
+- 配置传递方式
+- 调用方代码适配
+- 测试用例更新
+
+### ProtocolWsClientManager采用面向接口编程
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **创建IProtocolWsClientManager接口**
+ - 在CoreAgent.ProtocolClient项目中定义接口契约
+ - 继承 `IDisposable` 接口
+ - 定义 `StartAllClients` 和 `StopAllClients` 方法
+ - 使用 `ProtocolClientConfig[]` 作为参数类型
+
+2. **修改ProtocolWsClientManager实现类**
+ - 实现 `IProtocolWsClientManager` 接口
+ - 添加 `using CoreAgent.ProtocolClient.Interfaces;` 引用
+ - 保持原有的实现逻辑不变
+
+3. **具体实现**:
+ ```csharp
+ // 接口定义
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ void StartAllClients(ProtocolClientConfig[] configs);
+ void StopAllClients();
+ }
+
+ // 实现类
+ public class ProtocolWsClientManager : IProtocolWsClientManager
+ {
+ // 原有实现保持不变
+ }
+ ```
+
+4. **设计优势**:
+ - **依赖倒置**:高层模块依赖抽象,不依赖具体实现
+ - **易于测试**:可以轻松创建Mock实现进行单元测试
+ - **松耦合**:降低组件间的耦合度
+ - **可扩展性**:可以轻松添加新的实现类
+ - **符合SOLID原则**:遵循依赖倒置原则和开闭原则
+ - **便于依赖注入**:可以注册接口而不是具体实现
+
+5. **接口设计原则**:
+ - **单一职责**:接口只定义协议客户端管理的核心功能
+ - **简洁明了**:只包含必要的方法定义
+ - **易于理解**:方法名称和参数清晰明确
+ - **向后兼容**:保持与原有API的兼容性
+
+**影响范围**:
+- 依赖注入配置更新
+- 服务注册方式调整
+- 单元测试Mock创建
+- 调用方代码适配(使用接口类型)
+- 项目引用关系调整(CoreAgent.ProtocolClient项目包含接口定义)
+
+### CellularNetworkService集成IProtocolWsClientManager
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加IProtocolWsClientManager依赖注入**
+ - 在构造函数中添加 `IProtocolWsClientManager protocolWsClientManager` 参数
+ - 添加私有字段 `_protocolWsClientManager` 存储依赖
+ - 添加空值检查和异常抛出
+
+2. **StartNetworkAsync方法添加第12步**
+ - 在步骤11(更新网络状态)之后添加第12步
+ - 调用 `protocolConfigFactory.GetAllConfigs()` 获取配置数组
+ - 调用 `_protocolWsClientManager.StartAllClients(protocolConfigs)` 启动所有协议客户端
+ - 添加时间跟踪和错误处理
+ - 如果启动失败,立即返回失败结果
+
+3. **StopAsync方法集成协议客户端停止**
+ - 在步骤4(禁用网络配置)之后添加步骤5(停止所有协议客户端)
+ - 调用 `_protocolWsClientManager.StopAllClients()` 停止所有协议客户端
+ - 添加错误处理,但不中断停止流程
+ - 重新编号后续步骤(6-9步)
+
+4. **修复StopWebSocketTransportAsync方法**
+ - 修正方法实现,使用 `CloseAsync()` 而不是 `DisconnectAsync()`
+ - 修正日志信息和返回值逻辑
+ - 确保方法名称和实现一致
+
+5. **具体实现**:
+ ```csharp
+ // 构造函数添加依赖
+ public CellularNetworkService(
+ // ... 其他参数
+ IProtocolWsClientManager protocolWsClientManager)
+
+ // StartNetworkAsync第12步
+ // 12. 启动所有协议客户端
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ _protocolWsClientManager.StartAllClients(protocolConfigs);
+
+ // StopAsync步骤5
+ // 5. 停止所有协议客户端
+ _protocolWsClientManager.StopAllClients();
+ ```
+
+6. **设计优势**:
+ - **完整的生命周期管理**:启动和停止时都正确处理协议客户端
+ - **错误隔离**:启动失败时立即停止,停止失败时继续执行
+ - **时间跟踪**:为协议客户端操作添加详细的时间记录
+ - **依赖注入**:使用接口编程,便于测试和扩展
+ - **日志完整**:提供详细的启动和停止日志记录
+
+**影响范围**:
+- 蜂窝网络服务依赖注入配置
+- 协议客户端生命周期管理
+- 网络启动和停止流程
+- 服务注册配置更新
+
+### LogLayerHelp类名规范化
+
+**修改时间**: 2024年
+**修改文件**:
+- `
+
+### ProtocolWsClientManager方法返回类型优化
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **StartAllClients方法返回类型修改**
+ - 将返回类型从 `void` 改为 `bool`
+ - 添加连接状态检查逻辑,使用 `client.IsConnected` 判断连接状态
+ - 统计已连接的客户端数量
+ - 返回 `bool` 值表示是否所有客户端都成功启动并连接
+ - 添加详细的日志记录,包括连接状态信息
+
+2. **StopAllClients方法返回类型修改**
+ - 将返回类型从 `void` 改为 `bool`
+ - 添加断开连接状态检查逻辑,使用 `client.IsConnected` 判断连接状态
+ - 记录停止前的连接状态和停止后的连接状态
+ - 统计已断开连接的客户端数量
+ - 返回 `bool` 值表示是否所有客户端都成功停止并断开连接
+ - 添加详细的日志记录,包括连接状态变化信息
+
+3. **GetAllClientsStatus方法修复**
+ - 修复语法错误,完善方法实现
+ - 添加线程安全锁保护
+ - 遍历所有客户端并记录其状态信息
+ - 包括客户端名称、连接状态(IsConnected)和客户端状态(State)
+ - 添加空客户端检查
+
+4. **接口定义更新**
+ - 更新 `IProtocolWsClientManager` 接口中的方法签名
+ - `StartAllClients` 方法返回 `bool` 类型
+ - `StopAllClients` 方法返回 `bool` 类型
+ - 添加详细的XML文档注释说明返回值含义
+
+5. **具体实现**:
+ ```csharp
+ // StartAllClients方法
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected;
+ }
+
+ // StopAllClients方法
+ public bool StopAllClients()
+ {
+ var client = kvp.Value;
+ var wasConnected = client.IsConnected;
+
+ client.Stop();
+
+ // 检查连接状态
+ if (!client.IsConnected)
+ {
+ disconnectedCount++;
+ }
+
+ var allDisconnected = disconnectedCount == _clients.Count;
+ return allDisconnected;
+ }
+
+ // GetAllClientsStatus方法
+ public void GetAllClientsStatus()
+ {
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ _logger.LogInformation("客户端状态 - 名称: {ClientName}, 连接状态: {IsConnected}, 客户端状态: {State}",
+ kvp.Key, client.IsConnected, client.State);
+ }
+ }
+ ```
+
+6. **设计优势**:
+ - **状态可追踪**:通过返回值可以明确知道操作是否完全成功
+ - **连接状态监控**:使用 `IsConnected` 属性准确判断连接状态
+ - **详细日志记录**:提供完整的操作过程和状态变化日志
+ - **线程安全**:使用锁保护共享资源访问
+ - **错误处理完善**:提供详细的错误信息和状态统计
+ - **接口一致性**:接口和实现保持完全一致
+ - **向后兼容**:保持方法签名的一致性,只改变返回类型
+
+7. **返回值含义**:
+ - `StartAllClients` 返回 `true`:所有客户端都成功启动并连接
+ - `StartAllClients` 返回 `false`:部分或全部客户端启动失败或未连接
+ - `StopAllClients` 返回 `true`:所有客户端都成功停止并断开连接
+ - `StopAllClients` 返回 `false`:部分或全部客户端停止失败或仍保持连接
+
+**影响范围**:
+- 协议客户端管理器接口契约
+- 调用方代码需要处理返回值
+- 网络启动和停止流程的状态判断
+- 日志记录详细程度提升
+- 错误处理和状态监控能力增强
+
+### WebSocket传输服务依赖注入修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Extensions/WebSocketTransportExtensions.cs`
+- `CoreAgent.API/Startup.cs`
+
+**修改内容**:
+
+1. **修复依赖注入顺序问题**
+ - 将 `RegisterDefaultMiddleware` 移到 `RegisterCoreServices` 之前调用
+ - 确保中间件在核心服务注册之前就已经注册到容器中
+ - 解决 `provider.GetServices()` 无法找到服务的问题
+
+2. **修复CacheMiddleware注册方式**
+ - 将 `CacheMiddleware` 的注册方式从手动工厂方法改为使用 `AddWebSocketMiddleware`
+ - 简化注册逻辑,确保依赖注入容器能正确处理构造函数参数
+ - 移除复杂的工厂方法注册,使用标准的依赖注入模式
+
+3. **添加IMemoryCache服务注册**
+ - 在 `Startup.cs` 的 `ConfigureServices` 方法中添加 `services.AddMemoryCache()`
+ - 确保 `CacheMiddleware` 能够正确获取 `IMemoryCache` 依赖
+ - 在 WebSocket 传输服务注册之前添加内存缓存服务
+
+4. **具体实现**:
+ ```csharp
+ // WebSocketTransportExtensions.cs - 调整注册顺序
+ public static IServiceCollection AddWebSocketTransport(...)
+ {
+ // 注册配置
+ services.Configure(...);
+
+ // 注册默认中间件(在核心服务之前)
+ RegisterDefaultMiddleware(services);
+
+ // 注册核心服务
+ RegisterCoreServices(services);
+
+ return services;
+ }
+
+ // Startup.cs - 添加内存缓存服务
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // ... 其他服务注册 ...
+
+ // 添加内存缓存服务(WebSocket中间件需要)
+ services.AddMemoryCache();
+
+ // 添加 WebSocket 传输服务
+ services.AddWebSocketTransport(Configuration, "WebSocket");
+ }
+ ```
+
+5. **设计优势**:
+ - **依赖顺序正确**:确保中间件在核心服务之前注册
+ - **简化注册逻辑**:使用标准的依赖注入模式
+ - **完整的服务注册**:包含所有必要的依赖服务
+ - **错误预防**:避免运行时依赖注入异常
+ - **代码清晰**:注册逻辑更加直观和易于理解
+
+6. **修复的问题**:
+ - `System.InvalidOperationException: Cannot resolve scoped service 'System.Collections.Generic.IEnumerable`1[CoreAgent.WebSocketTransport.Middleware.IMessageMiddleware]' from root provider`
+ - 依赖注入容器无法找到 `IMessageMiddleware` 服务
+ - `CacheMiddleware` 无法获取 `IMemoryCache` 依赖
+
+**影响范围**:
+- WebSocket传输服务的依赖注入配置
+- 中间件注册和初始化顺序
+- 应用程序启动时的服务注册
+- 内存缓存服务的可用性
+- 错误处理和异常预防
+
+### CacheMiddleware构造函数依赖注入修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Middleware/CacheMiddleware.cs`
+
+**修改内容**:
+
+1. **修复构造函数参数类型**
+ - 将构造函数参数从 `WebSocketConfig config` 改为 `IOptions config`
+ - 添加 `using Microsoft.Extensions.Options;` 引用
+ - 在构造函数中通过 `config?.Value` 获取配置值
+
+2. **增强空值检查**
+ - 为所有构造函数参数添加空值检查和异常抛出
+ - 使用 `ArgumentNullException` 确保参数有效性
+ - 提供更明确的错误信息
+
+3. **具体实现**:
+ ```csharp
+ // 修复前
+ public CacheMiddleware(IMemoryCache cache, ILogger logger, WebSocketConfig config)
+ {
+ _cache = cache;
+ _logger = logger;
+ _config = config;
+ }
+
+ // 修复后
+ public CacheMiddleware(IMemoryCache cache, ILogger logger, IOptions config)
+ {
+ _cache = cache ?? throw new ArgumentNullException(nameof(cache));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _config = config?.Value ?? throw new ArgumentNullException(nameof(config));
+ }
+ ```
+
+4. **设计优势**:
+ - **正确的依赖注入模式**:使用 `IOptions` 模式获取配置
+ - **强化的错误处理**:提供详细的空值检查和异常信息
+ - **类型安全**:确保配置对象正确获取
+ - **符合最佳实践**:遵循 .NET 依赖注入的标准模式
+
+5. **修复的问题**:
+ - `Unable to resolve service for type 'CoreAgent.WebSocketTransport.Models.WebSocketConfig'`
+ - 依赖注入容器无法直接解析 `WebSocketConfig` 类型
+ - 中间件构造函数参数类型不匹配
+
+**影响范围**:
+- CacheMiddleware的依赖注入配置
+- WebSocket传输服务的启动
+- 配置对象的正确获取
+- 错误处理和异常预防
+
+### WebSocket中间件生命周期修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Extensions/WebSocketTransportExtensions.cs`
+
+**修改内容**:
+
+1. **修复生命周期不匹配问题**
+ - 将中间件注册从 `AddScoped` 改为 `AddTransient`
+ - 解决单例服务无法解析作用域服务的问题
+ - 确保中间件每次使用都创建新实例
+
+2. **生命周期分析**
+ - `IWebSocketTransport` 注册为 `Singleton`(单例)
+ - 中间件注册为 `Transient`(瞬时)
+ - 单例服务可以安全地解析瞬时服务
+ - 每次获取中间件都会创建新实例
+
+3. **具体实现**:
+ ```csharp
+ // 修复前
+ services.AddScoped();
+
+ // 修复后
+ services.AddTransient();
+ ```
+
+4. **设计优势**:
+ - **生命周期兼容**:瞬时服务可以被单例服务安全解析
+ - **性能优化**:中间件每次使用都是新实例,避免状态污染
+ - **线程安全**:瞬时服务天然线程安全
+ - **内存管理**:中间件使用完毕后自动释放
+
+5. **修复的问题**:
+ - `Cannot resolve scoped service 'System.Collections.Generic.IEnumerable`1[CoreAgent.WebSocketTransport.Middleware.IMessageMiddleware]' from root provider`
+ - 单例服务无法解析作用域服务的依赖注入异常
+ - 生命周期不匹配导致的运行时错误
+
+6. **生命周期说明**:
+ - **Singleton**: 整个应用程序生命周期内只有一个实例
+ - **Scoped**: 每个请求作用域内有一个实例
+ - **Transient**: 每次请求都创建新实例
+ - 单例服务只能解析瞬时服务,不能解析作用域服务
+
+**影响范围**:
+- WebSocket中间件的生命周期管理
+- 依赖注入容器的服务解析
+- 应用程序启动时的服务注册
+- 中间件的实例化策略
+- 性能和内存使用优化
+
+### NetworkConfigCopier方法Bug修复和日志增强
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+- `CoreAgent.Domain/Models/Network/NetworkConfigCopyResult.cs`
+- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
+
+**修改内容**:
+
+1. **返回类型统一化**
+ - **统一返回类型**:所有方法都返回 `NetworkConfigCopyResult` 类型,不再抛出异常
+ - **泛型结果类型**:创建 `NetworkConfigCopyResult` 泛型类,支持返回数据
+ - **接口更新**:更新 `INetworkConfigCopier` 接口以匹配新的返回类型
+ - **错误处理改进**:使用结果对象而不是异常来处理错误情况
+
+2. **CreateCellularNetworkConfigurationFile方法修复**
+ - **返回类型修改**:从 `Task` 改为 `Task`
+ - **路径构建Bug修复**:修复 `Path.Combine` 使用不当的问题,正确构建文件路径
+ - **参数验证增强**:添加完整的参数空值检查,返回失败结果而不是抛出异常
+ - **目录创建**:确保目标目录存在,避免文件写入失败
+ - **详细日志**:添加每个步骤的详细日志记录,包括开始、成功、警告和错误信息
+
+3. **CreateRadioAccessNetworkConfigurationFile方法修复**
+ - **返回类型修改**:从 `Task` 改为 `Task`
+ - **参数命名规范化**:使用小写开头的参数名,符合C#命名约定
+ - **目录创建逻辑**:添加目录存在性检查和自动创建
+ - **错误处理增强**:返回失败结果而不是抛出异常
+ - **参数验证**:验证文件路径和配置内容不为空
+
+4. **CreateCoreNetworkImsConfigurationFiles方法重构**
+ - **返回类型修改**:从 `Task>` 改为 `Task>>`
+ - **方法重命名**:避免与RAN配置文件创建方法名冲突
+ - **参数验证完善**:验证RuntimeCode、配置列表和AppSettings
+ - **配置项验证**:验证每个配置项的PLMN和配置内容
+ - **目录创建**:为CN和IMS配置文件分别创建目录
+ - **错误隔离**:单个配置项失败不影响其他配置项处理
+ - **详细日志**:记录每个配置项的处理过程和结果
+
+5. **DeleteCellularNetworkConfigurationFile方法增强**
+ - **返回类型修改**:从 `bool` 改为 `NetworkConfigCopyResult`
+ - **文件存在性检查**:删除前检查文件是否存在,避免异常
+ - **错误隔离**:单个文件删除失败不影响其他文件删除
+ - **删除统计**:统计成功删除的文件数量
+ - **详细日志**:记录每个文件的删除状态和结果
+ - **参数验证**:验证NetworkConfiguration参数不为空
+
+6. **NetworkConfigCopyResult类扩展**
+ - **泛型支持**:添加 `NetworkConfigCopyResult` 泛型类
+ - **数据返回**:支持返回操作结果的同时返回数据
+ - **继承关系**:泛型类继承自基础类,保持类型安全
+ - **静态工厂方法**:提供 `Success(T data)` 和 `Failure(string errorMessage)` 方法
+
+7. **具体修复的Bug**:
+ ```csharp
+ // 修复前 - 路径构建错误
+ string RanConfigPath = $"{appSettings.RanConfigDirectory}{Path.Combine("RAN", $"{cellular.RuntimeCode}.cfg")}";
+
+ // 修复后 - 正确的路径构建
+ string ranConfigPath = Path.Combine(appSettings.RanConfigDirectory, "RAN", $"{cellular.RuntimeCode}.cfg");
+
+ // 修复前 - 抛出异常
+ throw new ArgumentNullException(nameof(cellular));
+
+ // 修复后 - 返回失败结果
+ return NetworkConfigCopyResult.Failure("CellularNetworkConfiguration 参数为空");
+
+ // 修复前 - 返回原始类型
+ public async Task CreateCellularNetworkConfigurationFile(...)
+
+ // 修复后 - 返回结果类型
+ public async Task CreateCellularNetworkConfigurationFile(...)
+ ```
+
+8. **设计优势**:
+ - **统一错误处理**:所有方法都使用结果对象,避免异常传播
+ - **类型安全**:泛型结果类型确保类型安全
+ - **Bug修复**:修复路径构建、方法名冲突、文件删除等关键Bug
+ - **错误处理完善**:添加完整的错误处理和错误隔离机制
+ - **日志详细**:提供完整的操作跟踪和调试信息
+ - **参数验证**:确保所有输入参数的有效性
+ - **目录管理**:自动创建必要的目录结构
+ - **错误隔离**:单个操作失败不影响整体流程
+ - **命名规范**:遵循C#命名约定,提高代码可读性
+ - **方法职责清晰**:每个方法都有明确的职责和边界
+
+9. **修复的关键问题**:
+ - **异常处理不一致**:统一使用结果对象而不是异常
+ - **路径构建错误**:`Path.Combine` 使用不当导致路径错误
+ - **方法名冲突**:两个不同功能的方法使用相同名称
+ - **文件删除异常**:删除不存在的文件导致异常
+ - **目录不存在**:目标目录不存在导致文件写入失败
+ - **错误传播**:单个错误导致整个操作失败
+ - **日志缺失**:缺少关键操作的日志记录
+
+**影响范围**:
+- 网络配置文件创建和删除的稳定性
+- 错误处理和异常预防
+- 日志记录和调试能力
+- 代码可读性和维护性
+- 文件系统操作的可靠性
+- 配置管理的完整性
+- 接口契约的一致性
+- 调用方代码的错误处理方式
+
+### NetworkConfigCopier返回类型优化和GeneralCellularNetworkService适配
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **CreateCellularNetworkConfigurationFile返回类型优化**
+ - **返回类型修改**:从 `Task` 改为 `Task>`
+ - **数据返回**:成功时返回包含 `NetworkConfiguration` 对象的结果
+ - **接口更新**:更新接口定义以匹配新的返回类型
+ - **调用方适配**:修改调用方代码以正确使用返回的数据
+
+2. **GeneralCellularNetworkService调用修复**
+ - **返回类型适配**:修改调用方式以适配新的泛型返回类型
+ - **数据获取**:直接从结果对象的 `Data` 属性获取 `NetworkConfiguration`
+ - **错误处理改进**:使用 `IsSuccess` 属性检查操作是否成功
+ - **代码简化**:移除手动创建 `NetworkConfiguration` 的代码
+
+3. **接口定义更新**
+ - **泛型支持**:接口方法返回 `Task>`
+ - **文档更新**:更新XML文档注释说明返回的数据类型
+
+4. **具体修改**:
+ ```csharp
+ // NetworkConfigCopier.cs - 返回类型修改
+ // 修复前
+ public async Task CreateCellularNetworkConfigurationFile(...)
+ {
+ // ... 创建NetworkConfiguration对象
+ return NetworkConfigCopyResult.Success(); // 没有返回数据
+ }
+
+ // 修复后
+ public async Task> CreateCellularNetworkConfigurationFile(...)
+ {
+ // ... 创建NetworkConfiguration对象
+ return NetworkConfigCopyResult.Success(network); // 返回数据
+ }
+
+ // GeneralCellularNetworkService.cs - 调用修复
+ // 修复前 - 手动创建配置对象
+ var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!createResult.IsSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
+ }
+
+ var config = NetworkConfiguration.Create(cellular.RuntimeCode,
+ Path.Combine(_context.GetAppSettings().RanConfigDirectory, "RAN", $"{cellular.RuntimeCode}.cfg"),
+ new List());
+
+ // 修复后 - 直接使用返回的数据
+ var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!createResult.IsSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
+ }
+
+ var config = createResult.Data; // 直接获取返回的NetworkConfiguration对象
+ ```
+
+5. **设计优势**:
+ - **数据完整性**:返回完整的 `NetworkConfiguration` 对象,包含所有配置信息
+ - **类型安全**:泛型结果类型确保类型安全
+ - **代码简化**:调用方无需手动创建配置对象
+ - **错误处理统一**:保持统一的错误处理模式
+ - **接口一致性**:接口和实现保持完全一致
+
+6. **修复的关键问题**:
+ - **数据丢失**:原方法创建了 `NetworkConfiguration` 但没有返回
+ - **代码重复**:调用方需要手动创建配置对象
+ - **类型不匹配**:返回类型与实际需求不匹配
+ - **接口不一致**:接口定义与实际实现不一致
+
+**影响范围**:
+- 网络配置创建流程的数据完整性
+- 调用方代码的简化
+- 接口契约的一致性
+- 类型安全和错误处理
+
+### 修改CreateCellularNetworkConfigurationFile为元组返回
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **修改CreateCellularNetworkConfigurationFile返回类型**
+ - 将返回类型从 `Task>` 改为元组 `Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)>`
+ - 简化返回结构,直接返回三个值:是否成功、错误信息、网络配置对象
+
+2. **更新接口定义**
+ - 更新 `INetworkConfigCopier` 接口中的方法签名
+ - 更新XML文档注释说明新的返回类型
+
+3. **更新调用代码**
+ - 修改 `GeneralCellularNetworkService.cs` 中的调用逻辑
+ - 使用元组解构语法 `var (isSuccess, errorMessage, config) = await ...`
+ - 直接使用解构后的变量
+
+4. **具体实现**:
+ ```csharp
+ // 修改前
+ public async Task> CreateCellularNetworkConfigurationFile(...)
+ {
+ return NetworkConfigCopyResult.Failure("错误信息");
+ return NetworkConfigCopyResult.Success(network);
+ }
+
+ // 修改后
+ public async Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)> CreateCellularNetworkConfigurationFile(...)
+ {
+ return (false, "错误信息", null);
+ return (true, null, network);
+ }
+ ```
+
+5. **调用代码修改**:
+ ```csharp
+ // 修改前
+ var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!createResult.IsSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
+ }
+ var config = createResult.Data;
+
+ // 修改后
+ var (isSuccess, errorMessage, config) = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!isSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {errorMessage}");
+ }
+ ```
+
+6. **设计优势**:
+ - **简洁性**:元组返回比包装类更简洁
+ - **直观性**:直接返回三个值,语义更清晰
+ - **性能**:避免创建额外的包装对象
+ - **易用性**:使用元组解构语法,代码更简洁
+ - **类型安全**:保持强类型,编译时检查
+
+**影响范围**:
+- 网络配置创建方法的返回类型简化
+- 调用代码的简化
+- 接口定义的更新
+- 代码可读性的提升
+
+### 修改CreateCoreNetworkImsConfigurationFiles为元组返回
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+
+**修改内容**:
+
+1. **修改CreateCoreNetworkImsConfigurationFiles返回类型**
+ - 将返回类型从 `Task>>` 改为元组 `Task<(bool IsSuccess, string ErrorMessage, List CoreImsConfigs)>`
+ - 简化返回结构,直接返回三个值:是否成功、错误信息、核心IMS配置列表
+
+2. **更新调用代码**
+ - 修改 `CreateCellularNetworkConfigurationFile` 方法中对 `CreateCoreNetworkImsConfigurationFiles` 的调用
+ - 使用元组解构语法 `var (coreImsSuccess, coreImsError, coreImsConfigs) = await ...`
+ - 直接使用解构后的变量
+
+3. **具体实现**:
+ ```csharp
+ // 修改前
+ public async Task>> CreateCoreNetworkImsConfigurationFiles(...)
+ {
+ return NetworkConfigCopyResult>.Failure("错误信息");
+ return NetworkConfigCopyResult>.Success(list);
+ }
+
+ // 修改后
+ public async Task<(bool IsSuccess, string ErrorMessage, List CoreImsConfigs)> CreateCoreNetworkImsConfigurationFiles(...)
+ {
+ return (false, "错误信息", null);
+ return (true, null, list);
+ }
+ ```
+
+4. **调用代码修改**:
+ ```csharp
+ // 修改前
+ var coreImsResult = await CreateCoreNetworkImsConfigurationFiles(cellular.RuntimeCode, cellular.CoreNetworkImsConfigurations, appSettings);
+ if (!coreImsResult.IsSuccess)
+ {
+ return (false, $"创建核心网络和IMS配置文件失败: {coreImsResult.ErrorMessage}", null);
+ }
+ list = coreImsResult.Data;
+
+ // 修改后
+ var (coreImsSuccess, coreImsError, coreImsConfigs) = await CreateCoreNetworkImsConfigurationFiles(cellular.RuntimeCode, cellular.CoreNetworkImsConfigurations, appSettings);
+ if (!coreImsSuccess)
+ {
+ return (false, $"创建核心网络和IMS配置文件失败: {coreImsError}", null);
+ }
+ list = coreImsConfigs;
+ ```
+
+5. **设计优势**:
+ - **一致性**:与 `CreateCellularNetworkConfigurationFile` 方法保持相同的返回模式
+ - **简洁性**:元组返回比包装类更简洁
+ - **直观性**:直接返回三个值,语义更清晰
+ - **性能**:避免创建额外的包装对象
+ - **易用性**:使用元组解构语法,代码更简洁
+
+6. **接口说明**:
+ - `CreateCoreNetworkImsConfigurationFiles` 是内部方法,不在接口中定义
+ - 只在 `NetworkConfigCopier` 实现类中使用
+ - 保持接口的简洁性
+
+**影响范围**:
+- 核心网络和IMS配置文件创建方法的返回类型简化
+- 内部方法调用代码的简化
+- 代码一致性的提升
+- 性能的优化
+
+### ProtocolLogProcessor.ProcessLogDetails方法Info字段过滤修复
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs`
+
+**修改内容**:
+
+1. **修复foreach循环中的Info字段检查**
+ - 在 `ProcessLogDetails` 方法的 `foreach` 循环中移除 `Info` 字段不为空的检查
+ - 允许处理所有日志记录,包括 `Info` 为空的记录
+ - 保持完整的日志处理和输出功能
+
+2. **修复_protocolLogObserver.OnProtocolLogsReceived调用**
+ - 在调用 `_protocolLogObserver.OnProtocolLogsReceived(logDetails)` 之前过滤掉 `Info` 为空或无效的记录
+ - 使用 `!string.IsNullOrWhiteSpace(detail.Info)` 进行过滤,确保Info字段不为null、空字符串或只包含空白字符
+ - 确保传递给观察者的数据中只包含有效的 `Info` 字段
+ - 添加空集合检查,避免传递空集合给观察者
+
+3. **具体实现**:
+ ```csharp
+ // 修复前
+ foreach (var detail in logDetails)
+ {
+ try
+ {
+ // 检查Info字段不为空
+ if (!string.IsNullOrEmpty(detail.Info))
+ {
+ Console.WriteLine($"处理日志详情:Time={detail.Time} Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI},PLMN={detail.PLMN},INFO={detail.Info},Messsage={detail.Message}");
+ // 这里可以添加具体的业务处理逻辑
+ // 例如:保存到数据库、发送到其他服务等
+ _logger.LogDebug($"处理日志详情: Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"处理日志详情失败: Layer={detail.LayerType}, UEID={detail.UEID}");
+ }
+ }
+
+ // 通知协议日志观察者处理转换后的数据
+ try
+ {
+ // 过滤掉Info为空的记录
+ var filteredLogDetails = logDetails.Where(detail => !string.IsNullOrEmpty(detail.Info)).ToList();
+ _protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "通知协议日志观察者失败");
+ }
+
+ // 修复后
+ foreach (var detail in logDetails)
+ {
+ try
+ {
+ Console.WriteLine($"处理日志详情:Time={detail.Time} Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI},PLMN={detail.PLMN},INFO={detail.Info},Messsage={detail.Message}");
+ // 这里可以添加具体的业务处理逻辑
+ // 例如:保存到数据库、发送到其他服务等
+ _logger.LogDebug($"处理日志详情: Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI}");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"处理日志详情失败: Layer={detail.LayerType}, UEID={detail.UEID}");
+ }
+ }
+
+ // 通知协议日志观察者处理转换后的数据
+ try
+ {
+ // 添加打印输出 - 通知观察者前的日志
+ _logger.LogInformation("开始通知协议日志观察者处理转换后的数据,日志详情数量: {LogDetailsCount}", logDetails.Count);
+
+ // 过滤掉Info为空或无效的记录
+ var filteredLogDetails = logDetails.Where(detail => !string.IsNullOrWhiteSpace(detail.Info)).ToList();
+ if (filteredLogDetails.Any())
+ {
+ _protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
+ }
+ else
+ {
+ _logger.LogWarning("过滤后的日志详情为空,不通知协议日志观察者");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "通知协议日志观察者失败");
+ }
+ ```
+
+4. **修复优势**:
+ - **完整日志处理**:允许处理所有日志记录,包括 `Info` 为空的记录
+ - **观察者数据过滤**:过滤掉 `Info` 为空或无效的记录,确保传递给观察者的数据完整
+ - **空集合检查**:添加空集合检查,避免传递空集合给观察者
+ - **日志详细**:添加详细的日志输出,便于调试和监控
+ - **错误处理**:完整的异常捕获和处理机制
+ - **业务连续性**:异常被捕获并记录,但不会中断主程序流程
+ - **功能保持**:仍然能够正确停止和重新创建通道管理器
+ - **日志完善**:提供了详细的调试和错误日志信息
+
+### 创建蜂窝网络配置实体类
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs` (新建)
+
+**修改内容**:
+
+1. **创建CellularNetworkConfiguration实体类**
+ - 包含设备代码(DeviceCode)字符串属性
+ - 包含运行时代码(RuntimeCode)字符串属性
+ - 包含无线接入网配置(RadioAccessNetworkConfiguration)字符串属性
+ - 包含核心网IMS配置集合(List)属性
+
+2. **创建CoreNetworkImsConfiguration实体类**
+ - 包含索引(Index)整数属性
+ - 包含PLMN标识(Plmn)字符串属性
+ - 包含核心网配置(CoreNetworkConfiguration)字符串属性
+ - 包含IMS服务配置(ImsServiceConfiguration)字符串属性
+
+3. **具体实现**:
+ ```csharp
+ ///
+ /// 蜂窝网络配置实体
+ ///
+ public class CellularNetworkConfiguration
+ {
+ ///
+ /// 设备代码
+ ///
+ public string DeviceCode { get; set; }
+
+ ///
+ /// 运行时代码
+ ///
+ public string RuntimeCode { get; set; }
+
+ ///
+ /// 无线接入网配置
+ ///
+ public string RadioAccessNetworkConfiguration { get; set; }
+
+ ///
+ /// 核心网IMS配置集合
+ ///
+ public List CoreNetworkImsConfigurations { get; set; } = new List();
+ }
+
+ ///
+ /// 核心网IMS配置对象
+ ///
+ public class CoreNetworkImsConfiguration
+ {
+ ///
+ /// 索引
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// PLMN标识
+ ///
+ public string Plmn { get; set; }
+
+ ///
+ /// 核心网配置
+ ///
+ public string CoreNetworkConfiguration { get; set; }
+
+ ///
+ /// IMS服务配置
+ ///
+ public string ImsServiceConfiguration { get; set; }
+ }
+ ```
+
+4. **设计优势**:
+ - **命名规范**:遵循C#命名约定,使用PascalCase
+ - **命名清晰**:类名明确表达业务含义,提高代码可读性
+ - **属性专业**:使用完整的专业术语,避免缩写和模糊命名
+ - **类型安全**:使用强类型属性,避免类型错误
+ - **文档完整**:每个属性都有详细的XML文档注释
+ - **集合初始化**:使用集合初始化器确保集合不为null
+ - **职责清晰**:每个类都有明确的职责和用途
+ - **易于扩展**:结构清晰,便于后续添加新属性
+ - **业务导向**:类名直接反映业务领域概念
+ - **专业术语**:使用标准的电信网络术语,提高代码专业性
+
+### 添加蜂窝网络配置数据校验功能
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs`
+
+**修改内容**:
+
+1. **添加数据校验功能**
+ - 为 `DeviceCode` 和 `RuntimeCode` 添加 `[Required]` 特性,确保不能为空
+ - 为 `Plmn` 添加 `[Required]` 特性,确保不能为空
+ - 添加 `ValidateConfiguration()` 方法进行业务逻辑校验
+
+2. **业务规则校验**
+ - 验证 `DeviceCode` 和 `RuntimeCode` 不能为空
+ - 验证 `RadioAccessNetworkConfiguration` 和 `CoreNetworkImsConfigurations` 至少有一个有数据
+ - 验证 `CoreNetworkImsConfiguration` 中的 `CoreNetworkConfiguration` 和 `ImsServiceConfiguration` 至少有一个有数据
+
+3. **创建ValidationResult类**
+ - 提供统一的验证结果返回格式
+ - 支持成功和失败两种状态
+ - 提供详细的错误消息
+ - 支持隐式转换操作符
+
+4. **具体实现**:
+ ```csharp
+ // 必填字段校验
+ [Required(ErrorMessage = "设备代码不能为空")]
+ public string DeviceCode { get; set; }
+
+ [Required(ErrorMessage = "运行时代码不能为空")]
+ public string RuntimeCode { get; set; }
+
+ // 业务逻辑校验
+ public ValidationResult ValidateConfiguration()
+ {
+ // 验证必填字段
+ if (string.IsNullOrWhiteSpace(DeviceCode))
+ {
+ return new ValidationResult("设备代码不能为空");
+ }
+
+ // 验证无线接入网配置和核心网IMS配置至少有一个有数据
+ var hasRadioAccessConfig = !string.IsNullOrWhiteSpace(RadioAccessNetworkConfiguration);
+ var hasCoreNetworkConfigs = CoreNetworkImsConfigurations?.Any() == true;
+
+ if (!hasRadioAccessConfig && !hasCoreNetworkConfigs)
+ {
+ return new ValidationResult("无线接入网配置和核心网IMS配置至少需要配置其中一项");
+ }
+
+ return ValidationResult.Success;
+ }
+ ```
+
+5. **设计优势**:
+ - **数据完整性**:确保必填字段不为空
+ - **业务规则校验**:验证业务逻辑的正确性
+ - **统一验证接口**:提供一致的验证方法
+ - **详细错误信息**:提供具体的错误描述
+ - **分层校验**:支持嵌套对象的校验
+ - **易于扩展**:可以轻松添加新的校验规则
+
+**影响范围**:
+- 蜂窝网络配置数据模型定义
+- 核心网IMS配置管理
+- 数据实体结构标准化
+- 领域模型完整性
+- 代码可读性和维护性提升
+- 数据校验和业务规则验证
+- 错误处理和用户反馈
+
+### 创建MessageTransferProtocolLog模型解决命名冲突
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Models/MessageTransferProtocolLog.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs`
+
+**修改内容**:
+
+1. **创建MessageTransferProtocolLog模型**
+ - 在 `CoreAgent.WebSocketTransport` 项目中创建新的协议日志模型
+ - 与 `CoreAgent.ProtocolClient` 中的 `TransferProtocolLog` 区分开
+ - 保持相同的字段结构,避免命名冲突
+ - 专门用于WebSocket传输层的协议日志数据传输
+
+2. **修改NetworkProtocolLogObserver转换逻辑**
+ - 在 `OnProtocolLogsReceived` 方法中添加类型转换
+ - 将 `CoreAgent.ProtocolClient.TransferProtocolLog` 转换为 `CoreAgent.WebSocketTransport.MessageTransferProtocolLog`
+ - 保持所有字段的完整映射
+ - 添加必要的using语句引用
+
+3. **具体实现**:
+ ```csharp
+ // 时间跟踪和性能监控
+ var startTime = DateTime.UtcNow;
+
+ // 空值检查
+ if (logDetails == null)
+ {
+ _logger.LogWarning("接收到的协议日志为空");
+ return;
+ }
+
+ // 转换为列表以避免多次枚举
+ var logList = logDetails.ToList();
+ var logCount = logList.Count;
+
+ // 空集合检查
+ if (logCount == 0)
+ {
+ _logger.LogDebug("接收到的协议日志集合为空,跳过处理");
+ return;
+ }
+
+ // 类型转换逻辑
+ var webSocketLogs = logList.Select(log => new MessageTransferProtocolLog
+ {
+ Id = log.Id,
+ LayerType = log.LayerType.ToString(),
+ MessageDetailJson = log.MessageDetailJson,
+ CellID = log.CellID,
+ IMSI = log.IMSI,
+ Direction = log.Direction,
+ UEID = log.UEID,
+ PLMN = log.PLMN,
+ TimeMs = log.TimeMs,
+ Timestamp = log.Timestamp,
+ Info = log.Info,
+ Message = log.Message
+ });
+
+ // 通道写入状态监控
+ var writeSuccess = _ChannelManager.SendChannel.TryWrite(webSocketLogs);
+ var processingTime = DateTime.UtcNow - startTime;
+
+ if (writeSuccess)
+ {
+ _logger.LogDebug("协议日志处理成功,数量: {LogCount}, 处理时间: {ProcessingTime}ms",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ else
+ {
+ _logger.LogWarning("协议日志写入通道失败,数量: {LogCount}, 处理时间: {ProcessingTime}ms, 通道可能已满或已关闭",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ ```
+
+4. **设计优势**:
+ - **命名清晰**:`MessageTransferProtocolLog` 明确表示用于消息传输
+ - **避免冲突**:与原始 `TransferProtocolLog` 有明确区分
+ - **职责分离**:WebSocket传输层有独立的协议日志模型
+ - **类型安全**:通过显式转换确保类型安全
+ - **易于维护**:清晰的命名约定便于理解和维护
+ - **性能监控**:添加时间跟踪和通道写入状态监控
+ - **错误处理**:完善的异常处理和日志记录
+ - **Bug修复**:修复空引用检查和多次枚举的性能问题
+ - **边界处理**:添加空集合检查,避免处理空集合
+ - **代码规范**:优化ProtocolMessage模型注释,提高代码可读性
+
+**影响范围**:
+- WebSocket传输层协议日志处理
+- 协议日志观察者模式实现
+- 跨项目类型转换逻辑
+- 协议消息模型注释优化
+
+### CellularNetworkService.StartNetworkAsync 方法添加协议客户端配置创建
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **调整执行步骤顺序**
+ - 将原来的第5.5步改为第6步(创建协议客户端配置)
+ - 将WebSocket传输连接移到第7步(在网络配置启动之前)
+ - 重新编号后续步骤(8-11步)
+ - **优化执行顺序**:确保WebSocket连接在网络配置启动之前完成
+
+2. **添加第7步WebSocket传输连接**
+ - 在 `StartNetworkAsync` 方法的第6步(创建协议客户端配置)之后添加第7步
+ - 注入 `IWebSocketTransport` 依赖
+ - 创建独立的 `StartWebSocketTransportAsync()` 方法处理连接逻辑
+ - 返回 `bool` 值表示连接是否成功
+ - 添加连接状态检查和错误处理
+ - **严格检查**:WebSocket连接失败时立即返回失败结果,提示服务端可能未启动
+
+3. **修复Logger类型问题**
+ - 添加 `ILoggerFactory` 依赖注入到构造函数
+ - 使用 `_loggerFactory.CreateLogger()` 创建正确类型的Logger
+ - 确保 `ProtocolClientConfigFactory` 获得正确的Logger实例
+
+3. **具体实现**:
+ ```csharp
+ // 6. 创建协议客户端配置
+ var protocolConfigFactory = new ProtocolClientConfigFactory(_loggerFactory.CreateLogger(), _context);
+ var configCreated = protocolConfigFactory.CreateFromEntities();
+ if (configCreated)
+ {
+ _logger.LogInformation("协议客户端配置创建成功,共创建 {ConfigCount} 个配置", protocolConfigFactory.ConfigCount);
+ }
+ else
+ {
+ _logger.LogWarning("协议客户端配置创建失败");
+ return CellularNetworkOperationResult.Failure("协议客户端配置创建失败");
+ }
+
+ // 7. 启动 WebSocket 传输连接
+ var webSocketConnected = await StartWebSocketTransportAsync();
+ if (!webSocketConnected)
+ {
+ _logger.LogError("WebSocket 传输连接启动失败,服务端可能未启动");
+ return CellularNetworkOperationResult.Failure("WebSocket 传输连接启动失败,服务端可能未启动");
+ }
+ _logger.LogInformation("WebSocket 传输连接启动成功");
+ ```
+
+4. **添加必要的依赖注入**
+ - 添加 `ILoggerFactory loggerFactory` 参数到构造函数
+ - 添加 `IWebSocketTransport webSocketTransport` 参数到构造函数
+ - 添加 `using CoreAgent.Infrastructure.Services.Network;` 以支持 `ProtocolClientConfigFactory`
+ - 添加 `using CoreAgent.WebSocketTransport.Interfaces;` 以支持 `IWebSocketTransport`
+
+5. **设计优势**:
+ - 在IP端点信息准备完成后立即创建协议客户端配置
+ - 不依赖网络启动结果,确保配置创建的独立性
+ - 在网络配置启动之前启动WebSocket传输连接
+ - 提供详细的日志记录便于调试
+ - 保持代码的简洁性和可维护性
+ - 正确处理Logger类型,避免类型不匹配问题
+ - 优化执行顺序,提高错误隔离能力
+ - 完善的错误处理机制,确保配置创建失败时及时停止
+ - 严格检查机制,WebSocket连接失败时立即停止网络启动流程
+ - 方法职责单一,WebSocket连接逻辑独立封装
+
+**影响范围**:
+- 蜂窝网络启动流程
+- 协议客户端配置管理
+- WebSocket传输服务集成
+- 网络状态监控
+- 依赖注入配置(需要更新服务注册)
+
+### CellularNetworkService构造函数添加IProtocolLogObserver依赖
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加IProtocolLogObserver依赖注入**
+ - 在构造函数中添加 `IProtocolLogObserver protocolLogObserver` 参数
+ - 添加私有字段 `_protocolLogObserver` 存储依赖
+ - 添加空值检查和异常抛出
+
+2. **添加必要的using语句**
+ - 添加 `using CoreAgent.ProtocolClient.ProtocolEngineCore;` 以支持 `IProtocolLogObserver`
+
+3. **具体实现**:
+ ```csharp
+ // 构造函数参数
+ public CellularNetworkService(
+ // ... 其他参数
+ IWebSocketTransport webSocketTransport,
+ IProtocolLogObserver protocolLogObserver)
+
+ // 私有字段
+ private readonly IProtocolLogObserver _protocolLogObserver;
+
+ // 构造函数初始化
+ _protocolLogObserver = protocolLogObserver ?? throw new ArgumentNullException(nameof(protocolLogObserver));
+ ```
+
+4. **设计优势**:
+ - 为后续协议客户端管理提供必要的依赖
+ - 保持依赖注入的一致性
+ - 提供空值检查确保服务稳定性
+ - 为协议日志观察者模式提供支持
+
+**影响范围**:
+- 蜂窝网络服务依赖注入配置
+- 协议客户端日志观察者集成
+- 服务注册配置更新
+
+### StartNetworkAsync方法添加时间跟踪记录
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加整体时间跟踪**
+ - 在方法开始时记录开始时间
+ - 在方法结束时计算总耗时并记录
+ - 使用UTC时间确保时间一致性
+
+2. **为每个步骤添加详细时间跟踪**
+ - 为11个步骤中的每个步骤添加开始和结束时间记录
+ - 使用 `LogDebug` 级别记录每个步骤的耗时
+ - 保持原有的 `LogInformation` 和 `LogError` 级别日志不变
+
+3. **具体实现**:
+ ```csharp
+ // 方法开始时间跟踪
+ var startTime = DateTime.UtcNow;
+ _logger.LogInformation("开始启动网络配置 {ConfigKey},开始时间: {StartTime}", key, startTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
+
+ // 每个步骤的时间跟踪
+ var stepXStart = DateTime.UtcNow;
+ _logger.LogDebug("步骤X开始:[步骤描述]");
+
+ // 步骤执行逻辑...
+
+ var stepXDuration = (DateTime.UtcNow - stepXStart).TotalMilliseconds;
+ _logger.LogDebug("步骤X完成:[步骤描述],耗时: {Duration}ms", stepXDuration.ToString("F2"));
+
+ // 方法结束时间跟踪
+ var endTime = DateTime.UtcNow;
+ var duration = endTime - startTime;
+ _logger.LogInformation("蜂窝网络配置 {ConfigKey} 启动成功,当前状态: {Status},总耗时: {Duration}ms",
+ key, state.CurrentStatus, duration.TotalMilliseconds.ToString("F2"));
+ ```
+
+4. **设计优势**:
+ - **性能监控**:可以识别网络启动过程中的性能瓶颈
+ - **调试支持**:详细的时间信息有助于问题定位和性能优化
+ - **日志分级**:使用Debug级别避免生产环境日志过多
+ - **时间精度**:使用毫秒级精度提供准确的性能数据
+ - **UTC时间**:确保时间记录的一致性和准确性
+ - **非侵入性**:不影响原有的业务逻辑和错误处理
+
+5. **跟踪的步骤**:
+ - 步骤1:获取并验证网络配置
+ - 步骤2:执行网络接口初始化命令
+ - 步骤3:复制配置值到临时目录
+ - 步骤4:获取并验证 IP 端点信息
+ - 步骤5:更新 IP 端点管理器
+ - 步骤6:创建协议客户端配置
+ - 步骤7:启动 WebSocket 传输连接
+ - 步骤8:启动网络配置
+ - 步骤9:更新网络配置类型
+ - 步骤10:检查网络端点连接状态
+ - 步骤11:更新网络状态
+
+ var filteredLogDetails = logDetails.Where(detail =>
+ !string.IsNullOrWhiteSpace(detail.Info)
+ ).ToList();
+
+ if (filteredLogDetails.Any())
+ {
+ _logger.LogInformation("过滤后有效日志记录数量: {FilteredCount},准备通知观察者", filteredLogDetails.Count);
+ _protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
+ _logger.LogInformation("成功通知协议日志观察者,已处理 {ProcessedCount} 条日志记录", filteredLogDetails.Count);
+ }
+ else
+ {
+ _logger.LogDebug("过滤后没有有效的日志记录,跳过观察者通知");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "通知协议日志观察者失败");
+ }
+ ```
+
+4. **设计优势**:
+ - **完整日志处理**:处理所有日志记录,包括Info为空的记录
+ - **数据质量保证**:确保传递给观察者的数据中只包含有效的Info字段
+ - **调试友好**:可以看到所有日志记录的详细信息,便于调试
+ - **观察者模式优化**:传递给观察者的数据更加纯净
+ - **错误预防**:避免因空Info字段导致的潜在问题
+
+5. **修复的问题**:
+ - **日志信息缺失**:原代码会跳过Info为空的记录,导致日志信息不完整
+ - **调试困难**:无法看到所有日志记录的详细信息
+ - **数据传递问题**:将无效数据传递给协议日志观察者
+ - **信息不完整**:处理过程中丢失了部分日志信息
+
+**影响范围**:
+- 协议日志处理的完整性
+- 日志输出的完整性
+- 协议日志观察者接收的数据质量
+- 调试和问题排查能力
+
+### GeneralCellularNetworkService.StartNetworkAsync方法步骤注释优化
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **重新注释StartNetworkAsync方法中的步骤**
+ - 为每个步骤添加更详细和清晰的注释说明
+ - 统一注释格式,使用"步骤X: 功能描述"的格式
+ - 为每个步骤添加功能说明,解释该步骤的具体作用
+ - 保持原有的时间跟踪和日志记录功能
+
+2. **具体修改的步骤注释**:
+ - **步骤1**: 创建蜂窝网络配置文件 - 根据传入的蜂窝网络配置和应用程序设置,创建临时配置文件
+ - **步骤2**: 执行网络接口初始化命令 - 初始化网络接口,确保网络环境准备就绪
+ - **步骤3**: 复制配置值到临时目录 - 将网络配置的关键参数复制到系统临时目录,供后续步骤使用
+ - **步骤4**: 获取并验证IP端点信息 - 从配置中提取通信地址信息,验证端点的有效性
+ - **步骤5**: 更新IP端点管理器 - 将获取到的端点信息更新到网络上下文中的端点管理器
+ - **步骤6**: 创建协议客户端配置 - 基于网络实体信息创建协议客户端配置,为后续的协议通信做准备
+ - **步骤7**: 启动WebSocket传输连接 - 建立与协议服务器的WebSocket连接,为数据传输做准备
+ - **步骤8**: 启动网络配置 - 启用网络接口配置,激活蜂窝网络连接
+ - **步骤9**: 更新网络配置类型 - 根据启动结果更新网络配置类型,记录当前使用的配置类型
+ - **步骤10**: 检查网络端点连接状态 - 验证所有网络端点的连接状态,确保网络连接正常
+ - **步骤11**: 更新网络状态 - 将网络状态标记为已启动,更新内部状态管理
+ - **步骤12**: 启动所有协议客户端 - 启动所有协议客户端,开始协议数据采集和传输
+
+3. **设计优势**:
+ - **注释清晰**:每个步骤都有明确的功能描述
+ - **格式统一**:使用一致的注释格式,提高可读性
+ - **功能说明**:详细解释每个步骤的作用和目的
+ - **易于理解**:帮助开发者快速理解网络启动流程
+ - **维护友好**:清晰的注释便于后续维护和修改
+
+4. **注释示例**:
+ ```csharp
+ // 步骤1: 创建蜂窝网络配置文件
+ // 根据传入的蜂窝网络配置和应用程序设置,创建临时配置文件
+ var step1Start = DateTime.UtcNow;
+ _logger.LogDebug("步骤1开始:创建蜂窝网络配置文件");
+
+ // 步骤2: 执行网络接口初始化命令
+ // 初始化网络接口,确保网络环境准备就绪
+ var step2Start = DateTime.UtcNow;
+ _logger.LogDebug("步骤2开始:执行网络接口初始化命令");
+ ```
+
+**影响范围**:
+- 网络启动流程的代码可读性
+- 开发者对网络启动过程的理解
+- 代码维护和调试的便利性
+- 新团队成员的学习成本
+
+### GeneralCellularNetworkService网络状态恢复机制优化
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **重新命名和优化恢复方法**
+ - 将 `StartRestore` 方法重命名为 `RestoreNetworkStateAsync`
+ - 添加完整的XML文档注释说明方法用途
+ - 返回 `bool` 类型表示恢复操作是否成功
+ - 移除直接抛出异常的代码,改为记录日志并返回失败状态
+
+2. **完善恢复流程**
+ - **步骤1**: 停止所有协议客户端
+ - **步骤2**: 关闭WebSocket传输连接
+ - **步骤3**: 执行网络接口初始化命令,重置网络状态
+ - **步骤4**: 重置网络上下文状态
+ - 每个步骤都有独立的异常处理,确保单个步骤失败不影响其他步骤
+
+3. **在步骤10之后的所有步骤中添加恢复机制**
+ - **步骤10**: 网络端点状态检查失败时调用恢复
+ - **步骤11**: 更新网络状态失败时调用恢复
+ - **步骤12**: 启动协议客户端失败时调用恢复
+ - 确保在任何步骤失败时都能正确清理资源
+
+4. **具体实现**:
+ ```csharp
+ // 恢复方法优化
+ private async Task RestoreNetworkStateAsync()
+ {
+ try
+ {
+ _logger.LogInformation("开始恢复网络状态");
+
+ // 1. 停止所有协议客户端
+ try
+ {
+ _protocolWsClientManager.StopAllClients();
+ _logger.LogDebug("协议客户端停止完成");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "停止协议客户端时出现异常,继续执行恢复流程");
+ }
+
+ // 2. 关闭WebSocket传输连接
+ try
+ {
+ if (_webSocketTransport.IsConnected)
+ {
+ await _webSocketTransport.CloseAsync();
+ _logger.LogDebug("WebSocket传输连接关闭完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "关闭WebSocket传输连接时出现异常,继续执行恢复流程");
+ }
+
+ // 3. 执行网络接口初始化命令,重置网络状态
+ try
+ {
+ var initResult = await _interfaceManager.ExecuteInitializeCommandsAsync(true);
+ if (!initResult.IsSuccess)
+ {
+ _logger.LogWarning("执行网络接口初始化命令失败: {ErrorMessage}", initResult.ErrorMessage);
+ }
+ else
+ {
+ _logger.LogDebug("网络接口初始化命令执行成功");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "执行网络接口初始化命令时出现异常");
+ }
+
+ // 4. 重置网络上下文状态
+ _context.Reset();
+ _logger.LogDebug("网络上下文状态重置完成");
+
+ _logger.LogInformation("网络状态恢复完成");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "恢复网络状态过程中出现未预期的异常");
+ return false;
+ }
+ }
+ ```
+
+5. **步骤10-12的恢复调用**:
+ ```csharp
+ // 步骤10: 网络端点状态检查失败
+ if (!statusCheckResult.IsSuccess)
+ {
+ var errorMessage = string.Join("; ", statusCheckResult.ErrorMessage);
+ _logger.LogWarning("网络端点状态检查未通过: {ErrorMessage}", errorMessage);
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"网络端点状态检查失败: {errorMessage}");
+ }
+
+ // 步骤11: 更新网络状态
+ var state = _context.GetNetworkState();
+ state.MarkAsStarted();
+
+ // 步骤12: 启动协议客户端失败
+ try
+ {
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+ _logger.LogInformation("所有协议客户端启动完成");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ ```
+
+6. **设计优势**:
+ - **完整的资源清理**:确保所有已创建的资源都被正确清理
+ - **错误隔离**:每个恢复步骤都有独立的异常处理,避免单个失败影响整体恢复
+ - **详细日志记录**:提供完整的恢复过程日志,便于问题排查
+ - **状态一致性**:确保网络状态在失败时能够恢复到初始状态
+ - **方法命名清晰**:`RestoreNetworkStateAsync` 明确表达方法功能
+ - **返回值有意义**:返回bool值表示恢复操作是否成功
+ - **异常处理完善**:不直接抛出异常,而是记录日志并返回失败状态
+
+**影响范围**:
+- 网络启动失败时的资源清理机制
+- 网络状态恢复的可靠性
+- 错误处理和日志记录的完整性
+- 系统稳定性和资源管理
+- 调试和问题排查能力
+
+### 优化GeneralCellularNetworkService中的RestoreNetworkStateAsync方法和启动流程
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `RestoreNetworkStateAsync` 方法返回布尔值但调用方不做判断,返回值无意义
+ - 步骤12(启动所有协议客户端)代码较长,需要提取为单独方法以提高可读性
+ - 需要确保只有步骤1成功时才继续执行后续步骤
+ - 删除临时配置文件这一步无论成功还是失败都需要执行
+
+2. **修复方案**:
+ - 将 `RestoreNetworkStateAsync` 方法改为 `void` 返回类型,移除不必要的返回值
+ - 提取步骤12的协议客户端启动逻辑到单独的私有方法 `StartAllProtocolClientsAsync`
+ - 重构 `StartNetworkAsync` 方法,将步骤1和删除临时配置文件分离
+ - 提取步骤2-12到新的私有方法 `ExecuteRemainingStepsAsync`
+ - 确保删除临时配置文件无论成功还是失败都会执行
+
+3. **具体修改**:
+ **RestoreNetworkStateAsync 方法优化**:
+ ```csharp
+ // 修改前
+ private async Task RestoreNetworkStateAsync()
+
+ // 修改后
+ private async Task RestoreNetworkStateAsync()
+ ```
+
+ **新增 StartAllProtocolClientsAsync 方法**:
+ ```csharp
+ ///
+ /// 启动所有协议客户端
+ ///
+ /// 协议配置工厂
+ /// 启动结果
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ ```
+
+ **新增 ExecuteRemainingStepsAsync 方法**:
+ ```csharp
+ ///
+ /// 执行剩余的启动步骤(步骤2-12)
+ ///
+ /// 网络配置
+ /// 配置键
+ /// 开始时间
+ /// 执行结果
+ private async Task ExecuteRemainingStepsAsync(object config, string key, DateTime startTime)
+ ```
+
+ **StartNetworkAsync 方法重构**:
+ ```csharp
+ // 步骤1成功后,调用ExecuteRemainingStepsAsync执行后续步骤
+ var result = await ExecuteRemainingStepsAsync(config, key, startTime);
+
+ // 删除临时配置文件 - 无论成功还是失败都需要执行
+ var deleteResult = _configCopier.DeleteCellularNetworkConfigurationFile(config);
+ // 记录结果但不影响返回值
+ ```
+
+4. **影响范围**:
+ - 简化了方法调用,移除了不必要的返回值判断
+ - 提高了代码可读性和维护性
+ - 确保了步骤1的成功验证逻辑
+ - 保证了临时配置文件的清理操作总是执行
+ - 保持了原有的错误处理逻辑
+
+### 代码提交 - 完善网络配置和协议客户端功能
+
+**修改时间**: 2024年12月
+**提交信息**: 更新代码:完善网络配置和协议客户端功能
+**提交哈希**: d011b25
+
+**修改内容**:
+
+1. **GeneralCellularNetworkService网络状态恢复机制优化**
+ - 重新命名和优化恢复方法:将 `StartRestore` 重命名为 `RestoreNetworkStateAsync`
+ - 完善恢复流程:停止协议客户端、关闭WebSocket连接、重置网络状态
+ - 在步骤10-12中添加恢复机制,确保失败时能正确清理资源
+ - 添加完整的异常处理和日志记录
+
+2. **ProtocolLogProcessor.ProcessLogDetails方法Info字段过滤修复**
+ - 修复foreach循环中的Info字段检查,允许处理所有日志记录
+ - 在调用观察者之前过滤掉Info为空或无效的记录
+ - 确保传递给观察者的数据质量,添加空集合检查
+
+3. **GeneralCellularNetworkService.StartNetworkAsync方法步骤注释优化**
+ - 为每个步骤添加详细和清晰的功能说明注释
+ - 统一注释格式,使用"步骤X: 功能描述"的格式
+ - 提高代码可读性和维护性
+
+4. **NetworkConfigCopier方法Bug修复和日志增强**
+ - 统一返回类型为 `NetworkConfigCopyResult`,不再抛出异常
+ - 修复路径构建Bug,正确使用 `Path.Combine`
+ - 添加完整的参数验证和目录创建逻辑
+ - 增强错误处理和详细日志记录
+
+5. **WebSocket传输服务依赖注入修复**
+ - 修复依赖注入顺序问题,确保中间件在核心服务之前注册
+ - 修复CacheMiddleware注册方式,使用标准的依赖注入模式
+ - 添加IMemoryCache服务注册,确保中间件依赖完整
+
+6. **ProtocolWsClientManager方法参数优化**
+ - 简化构造函数,移除配置参数
+ - 修改StartAllClients方法签名,添加配置参数
+ - 采用面向接口编程,创建IProtocolWsClientManager接口
+
+7. **CellularNetworkService集成协议客户端管理**
+ - 添加IProtocolWsClientManager依赖注入
+ - 在StartNetworkAsync方法中添加协议客户端启动步骤
+ - 在StopAsync方法中集成协议客户端停止逻辑
+ - 添加时间跟踪记录,提供详细的性能监控
+
+8. **MessageTransferProtocolLog模型创建**
+ - 创建新的协议日志模型,解决命名冲突
+ - 修改NetworkProtocolLogObserver转换逻辑
+ - 添加类型转换和字段映射
+
+9. **蜂窝网络配置实体类创建**
+ - 创建CellularNetworkConfiguration实体类
+ - 创建CoreNetworkImsConfiguration实体类
+ - 添加数据校验功能和ValidationResult类
+
+10. **NetworkProtocolLogObserver中的StopChannelManager方法修复**
+ - 修复资源管理问题,避免过早释放
+ - 优化通道管理方法,职责更加清晰
+ - 添加自动重连配置选项
+ - 修复WebSocket配置文件
+
+**影响范围**:
+
+
+## 2025-01-02
+
+### 日志规范修改 - GeneralCellularNetworkService和CellularNetworkContext
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+- `CoreAgent.Infrastructure/Contexts/CellularNetworkContext.cs`
+
+**修改内容**:
+
+1. **GeneralCellularNetworkService.cs 日志规范**:
+ - **第99行日志级别修正**: 将 `LogWarning` 改为 `LogInformation`,因为这是正常的初始化操作
+ - **日志格式统一**: 将字符串插值改为结构化日志格式
+ ```csharp
+ // 修改前
+ _logger.LogWarning($"初始化网络上下文 运行编码 {key}");
+
+ // 修改后
+ _logger.LogInformation("开始初始化网络上下文,运行编码: {RuntimeCode}", key);
+ ```
+ - **添加SetDeviceCode日志**: 在 `SetDeviceCode` 调用前添加日志记录
+ ```csharp
+ _logger.LogInformation("设置设备代码: {DeviceCode}", cellular.DeviceCode);
+ ```
+
+2. **CellularNetworkContext.cs 日志增强**:
+ - **Initialize方法日志**: 添加初始化和完成日志
+ ```csharp
+ _logger.LogInformation("开始初始化蜂窝网络上下文,配置键: {ConfigKey}", neConfigKey);
+ _logger.LogInformation("蜂窝网络上下文初始化完成,配置键: {ConfigKey}", neConfigKey);
+ ```
+ - **Initialize方法逻辑修复**: 移除被注释掉的 `_isInitialized` 检查,允许重新初始化以支持配置键更新
+ ```csharp
+ // 移除了以下被注释的代码,允许重新初始化
+ //if (_isInitialized)
+ //{
+ // return;
+ //}
+ ```
+ - **SetDeviceCode方法日志规范**: 将字符串插值改为结构化日志
+ ```csharp
+ // 修改前
+ _logger.LogInformation($"设备代码已设置为: {deviceCode}");
+
+ // 修改后
+ _logger.LogInformation("设备代码已设置为: {DeviceCode}", deviceCode);
+ ```
+ - **UpdateNetworkConfigType方法日志**: 添加配置类型更新日志
+ ```csharp
+ _logger.LogInformation("网络配置类型已更新: {PreviousConfigType} -> {NewConfigType}", previousConfigType, configType);
+ ```
+
+3. **修改原则**:
+ - **日志级别规范**: 正常操作使用 `LogInformation`,警告使用 `LogWarning`,错误使用 `LogError`
+ - **结构化日志**: 使用占位符格式 `{ParameterName}` 而不是字符串插值
+ - **日志完整性**: 为关键操作添加开始和完成的日志记录
+ - **日志一致性**: 保持日志格式和命名规范的一致性
+
+4. **影响范围**:
+ - 蜂窝网络启动流程的日志记录更加规范和详细
+ - 网络上下文初始化和状态变更的日志更加清晰
+ - 便于问题排查和系统监控
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StopAllClients方法移除_clients条目
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopAllClients` 方法停止所有协议客户端后,`_clients` 字典中仍然保留客户端条目
+ - 导致下次调用 `StartAllClients` 时会检查到已存在的客户端,不会创建新的 `ProtocolWsClient` 实例
+ - 需要确保每次 `StartAllClients` 都创建新的客户端实例
+
+2. **修复方案**:
+ - 在 `StopAllClients` 方法中添加 `_clients.Clear()` 调用
+ - 移除所有客户端条目,确保下次启动时创建新实例
+ - 修改返回值计算逻辑,使用 `stoppedCount` 而不是 `_clients.Count`
+ - 添加日志记录,说明已清空客户端列表
+
+3. **具体修改**:
+ ```csharp
+ // 修改前
+ var allDisconnected = disconnectedCount == _clients.Count;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+
+ // 修改后
+ // 移除所有客户端条目,确保下次StartAllClients会创建新实例
+ _clients.Clear();
+
+ var allDisconnected = disconnectedCount == stoppedCount;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}, 已清空客户端列表",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+ ```
+
+4. **设计优势**:
+ - **实例隔离**: 确保每次启动都创建新的协议客户端实例
+ - **资源清理**: 完全清理客户端引用,避免内存泄漏
+ - **状态重置**: 确保客户端管理器状态完全重置
+ - **日志完整**: 记录清空操作,便于调试和监控
+ - **逻辑正确**: 返回值计算基于实际停止的客户端数量
+
+5. **修复的关键问题**:
+ - **实例复用**: 避免复用已停止的客户端实例
+ - **状态污染**: 防止旧实例状态影响新启动的客户端
+ - **资源管理**: 确保客户端资源完全释放
+ - **启动逻辑**: 保证 `StartAllClients` 每次都创建新实例
+
+**影响范围**:
+- 协议客户端管理器的生命周期管理
+- 客户端实例的创建和销毁逻辑
+- 资源清理和内存管理
+- 网络启动流程的稳定性
+
+## 2025-01-02
+
+### WebSocketConnection.CloseAsync方法添加跟踪日志
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Services/WebSocketConnection.cs`
+
+**修改内容**:
+
+1. **为CloseAsync方法添加详细的跟踪日志信息**
+ - 在方法开始时记录关闭操作的详细信息,包括关闭状态、状态描述和当前连接状态
+ - 在WebSocket连接处于打开状态时,添加Debug级别的日志记录正常关闭流程
+ - 在关闭成功时记录成功信息,包括关闭状态和状态描述
+ - 在关闭失败时记录错误信息,包括异常详情和关闭参数
+ - 在WebSocket连接未处于打开状态时,记录警告信息并跳过关闭操作
+
+2. **具体实现**:
+ ```csharp
+ public async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("开始关闭 WebSocket 连接 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 当前连接状态: {CurrentState}",
+ closeStatus, statusDescription, _webSocket.State);
+
+ if (_webSocket.State == WebSocketState.Open)
+ {
+ _logger.LogDebug("WebSocket 连接处于打开状态,执行正常关闭流程");
+ try
+ {
+ await _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
+ _logger.LogInformation("WebSocket 连接关闭成功 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}",
+ closeStatus, statusDescription);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "WebSocket 连接关闭失败 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 错误信息: {ErrorMessage}",
+ closeStatus, statusDescription, ex.Message);
+ throw;
+ }
+ }
+ else
+ {
+ _logger.LogWarning("WebSocket 连接未处于打开状态,跳过关闭操作 - 当前状态: {CurrentState}", _webSocket.State);
+ }
+ }
+ ```
+
+3. **设计优势**:
+ - **详细跟踪**: 提供完整的关闭操作跟踪信息,便于调试和问题排查
+ - **状态监控**: 记录当前连接状态,帮助理解关闭操作的上下文
+ - **错误处理**: 完善的异常处理和错误日志记录
+ - **性能监控**: 可以跟踪关闭操作的执行情况
+ - **调试友好**: 提供足够的调试信息,便于问题定位
+ - **日志分级**: 使用不同级别的日志,避免生产环境日志过多
+
+4. **日志级别说明**:
+ - **Information**: 记录关闭操作的开始和成功完成
+ - **Debug**: 记录正常关闭流程的执行
+ - **Warning**: 记录跳过关闭操作的情况
+ - **Error**: 记录关闭失败和异常信息
+
+**影响范围**:
+- WebSocket连接关闭操作的日志记录
+- 调试和问题排查能力
+- 连接状态监控
+- 错误处理和异常跟踪
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StartAllClients方法启动和连接状态检查分离
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StartAllClients` 方法中启动客户端和检查连接状态混在一起
+ - 方法职责不够清晰,同时处理启动和连接状态检查
+ - 需要将启动和连接状态检查分离,提高代码的可读性和可维护性
+ - `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法需要适配新的接口
+
+2. **修复方案**:
+ - **分离启动和连接状态检查**:将 `StartAllClients` 方法中的连接状态检查逻辑分离出来
+ - **新增连接状态检查方法**:创建 `CheckAllClientsConnection()` 方法专门用于检查连接状态
+ - **简化启动方法**:`StartAllClients` 方法只负责启动客户端,不检查连接状态
+ - **更新接口定义**:在 `IProtocolWsClientManager` 接口中添加新的方法定义
+ - **优化返回值**:`StartAllClients` 返回是否所有客户端都成功启动,`CheckAllClientsConnection` 返回是否所有客户端都已连接
+ - **修复调用方代码**:更新 `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法以适配新的接口
+
+3. **具体修改**:
+ **StartAllClients方法优化**:
+ ```csharp
+ // 修改前 - 启动和连接状态检查混在一起
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected; // 返回连接状态
+ }
+
+ // 修改后 - 只负责启动客户端
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ var allStarted = startedCount == configs.Length;
+ return allStarted; // 返回启动状态
+ }
+ ```
+
+ **新增CheckAllClientsConnection方法**:
+ ```csharp
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ public bool CheckAllClientsConnection(int timeoutSeconds = 10)
+ {
+ ThrowIfDisposed();
+
+ if (timeoutSeconds <= 0)
+ {
+ _logger.LogWarning("超时时间必须大于0,使用默认值10秒");
+ timeoutSeconds = 10;
+ }
+
+ lock (_lock)
+ {
+ if (_clients.Count == 0)
+ {
+ _logger.LogWarning("没有运行中的协议客户端");
+ return false;
+ }
+
+ _logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
+ _clients.Count, timeoutSeconds);
+
+ var startTime = DateTime.UtcNow;
+ var timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ var connectedCount = 0;
+ var maxAttempts = 10; // 最大尝试次数
+ var attempt = 0;
+
+ while (attempt < maxAttempts)
+ {
+ attempt++;
+ connectedCount = 0;
+
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ if (client.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ _logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
+ attempt, kvp.Key, client.IsConnected);
+ }
+
+ var allConnected = connectedCount == _clients.Count;
+
+ if (allConnected)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
+ return true;
+ }
+
+ // 检查是否超时
+ if (DateTime.UtcNow - startTime > timeout)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
+ connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
+ return false;
+ }
+
+ // 等待一段时间后重试
+ if (attempt < maxAttempts)
+ {
+ var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
+ Thread.Sleep(waitTime);
+ }
+ }
+
+ var finalElapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
+
+ return false;
+ }
+ }
+ ```
+
+ **GeneralCellularNetworkService.StartAllProtocolClientsAsync方法修复**:
+ ```csharp
+ // 修改前 - 只检查启动状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+ _logger.LogInformation("所有协议客户端启动完成");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+
+ // 修改后 - 分别检查启动状态和连接状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ _logger.LogInformation("开始启动所有协议客户端");
+
+ // 获取协议客户端配置
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ if (protocolConfigs == null || protocolConfigs.Length == 0)
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
+ }
+
+ _logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
+
+ // 启动所有协议客户端
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+
+ _logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
+
+ // 检查连接状态(使用默认10秒超时)
+ var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
+ if (!connectionResult)
+ {
+ _logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
+ }
+
+ _logger.LogInformation("所有协议客户端启动并连接成功");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+ ```
+
+ **接口定义更新**:
+ ```csharp
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ ///
+ /// 启动所有协议客户端
+ ///
+ /// 协议客户端配置数组
+ /// 是否所有客户端都成功启动
+ bool StartAllClients(ProtocolClientConfig[] configs);
+
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ bool CheckAllClientsConnection(int timeoutSeconds = 10);
+
+ ///
+ /// 停止所有协议客户端
+ ///
+ /// 是否所有客户端都成功停止并断开连接
+ bool StopAllClients();
+ }
+ ```
+
+4. **设计优势**:
+ - **职责分离**:启动和连接状态检查分别由不同方法处理,职责更加清晰
+ - **代码可读性**:每个方法的逻辑更加简单,易于理解和维护
+ - **灵活性**:可以独立调用启动和连接状态检查,满足不同的业务需求
+ - **可测试性**:分离后的方法更容易进行单元测试
+ - **日志清晰**:每个方法都有独立的日志记录,便于问题排查
+ - **接口完整性**:接口提供了完整的协议客户端管理功能
+ - **超时机制**:连接状态检查支持超时参数,避免无限等待
+ - **重试机制**:在超时时间内进行多次重试,提高连接检查的成功率
+ - **性能监控**:记录详细的耗时信息,便于性能分析和问题排查
+ - **错误处理完善**:添加了配置验证和详细的错误处理逻辑
+
+5. **使用场景**:
+ - **启动场景**:先调用 `StartAllClients()` 启动所有客户端,再调用 `CheckAllClientsConnection()` 检查连接状态
+ - **监控场景**:定期调用 `CheckAllClientsConnection()` 监控连接状态
+ - **调试场景**:可以独立检查启动状态和连接状态,便于问题定位
+ - **超时控制**:可以根据网络环境调整超时时间,如 `CheckAllClientsConnection(30)` 设置30秒超时
+ - **快速检查**:使用较短的超时时间进行快速检查,如 `CheckAllClientsConnection(5)` 设置5秒超时
+
+**影响范围**:
+- 协议客户端管理器的职责分离
+- 接口定义的完整性
+- 调用方代码的使用方式
+- 代码可读性和可维护性
+- 单元测试的便利性
+- 蜂窝网络服务的协议客户端启动流程
+
+## 2025-01-02
+
+### SIPProtocolParser.GeneralParse方法严谨性修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/BuildProtocolParser/SIPProtocolParser.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - 变量名错误:`regisMccLine` 实际匹配的是MNC,`regisMncLine` 实际匹配的是MCC
+ - 空引用风险:使用了 `!` 操作符但没有进行空值检查
+ - 重复赋值:对 `log.SIP.Plmn` 进行了两次赋值,第二次会覆盖第一次
+ - 正则表达式不够严谨:缺少编译选项
+ - 返回值检查错误:`StringToId` 返回0表示空字符串,不是-1
+
+2. **修复方案**:
+ - **修复变量名错误**:将变量名与实际匹配内容对应
+ - **增强空值检查**:添加参数验证和空值检查
+ - **修复重复赋值**:分别设置PLMN和IMSI属性
+ - **优化正则表达式**:添加 `RegexOptions.Compiled` 提高性能
+ - **修复返回值检查**:使用正确的返回值判断逻辑
+ - **添加参数验证**:验证Groups数量和参数有效性
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 变量名错误
+ var regisMccLine = log!.Data.Select(line => _regMnc.Match(line))... // 实际匹配MNC
+ var regisMncLine = log!.Data.Select(line => _regMcc.Match(line))... // 实际匹配MCC
+
+ // 修复后 - 变量名正确
+ var mncValue = log.Data?.Select(line => _regMnc.Match(line))... // 匹配MNC
+ var mccValue = log.Data?.Select(line => _regMcc.Match(line))... // 匹配MCC
+
+ // 修复前 - 重复赋值
+ log.SIP.Plmn = $"{regisMncLine}{regisMccLine!.Substring(1)}";
+ log.SIP.Plmn = regisIMSILine; // 覆盖了上面的赋值
+
+ // 修复后 - 分别设置不同属性
+ if (!string.IsNullOrEmpty(mccValue) && !string.IsNullOrEmpty(mncValue))
+ {
+ log.SIP.Plmn = $"{mccValue}{mncValue}";
+ }
+ if (!string.IsNullOrEmpty(imsiValue))
+ {
+ log.SIP.IMSI = imsiValue; // 使用正确的IMSI属性
+ }
+
+ // 修复前 - 返回值检查错误
+ if (info == -1) return;
+
+ // 修复后 - 正确的返回值检查
+ if (info == 0) return; // StringToId返回0表示空字符串
+ ```
+
+4. **设计优势**:
+ - **逻辑正确性**:修复了变量名错误和重复赋值问题
+ - **空值安全**:添加了完整的空值检查和参数验证
+ - **性能优化**:正则表达式使用编译选项提高性能
+ - **代码严谨性**:添加了Groups数量验证和参数有效性检查
+ - **错误预防**:使用空条件操作符避免空引用异常
+ - **属性正确性**:使用正确的SIP属性设置PLMN和IMSI
+
+5. **修复的关键问题**:
+ - **变量命名混乱**:MCC和MNC变量名与实际匹配内容不匹配
+ - **空引用风险**:使用 `!` 操作符但没有验证对象不为空
+ - **数据覆盖**:对同一属性进行两次赋值导致数据丢失
+ - **返回值误解**:对 `StringToId` 方法返回值理解错误
+ - **正则表达式性能**:缺少编译选项影响性能
+ - **参数验证缺失**:没有验证正则匹配结果的Groups数量
+ - **日志跟踪缺失**:缺少详细的错误日志记录,难以调试问题
+
+6. **日志跟踪增强**:
+ - **添加ILogger支持**:在构造函数中创建类型安全的Logger实例
+ - **参数验证日志**:记录BuildProtocolLog参数为空的情况
+ - **StringToId失败日志**:记录StringToId返回0(空字符串)的情况
+ - **Groups数量验证日志**:记录正则匹配Groups数量不足的详细信息
+ - **性能优化**:使用私有Logger字段避免重复创建Logger实例
+
+**影响范围**:
+- SIP协议解析的准确性
+- 协议日志数据的完整性
+- 代码的稳定性和可靠性
+- 性能优化和错误预防
+- 代码可读性和维护性
+- 调试和问题排查能力显著提升
+
+### TimeStampHelper时区初始化异常修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `TimeStampHelper`类在静态初始化时抛出`System.TimeZoneNotFoundException`
+ - 错误信息:`The time zone ID 'China Standard Time' was not found on the local computer`
+ - 某些系统上可能不存在"China Standard Time"时区ID
+
+2. **修复方案**:
+ - **多时区ID支持**:尝试多种可能的时区ID,包括Windows和Linux/macOS格式
+ - **回退机制**:如果系统时区ID都不可用,创建自定义UTC+8时区
+ - **最终回退**:如果自定义时区创建失败,使用UTC时区作为最后回退
+ - **调试支持**:添加时区信息查询和验证方法
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 直接使用单一时区ID
+ private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
+
+ // 修复后 - 支持多种时区ID和回退机制
+ private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
+
+ private static TimeZoneInfo GetChinaStandardTime()
+ {
+ // 尝试多种可能的时区ID
+ string[] timeZoneIds = {
+ "China Standard Time", // Windows
+ "Asia/Shanghai", // Linux/macOS
+ "Asia/Chongqing", // 备选
+ "Asia/Harbin", // 备选
+ "Asia/Urumqi" // 备选
+ };
+
+ foreach (string timeZoneId in timeZoneIds)
+ {
+ try
+ {
+ return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
+ }
+ catch (TimeZoneNotFoundException)
+ {
+ continue;
+ }
+ }
+
+ // 创建自定义UTC+8时区
+ try
+ {
+ return TimeZoneInfo.CreateCustomTimeZone(
+ "China Standard Time",
+ TimeSpan.FromHours(8),
+ "China Standard Time",
+ "China Standard Time");
+ }
+ catch
+ {
+ return TimeZoneInfo.Utc; // 最终回退
+ }
+ }
+ ```
+
+4. **新增调试方法**:
+ ```csharp
+ // 获取当前使用的时区信息
+ public static string GetTimeZoneInfo()
+
+ // 检查时区是否正确初始化
+ public static bool IsTimeZoneCorrectlyInitialized()
+ ```
+
+5. **设计优势**:
+ - **跨平台兼容**:支持Windows、Linux、macOS等不同操作系统
+ - **健壮性**:多层回退机制确保在任何环境下都能正常工作
+ - **调试友好**:提供时区信息查询方法便于问题排查
+ - **向后兼容**:保持原有API不变,不影响现有代码
+ - **性能优化**:静态初始化,避免重复计算
+
+6. **修复的关键问题**:
+ - **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
+ - **跨平台兼容性**:不同操作系统使用不同的时区ID格式
+ - **初始化失败**:静态构造函数异常导致整个类无法使用
+ - **调试困难**:缺少时区状态查询方法
+
+**影响范围**:
+- 时间戳工具类的跨平台兼容性
+- 系统启动时的稳定性
+- 时间转换功能的可靠性
+- 调试和问题排查能力
+
+**后续优化**:
+- **简化时区选择逻辑**:优先使用`Asia/Shanghai`时区(Linux标准格式)
+- **提高性能**:减少不必要的时区ID尝试,直接使用最常用的时区
+- **代码简洁性**:简化回退逻辑,提高代码可读性
+- **移除Windows依赖**:完全移除`China Standard Time`时区ID,专注于Linux系统优化
+
+## 2024年修改记录
+
+### 修复NetworkProtocolLogObserver中的StopChannelManager方法并支持重新创建
+
+**修改时间**: 2024年12月
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs`
+- `CoreAgent.WebSocketTransport/Interfaces/IMessageChannelManager.cs`
+- `CoreAgent.WebSocketTransport/Services/MessageChannelManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopChannelManager()` 方法直接调用 `_ChannelManager.Dispose()` 可能导致资源过早释放
+ - `IMessageChannelManager` 接口已实现 `IDisposable`,生命周期应由DI容器管理
+ - 直接调用 `Dispose()` 可能影响其他使用该实例的组件
+
+2. **修复方案**:
+ - 修改 `MessageChannelManager` 设计,不在构造函数中创建通道
+ - 移除构造函数中的默认参数,要求必须传入容量参数
+ - 添加容量参数验证,确保参数有效性
+ - 扩展 `WebSocketConfig` 配置类,添加分别的通道容量配置
+ - 更新依赖注入注册,从配置中读取不同的通道容量
+ - 优化通道管理方法,职责更加清晰:
+ - `CreateChannels()` - 安全创建(已存在则跳过)
+ - `ClearAllChannels()` - 清空通道消息(保持通道可用)
+ - `CompleteAllChannels()` - 完成通道(标记不再接受新消息,但保持可读)
+ - `ReleaseChannels()` - 完全释放通道资源
+
+ - 将 `StopChannelManager()` 改为调用 `ReleaseChannels()`
+ - 将 `RecreateChannelManager()` 改为调用 `CreateChannels()`
+ - **优化通道创建逻辑**:
+ - 修改 `CreateChannels()` 方法,如果通道已存在则先释放再创建
+ - 移除 `ResetChannels()` 方法,避免功能重复
+ - 简化接口设计,减少方法数量
+ - **WebSocketTransport 集成通道管理**:
+ - 重构 `ConnectInternalAsync()` 方法,提取为多个独立方法:
+ - `CreateMessageChannels()` - 先创建消息通道(同步方法)
+ - `EstablishWebSocketConnectionAsync()` - 再建立WebSocket连接(异步方法)
+ - `StartBackgroundTasks()` - 最后启动后台任务(同步方法)
+ - 在 `CloseAsync()` 中调用 `_channelManager.ReleaseChannels()` 释放通道
+ - 在重连失败时也释放通道,确保资源正确清理
+ - 通道生命周期与WebSocket连接生命周期完全同步
+ - **添加自动重连配置选项**:
+ - 在 `WebSocketConfig` 中添加 `EnableAutoReconnect` 配置项
+ - 在 `TriggerReconnect()` 方法中添加配置检查
+ - 支持通过配置文件控制是否启用自动重连功能
+ - **修复WebSocket配置文件**:
+ - 更新 `websocket.json` 和 `websocket.Development.json` 配置文件
+ - 添加所有新增的配置项:`EnableAutoReconnect`、`SendChannelCapacity`、`ReceiveChannelCapacity`、`PriorityChannelCapacity`、`MaxChunkSize`、`ChunkDelayMs`
+ - 为开发环境和生产环境提供不同的配置值
+ - 添加重复调用检查,避免二次调用导致异常
+ - 添加异常处理和日志记录
+ - 保持方法功能的同时避免资源管理问题
+
+3. **具体修改**:
+ **IMessageChannelManager 接口新增方法**:
+ ```csharp
+ ///
+ /// 创建所有通道
+ ///
+ void CreateChannels();
+
+ ///
+ /// 释放所有通道
+ ///
+ void ReleaseChannels();
+ ```
+
+ **MessageChannelManager 实现**:
+ ```csharp
+ // 构造函数要求必须传入容量参数,并验证参数有效性
+ public MessageChannelManager(ILogger logger, int sendChannelCapacity, int receiveChannelCapacity, int priorityChannelCapacity)
+ {
+ // 验证容量参数
+ if (sendChannelCapacity <= 0) throw new ArgumentOutOfRangeException(...);
+ // 保存容量配置,不创建通道
+ }
+
+ // 安全创建通道(如果已存在则跳过)
+ public void CreateChannels()
+ {
+ // 检查通道是否已存在,如果存在则跳过创建
+ }
+
+ // 清空通道消息(保持通道可用)
+ public void ClearAllChannels()
+ {
+ // 清空所有通道中的消息,但保持通道结构
+ }
+
+ // 完成通道(标记不再接受新消息,但保持可读)
+ public void CompleteAllChannels()
+ {
+ // 标记通道完成,不再接受新消息,但可以继续读取
+ }
+
+ // 完全释放通道资源
+ public void ReleaseChannels()
+ {
+ // 完成通道,释放资源,清空引用
+ }
+
+
+ ```
+
+ **WebSocketConfig 配置扩展**:
+ ```csharp
+ public class WebSocketConfig
+ {
+ // 是否启用自动重连功能
+ public bool EnableAutoReconnect { get; set; } = true;
+
+ // 最大重连尝试次数
+ public int MaxReconnectAttempts { get; set; } = 5;
+
+ // 发送通道容量
+ public int SendChannelCapacity { get; set; } = 1000;
+
+ // 接收通道容量
+ public int ReceiveChannelCapacity { get; set; } = 1000;
+
+ // 优先级通道容量
+ public int PriorityChannelCapacity { get; set; } = 100;
+ }
+ ```
+
+ **依赖注入注册更新**:
+ ```csharp
+ services.AddSingleton(provider =>
+ {
+ var config = provider.GetRequiredService>().Value;
+ return new MessageChannelManager(logger, config.SendChannelCapacity, config.ReceiveChannelCapacity, config.PriorityChannelCapacity);
+ });
+ ```
+
+ **NetworkProtocolLogObserver 修改**:
+ ```csharp
+ public void StopChannelManager()
+ {
+ // 调用 ReleaseChannels() 释放通道
+ _ChannelManager.ReleaseChannels();
+ }
+
+ public void RecreateChannelManager()
+ {
+ // 调用 CreateChannels() 重新创建通道(会自动释放现有通道)
+ _ChannelManager.CreateChannels();
+ }
+ ```
+
+ **WebSocketTransport 重构**:
+ ```csharp
+ // 连接时先创建通道,再建立连接
+ private async Task ConnectInternalAsync(CancellationToken cancellationToken)
+ {
+ // 1. 先创建消息通道
+ await CreateMessageChannelsAsync();
+
+ // 2. 再建立 WebSocket 连接
+ await EstablishWebSocketConnectionAsync(cancellationToken);
+
+ // 3. 最后启动后台任务
+ await StartBackgroundTasksAsync();
+ }
+
+ // 提取的独立方法
+ private void CreateMessageChannels()
+ {
+ _channelManager.CreateChannels();
+ }
+
+ private async Task EstablishWebSocketConnectionAsync(CancellationToken cancellationToken)
+ {
+ await _connection.ConnectAsync(...);
+ }
+
+ private void StartBackgroundTasks()
+ {
+ // 启动发送、接收、心跳任务
+ }
+ ```
+
+4. **修复优势**:
+ - **配置灵活性**: 支持通过配置文件分别设置不同通道的容量,更加灵活
+ - **参数验证**: 构造函数中验证容量参数,确保参数有效性
+ - **方法职责清晰**: 每个方法职责明确,避免功能重叠
+ - **生命周期控制**: 通道的创建和释放完全由用户控制,更加灵活
+ - **资源管理**: 避免在构造函数中创建资源,符合延迟初始化原则
+ - **重复使用**: 支持多次创建和释放,满足业务需求
+ - **重复调用保护**: 防止二次调用导致异常,提高系统稳定性
+ - **异常处理**: 添加了完整的异常处理和日志记录,但不影响主程序运行
+ - **业务连续性**: 异常被捕获并记录,但不会中断主程序流程
+ - **功能保持**: 仍然能够正确停止和重新创建通道管理器
+ - **日志完善**: 提供了详细的调试和错误日志信息
+
+### 创建蜂窝网络配置实体类
+
+### 创建蜂窝网络配置实体类
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs` (新建)
+
+**修改内容**:
+
+1. **创建CellularNetworkConfiguration实体类**
+ - 包含设备代码(DeviceCode)字符串属性
+ - 包含运行时代码(RuntimeCode)字符串属性
+ - 包含无线接入网配置(RadioAccessNetworkConfiguration)字符串属性
+ - 包含核心网IMS配置集合(List)属性
+
+2. **创建CoreNetworkImsConfiguration实体类**
+ - 包含索引(Index)整数属性
+ - 包含PLMN标识(Plmn)字符串属性
+ - 包含核心网配置(CoreNetworkConfiguration)字符串属性
+ - 包含IMS服务配置(ImsServiceConfiguration)字符串属性
+
+3. **具体实现**:
+ ```csharp
+ ///
+ /// 蜂窝网络配置实体
+ ///
+ public class CellularNetworkConfiguration
+ {
+ ///
+ /// 设备代码
+ ///
+ public string DeviceCode { get; set; }
+
+ ///
+ /// 运行时代码
+ ///
+ public string RuntimeCode { get; set; }
+
+ ///
+ /// 无线接入网配置
+ ///
+ public string RadioAccessNetworkConfiguration { get; set; }
+
+ ///
+ /// 核心网IMS配置集合
+ ///
+ public List CoreNetworkImsConfigurations { get; set; } = new List();
+ }
+
+ ///
+ /// 核心网IMS配置对象
+ ///
+ public class CoreNetworkImsConfiguration
+ {
+ ///
+ /// 索引
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// PLMN标识
+ ///
+ public string Plmn { get; set; }
+
+ ///
+ /// 核心网配置
+ ///
+ public string CoreNetworkConfiguration { get; set; }
+
+ ///
+ /// IMS服务配置
+ ///
+ public string ImsServiceConfiguration { get; set; }
+ }
+ ```
+
+4. **设计优势**:
+ - **命名规范**:遵循C#命名约定,使用PascalCase
+ - **命名清晰**:类名明确表达业务含义,提高代码可读性
+ - **属性专业**:使用完整的专业术语,避免缩写和模糊命名
+ - **类型安全**:使用强类型属性,避免类型错误
+ - **文档完整**:每个属性都有详细的XML文档注释
+ - **集合初始化**:使用集合初始化器确保集合不为null
+ - **职责清晰**:每个类都有明确的职责和用途
+ - **易于扩展**:结构清晰,便于后续添加新属性
+ - **业务导向**:类名直接反映业务领域概念
+ - **专业术语**:使用标准的电信网络术语,提高代码专业性
+
+### 添加蜂窝网络配置数据校验功能
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs`
+
+**修改内容**:
+
+1. **添加数据校验功能**
+ - 为 `DeviceCode` 和 `RuntimeCode` 添加 `[Required]` 特性,确保不能为空
+ - 为 `Plmn` 添加 `[Required]` 特性,确保不能为空
+ - 添加 `ValidateConfiguration()` 方法进行业务逻辑校验
+
+2. **业务规则校验**
+ - 验证 `DeviceCode` 和 `RuntimeCode` 不能为空
+ - 验证 `RadioAccessNetworkConfiguration` 和 `CoreNetworkImsConfigurations` 至少有一个有数据
+ - 验证 `CoreNetworkImsConfiguration` 中的 `CoreNetworkConfiguration` 和 `ImsServiceConfiguration` 至少有一个有数据
+
+3. **创建ValidationResult类**
+ - 提供统一的验证结果返回格式
+ - 支持成功和失败两种状态
+ - 提供详细的错误消息
+ - 支持隐式转换操作符
+
+4. **具体实现**:
+ ```csharp
+ // 必填字段校验
+ [Required(ErrorMessage = "设备代码不能为空")]
+ public string DeviceCode { get; set; }
+
+ [Required(ErrorMessage = "运行时代码不能为空")]
+ public string RuntimeCode { get; set; }
+
+ // 业务逻辑校验
+ public ValidationResult ValidateConfiguration()
+ {
+ // 验证必填字段
+ if (string.IsNullOrWhiteSpace(DeviceCode))
+ {
+ return new ValidationResult("设备代码不能为空");
+ }
+
+ // 验证无线接入网配置和核心网IMS配置至少有一个有数据
+ var hasRadioAccessConfig = !string.IsNullOrWhiteSpace(RadioAccessNetworkConfiguration);
+ var hasCoreNetworkConfigs = CoreNetworkImsConfigurations?.Any() == true;
+
+ if (!hasRadioAccessConfig && !hasCoreNetworkConfigs)
+ {
+ return new ValidationResult("无线接入网配置和核心网IMS配置至少需要配置其中一项");
+ }
+
+ return ValidationResult.Success;
+ }
+ ```
+
+5. **设计优势**:
+ - **数据完整性**:确保必填字段不为空
+ - **业务规则校验**:验证业务逻辑的正确性
+ - **统一验证接口**:提供一致的验证方法
+ - **详细错误信息**:提供具体的错误描述
+ - **分层校验**:支持嵌套对象的校验
+ - **易于扩展**:可以轻松添加新的校验规则
+
+**影响范围**:
+- 蜂窝网络配置数据模型定义
+- 核心网IMS配置管理
+- 数据实体结构标准化
+- 领域模型完整性
+- 代码可读性和维护性提升
+- 数据校验和业务规则验证
+- 错误处理和用户反馈
+
+### 创建MessageTransferProtocolLog模型解决命名冲突
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Models/MessageTransferProtocolLog.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs`
+
+**修改内容**:
+
+1. **创建MessageTransferProtocolLog模型**
+ - 在 `CoreAgent.WebSocketTransport` 项目中创建新的协议日志模型
+ - 与 `CoreAgent.ProtocolClient` 中的 `TransferProtocolLog` 区分开
+ - 保持相同的字段结构,避免命名冲突
+ - 专门用于WebSocket传输层的协议日志数据传输
+
+2. **修改NetworkProtocolLogObserver转换逻辑**
+ - 在 `OnProtocolLogsReceived` 方法中添加类型转换
+ - 将 `CoreAgent.ProtocolClient.TransferProtocolLog` 转换为 `CoreAgent.WebSocketTransport.MessageTransferProtocolLog`
+ - 保持所有字段的完整映射
+ - 添加必要的using语句引用
+
+3. **具体实现**:
+ ```csharp
+ // 时间跟踪和性能监控
+ var startTime = DateTime.UtcNow;
+
+ // 空值检查
+ if (logDetails == null)
+ {
+ _logger.LogWarning("接收到的协议日志为空");
+ return;
+ }
+
+ // 转换为列表以避免多次枚举
+ var logList = logDetails.ToList();
+ var logCount = logList.Count;
+
+ // 空集合检查
+ if (logCount == 0)
+ {
+ _logger.LogDebug("接收到的协议日志集合为空,跳过处理");
+ return;
+ }
+
+ // 类型转换逻辑
+ var webSocketLogs = logList.Select(log => new MessageTransferProtocolLog
+ {
+ Id = log.Id,
+ LayerType = log.LayerType.ToString(),
+ MessageDetailJson = log.MessageDetailJson,
+ CellID = log.CellID,
+ IMSI = log.IMSI,
+ Direction = log.Direction,
+ UEID = log.UEID,
+ PLMN = log.PLMN,
+ TimeMs = log.TimeMs,
+ Timestamp = log.Timestamp,
+ Info = log.Info,
+ Message = log.Message
+ });
+
+ // 通道写入状态监控
+ var writeSuccess = _ChannelManager.SendChannel.TryWrite(webSocketLogs);
+ var processingTime = DateTime.UtcNow - startTime;
+
+ if (writeSuccess)
+ {
+ _logger.LogDebug("协议日志处理成功,数量: {LogCount}, 处理时间: {ProcessingTime}ms",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ else
+ {
+ _logger.LogWarning("协议日志写入通道失败,数量: {LogCount}, 处理时间: {ProcessingTime}ms, 通道可能已满或已关闭",
+ logCount, processingTime.TotalMilliseconds);
+ }
+ ```
+
+4. **设计优势**:
+ - **命名清晰**:`MessageTransferProtocolLog` 明确表示用于消息传输
+ - **避免冲突**:与原始 `TransferProtocolLog` 有明确区分
+ - **职责分离**:WebSocket传输层有独立的协议日志模型
+ - **类型安全**:通过显式转换确保类型安全
+ - **易于维护**:清晰的命名约定便于理解和维护
+ - **性能监控**:添加时间跟踪和通道写入状态监控
+ - **错误处理**:完善的异常处理和日志记录
+ - **Bug修复**:修复空引用检查和多次枚举的性能问题
+ - **边界处理**:添加空集合检查,避免处理空集合
+ - **代码规范**:优化ProtocolMessage模型注释,提高代码可读性
+
+**影响范围**:
+- WebSocket传输层协议日志处理
+- 协议日志观察者模式实现
+- 跨项目类型转换逻辑
+- 协议消息模型注释优化
+
+### CellularNetworkService.StartNetworkAsync 方法添加协议客户端配置创建
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **调整执行步骤顺序**
+ - 将原来的第5.5步改为第6步(创建协议客户端配置)
+ - 将WebSocket传输连接移到第7步(在网络配置启动之前)
+ - 重新编号后续步骤(8-11步)
+ - **优化执行顺序**:确保WebSocket连接在网络配置启动之前完成
+
+2. **添加第7步WebSocket传输连接**
+ - 在 `StartNetworkAsync` 方法的第6步(创建协议客户端配置)之后添加第7步
+ - 注入 `IWebSocketTransport` 依赖
+ - 创建独立的 `StartWebSocketTransportAsync()` 方法处理连接逻辑
+ - 返回 `bool` 值表示连接是否成功
+ - 添加连接状态检查和错误处理
+ - **严格检查**:WebSocket连接失败时立即返回失败结果,提示服务端可能未启动
+
+3. **修复Logger类型问题**
+ - 添加 `ILoggerFactory` 依赖注入到构造函数
+ - 使用 `_loggerFactory.CreateLogger()` 创建正确类型的Logger
+ - 确保 `ProtocolClientConfigFactory` 获得正确的Logger实例
+
+3. **具体实现**:
+ ```csharp
+ // 6. 创建协议客户端配置
+ var protocolConfigFactory = new ProtocolClientConfigFactory(_loggerFactory.CreateLogger(), _context);
+ var configCreated = protocolConfigFactory.CreateFromEntities();
+ if (configCreated)
+ {
+ _logger.LogInformation("协议客户端配置创建成功,共创建 {ConfigCount} 个配置", protocolConfigFactory.ConfigCount);
+ }
+ else
+ {
+ _logger.LogWarning("协议客户端配置创建失败");
+ return CellularNetworkOperationResult.Failure("协议客户端配置创建失败");
+ }
+
+ // 7. 启动 WebSocket 传输连接
+ var webSocketConnected = await StartWebSocketTransportAsync();
+ if (!webSocketConnected)
+ {
+ _logger.LogError("WebSocket 传输连接启动失败,服务端可能未启动");
+ return CellularNetworkOperationResult.Failure("WebSocket 传输连接启动失败,服务端可能未启动");
+ }
+ _logger.LogInformation("WebSocket 传输连接启动成功");
+ ```
+
+4. **添加必要的依赖注入**
+ - 添加 `ILoggerFactory loggerFactory` 参数到构造函数
+ - 添加 `IWebSocketTransport webSocketTransport` 参数到构造函数
+ - 添加 `using CoreAgent.Infrastructure.Services.Network;` 以支持 `ProtocolClientConfigFactory`
+ - 添加 `using CoreAgent.WebSocketTransport.Interfaces;` 以支持 `IWebSocketTransport`
+
+5. **设计优势**:
+ - 在IP端点信息准备完成后立即创建协议客户端配置
+ - 不依赖网络启动结果,确保配置创建的独立性
+ - 在网络配置启动之前启动WebSocket传输连接
+ - 提供详细的日志记录便于调试
+ - 保持代码的简洁性和可维护性
+ - 正确处理Logger类型,避免类型不匹配问题
+ - 优化执行顺序,提高错误隔离能力
+ - 完善的错误处理机制,确保配置创建失败时及时停止
+ - 严格检查机制,WebSocket连接失败时立即停止网络启动流程
+ - 方法职责单一,WebSocket连接逻辑独立封装
+
+**影响范围**:
+- 蜂窝网络启动流程
+- 协议客户端配置管理
+- WebSocket传输服务集成
+- 网络状态监控
+- 依赖注入配置(需要更新服务注册)
+
+### CellularNetworkService构造函数添加IProtocolLogObserver依赖
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加IProtocolLogObserver依赖注入**
+ - 在构造函数中添加 `IProtocolLogObserver protocolLogObserver` 参数
+ - 添加私有字段 `_protocolLogObserver` 存储依赖
+ - 添加空值检查和异常抛出
+
+2. **添加必要的using语句**
+ - 添加 `using CoreAgent.ProtocolClient.ProtocolEngineCore;` 以支持 `IProtocolLogObserver`
+
+3. **具体实现**:
+ ```csharp
+ // 构造函数参数
+ public CellularNetworkService(
+ // ... 其他参数
+ IWebSocketTransport webSocketTransport,
+ IProtocolLogObserver protocolLogObserver)
+
+ // 私有字段
+ private readonly IProtocolLogObserver _protocolLogObserver;
+
+ // 构造函数初始化
+ _protocolLogObserver = protocolLogObserver ?? throw new ArgumentNullException(nameof(protocolLogObserver));
+ ```
+
+4. **设计优势**:
+ - 为后续协议客户端管理提供必要的依赖
+ - 保持依赖注入的一致性
+ - 提供空值检查确保服务稳定性
+ - 为协议日志观察者模式提供支持
+
+**影响范围**:
+- 蜂窝网络服务依赖注入配置
+- 协议客户端日志观察者集成
+- 服务注册配置更新
+
+### StartNetworkAsync方法添加时间跟踪记录
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加整体时间跟踪**
+ - 在方法开始时记录开始时间
+ - 在方法结束时计算总耗时并记录
+ - 使用UTC时间确保时间一致性
+
+2. **为每个步骤添加详细时间跟踪**
+ - 为11个步骤中的每个步骤添加开始和结束时间记录
+ - 使用 `LogDebug` 级别记录每个步骤的耗时
+ - 保持原有的 `LogInformation` 和 `LogError` 级别日志不变
+
+3. **具体实现**:
+ ```csharp
+ // 方法开始时间跟踪
+ var startTime = DateTime.UtcNow;
+ _logger.LogInformation("开始启动网络配置 {ConfigKey},开始时间: {StartTime}", key, startTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
+
+ // 每个步骤的时间跟踪
+ var stepXStart = DateTime.UtcNow;
+ _logger.LogDebug("步骤X开始:[步骤描述]");
+
+ // 步骤执行逻辑...
+
+ var stepXDuration = (DateTime.UtcNow - stepXStart).TotalMilliseconds;
+ _logger.LogDebug("步骤X完成:[步骤描述],耗时: {Duration}ms", stepXDuration.ToString("F2"));
+
+ // 方法结束时间跟踪
+ var endTime = DateTime.UtcNow;
+ var duration = endTime - startTime;
+ _logger.LogInformation("蜂窝网络配置 {ConfigKey} 启动成功,当前状态: {Status},总耗时: {Duration}ms",
+ key, state.CurrentStatus, duration.TotalMilliseconds.ToString("F2"));
+ ```
+
+4. **设计优势**:
+ - **性能监控**:可以识别网络启动过程中的性能瓶颈
+ - **调试支持**:详细的时间信息有助于问题定位和性能优化
+ - **日志分级**:使用Debug级别避免生产环境日志过多
+ - **时间精度**:使用毫秒级精度提供准确的性能数据
+ - **UTC时间**:确保时间记录的一致性和准确性
+ - **非侵入性**:不影响原有的业务逻辑和错误处理
+
+5. **跟踪的步骤**:
+ - 步骤1:获取并验证网络配置
+ - 步骤2:执行网络接口初始化命令
+ - 步骤3:复制配置值到临时目录
+ - 步骤4:获取并验证 IP 端点信息
+ - 步骤5:更新 IP 端点管理器
+ - 步骤6:创建协议客户端配置
+ - 步骤7:启动 WebSocket 传输连接
+ - 步骤8:启动网络配置
+ - 步骤9:更新网络配置类型
+ - 步骤10:检查网络端点连接状态
+ - 步骤11:更新网络状态
+
+**影响范围**:
+# 修改记录
+
+## 2025-08-21
+
+### CoreAgent.Infrastructure/Services/Network/NetworkProtocolLogObserver.cs
+- **问题**: 第一次停止协议客户端成功后,第二次启动时 OnProtocolLogsReceived 方法似乎没有被调用
+- **修改**: 在 OnProtocolLogsReceived 和 ProcessLogsInternal 方法中添加了详细的跟踪日志
+ - 添加了方法开始执行的日志记录
+ - 添加了线程ID跟踪,便于调试多线程问题
+ - 添加了参数验证和数据处理各阶段的日志记录
+ - 添加了锁获取和释放的日志记录
+ - 添加了配置获取、数据转换、通道写入等关键步骤的日志记录
+ - 将部分 Debug 级别日志提升为 Information 级别,便于问题排查
+
+**修改目的**: 通过详细的日志跟踪,帮助诊断为什么在第二次启动协议客户端时 OnProtocolLogsReceived 方法没有被正确调用的问题。
+
+### CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs
+- **问题**: 第二次启动协议客户端时,虽然日志显示"成功通知协议日志观察者",但 OnProtocolLogsReceived 方法实际没有被处理
+- **修改**: 在通知观察者的代码段中添加了更详细的诊断日志
+ - 添加了观察者实例null检查
+ - 添加了观察者类型信息记录
+ - 添加了方法调用前后的详细日志
+ - 改进了异常处理中的观察者信息记录
+
+**修改目的**: 通过详细的观察者状态检查和方法调用跟踪,帮助诊断观察者实例状态问题,确定是观察者引用问题还是方法调用问题。
+
+### CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs
+- **问题**: 需要检查观察者传递和管理是否存在问题
+- **修改**: 在关键方法中添加了观察者状态检查和详细跟踪日志
+ - 在构造函数中添加了观察者类型信息记录
+ - 在 CreateProtocolWsClient 方法中添加了观察者null检查和类型信息
+ - 在 StartAllClients 方法中添加了观察者状态检查和详细启动日志
+ - 在 StopAllClients 方法中添加了更详细的停止过程日志
+ - 添加了客户端实例创建和复用的区分日志
+
+**修改目的**: 通过详细的观察者状态跟踪和客户端生命周期日志,帮助诊断观察者传递链路中的问题,确保观察者实例在整个生命周期中的正确性。
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StopAllClients方法移除_clients条目
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StopAllClients` 方法停止所有协议客户端后,`_clients` 字典中仍然保留客户端条目
+ - 导致下次调用 `StartAllClients` 时会检查到已存在的客户端,不会创建新的 `ProtocolWsClient` 实例
+ - 需要确保每次 `StartAllClients` 都创建新的客户端实例
+
+2. **修复方案**:
+ - 在 `StopAllClients` 方法中添加 `_clients.Clear()` 调用
+ - 移除所有客户端条目,确保下次启动时创建新实例
+ - 修改返回值计算逻辑,使用 `stoppedCount` 而不是 `_clients.Count`
+ - 添加日志记录,说明已清空客户端列表
+
+3. **具体修改**:
+ ```csharp
+ // 修复前
+ var allDisconnected = disconnectedCount == _clients.Count;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+
+ // 修复后
+ // 移除所有客户端条目,确保下次StartAllClients会创建新实例
+ _clients.Clear();
+
+ var allDisconnected = disconnectedCount == stoppedCount;
+ _logger.LogInformation("协议客户端停止完成 - 成功: {StoppedCount}, 失败: {FailedCount}, 已断开: {DisconnectedCount}, 全部断开: {AllDisconnected}, 已清空客户端列表",
+ stoppedCount, failedCount, disconnectedCount, allDisconnected);
+ ```
+
+4. **设计优势**:
+ - **实例隔离**: 确保每次启动都创建新的协议客户端实例
+ - **资源清理**: 完全清理客户端引用,避免内存泄漏
+ - **状态重置**: 确保客户端管理器状态完全重置
+ - **日志完整**: 记录清空操作,便于调试和监控
+ - **逻辑正确**: 返回值计算基于实际停止的客户端数量
+
+5. **修复的关键问题**:
+ - **实例复用**: 避免复用已停止的客户端实例
+ - **状态污染**: 防止旧实例状态影响新启动的客户端
+ - **资源管理**: 确保客户端资源完全释放
+ - **启动逻辑**: 保证 `StartAllClients` 每次都创建新实例
+
+**影响范围**:
+- 协议客户端管理器的生命周期管理
+- 客户端实例的创建和销毁逻辑
+- 资源清理和内存管理
+- 网络启动流程的稳定性
+
+## 2025-01-02
+
+### WebSocketConnection.CloseAsync方法添加跟踪日志
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Services/WebSocketConnection.cs`
+
+**修改内容**:
+
+1. **为CloseAsync方法添加详细的跟踪日志信息**
+ - 在方法开始时记录关闭操作的详细信息,包括关闭状态、状态描述和当前连接状态
+ - 在WebSocket连接处于打开状态时,添加Debug级别的日志记录正常关闭流程
+ - 在关闭成功时记录成功信息,包括关闭状态和状态描述
+ - 在关闭失败时记录错误信息,包括异常详情和关闭参数
+ - 在WebSocket连接未处于打开状态时,记录警告信息并跳过关闭操作
+
+2. **具体实现**:
+ ```csharp
+ public async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("开始关闭 WebSocket 连接 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 当前连接状态: {CurrentState}",
+ closeStatus, statusDescription, _webSocket.State);
+
+ if (_webSocket.State == WebSocketState.Open)
+ {
+ _logger.LogDebug("WebSocket 连接处于打开状态,执行正常关闭流程");
+ try
+ {
+ await _webSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
+ _logger.LogInformation("WebSocket 连接关闭成功 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}",
+ closeStatus, statusDescription);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "WebSocket 连接关闭失败 - 关闭状态: {CloseStatus}, 状态描述: {StatusDescription}, 错误信息: {ErrorMessage}",
+ closeStatus, statusDescription, ex.Message);
+ throw;
+ }
+ }
+ else
+ {
+ _logger.LogWarning("WebSocket 连接未处于打开状态,跳过关闭操作 - 当前状态: {CurrentState}", _webSocket.State);
+ }
+ }
+ ```
+
+3. **设计优势**:
+ - **详细跟踪**: 提供完整的关闭操作跟踪信息,便于调试和问题排查
+ - **状态监控**: 记录当前连接状态,帮助理解关闭操作的上下文
+ - **错误处理**: 完善的异常处理和错误日志记录
+ - **性能监控**: 可以跟踪关闭操作的执行情况
+ - **调试友好**: 提供足够的调试信息,便于问题定位
+ - **日志分级**: 使用不同级别的日志,避免生产环境日志过多
+
+4. **日志级别说明**:
+ - **Information**: 记录关闭操作的开始和成功完成
+ - **Debug**: 记录正常关闭流程的执行
+ - **Warning**: 记录跳过关闭操作的情况
+ - **Error**: 记录关闭失败和异常信息
+
+**影响范围**:
+- WebSocket连接关闭操作的日志记录
+- 调试和问题排查能力
+- 连接状态监控
+- 错误处理和异常跟踪
+
+## 2025-01-02
+
+### ProtocolWsClientManager.StartAllClients方法启动和连接状态检查分离
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `StartAllClients` 方法中启动客户端和检查连接状态混在一起
+ - 方法职责不够清晰,同时处理启动和连接状态检查
+ - 需要将启动和连接状态检查分离,提高代码的可读性和可维护性
+ - `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法需要适配新的接口
+
+2. **修复方案**:
+ - **分离启动和连接状态检查**:将 `StartAllClients` 方法中的连接状态检查逻辑分离出来
+ - **新增连接状态检查方法**:创建 `CheckAllClientsConnection()` 方法专门用于检查连接状态
+ - **简化启动方法**:`StartAllClients` 方法只负责启动客户端,不检查连接状态
+ - **更新接口定义**:在 `IProtocolWsClientManager` 接口中添加新的方法定义
+ - **优化返回值**:`StartAllClients` 返回是否所有客户端都成功启动,`CheckAllClientsConnection` 返回是否所有客户端都已连接
+ - **修复调用方代码**:更新 `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法以适配新的接口
+
+3. **具体修改**:
+ **StartAllClients方法优化**:
+ ```csharp
+ // 修复前 - 启动和连接状态检查混在一起
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected; // 返回连接状态
+ }
+
+ // 修复后 - 只负责启动客户端
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 启动客户端逻辑...
+
+ var allStarted = startedCount == configs.Length;
+ return allStarted; // 返回启动状态
+ }
+ ```
+
+ **新增CheckAllClientsConnection方法**:
+ ```csharp
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ public bool CheckAllClientsConnection(int timeoutSeconds = 10)
+ {
+ ThrowIfDisposed();
+
+ if (timeoutSeconds <= 0)
+ {
+ _logger.LogWarning("超时时间必须大于0,使用默认值10秒");
+ timeoutSeconds = 10;
+ }
+
+ lock (_lock)
+ {
+ if (_clients.Count == 0)
+ {
+ _logger.LogWarning("没有运行中的协议客户端");
+ return false;
+ }
+
+ _logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
+ _clients.Count, timeoutSeconds);
+
+ var startTime = DateTime.UtcNow;
+ var timeout = TimeSpan.FromSeconds(timeoutSeconds);
+ var connectedCount = 0;
+ var maxAttempts = 10; // 最大尝试次数
+ var attempt = 0;
+
+ while (attempt < maxAttempts)
+ {
+ attempt++;
+ connectedCount = 0;
+
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ if (client.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ _logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
+ attempt, kvp.Key, client.IsConnected);
+ }
+
+ var allConnected = connectedCount == _clients.Count;
+
+ if (allConnected)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
+ return true;
+ }
+
+ // 检查是否超时
+ if (DateTime.UtcNow - startTime > timeout)
+ {
+ var elapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
+ connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
+ return false;
+ }
+
+ // 等待一段时间后重试
+ if (attempt < maxAttempts)
+ {
+ var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
+ Thread.Sleep(waitTime);
+ }
+ }
+
+ var finalElapsed = DateTime.UtcNow - startTime;
+ _logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
+ connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
+
+ return false;
+ }
+ }
+ ```
+
+ **GeneralCellularNetworkService.StartAllProtocolClientsAsync方法修复**:
+ ```csharp
+ // 修复前 - 只检查启动状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+ _logger.LogInformation("所有协议客户端启动完成");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+
+ // 修复后 - 分别检查启动状态和连接状态
+ private async Task StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
+ {
+ try
+ {
+ _logger.LogInformation("开始启动所有协议客户端");
+
+ // 获取协议客户端配置
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ if (protocolConfigs == null || protocolConfigs.Length == 0)
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
+ }
+
+ _logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
+
+ // 启动所有协议客户端
+ var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
+ if (!startResult)
+ {
+ _logger.LogWarning("部分协议客户端启动失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
+ }
+
+ _logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
+
+ // 检查连接状态(使用默认10秒超时)
+ var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
+ if (!connectionResult)
+ {
+ _logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
+ }
+
+ _logger.LogInformation("所有协议客户端启动并连接成功");
+ return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启动协议客户端失败");
+ await RestoreNetworkStateAsync();
+ return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
+ }
+ }
+ ```
+
+ **接口定义更新**:
+ ```csharp
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ ///
+ /// 启动所有协议客户端
+ ///
+ /// 协议客户端配置数组
+ /// 是否所有客户端都成功启动
+ bool StartAllClients(ProtocolClientConfig[] configs);
+
+ ///
+ /// 检查所有协议客户端连接状态
+ ///
+ /// 超时时间(秒),默认10秒
+ /// 是否所有客户端都已连接
+ bool CheckAllClientsConnection(int timeoutSeconds = 10);
+
+ ///
+ /// 停止所有协议客户端
+ ///
+ /// 是否所有客户端都成功停止并断开连接
+ bool StopAllClients();
+ }
+ ```
+
+4. **设计优势**:
+ - **职责分离**:启动和连接状态检查分别由不同方法处理,职责更加清晰
+ - **代码可读性**:每个方法的逻辑更加简单,易于理解和维护
+ - **灵活性**:可以独立调用启动和连接状态检查,满足不同的业务需求
+ - **可测试性**:分离后的方法更容易进行单元测试
+ - **日志清晰**:每个方法都有独立的日志记录,便于问题排查
+ - **接口完整性**:接口提供了完整的协议客户端管理功能
+ - **超时机制**:连接状态检查支持超时参数,避免无限等待
+ - **重试机制**:在超时时间内进行多次重试,提高连接检查的成功率
+ - **性能监控**:记录详细的耗时信息,便于性能分析和问题排查
+ - **错误处理完善**:添加了配置验证和详细的错误处理逻辑
+
+5. **使用场景**:
+ - **启动场景**:先调用 `StartAllClients()` 启动所有客户端,再调用 `CheckAllClientsConnection()` 检查连接状态
+ - **监控场景**:定期调用 `CheckAllClientsConnection()` 监控连接状态
+ - **调试场景**:可以独立检查启动状态和连接状态,便于问题定位
+ - **超时控制**:可以根据网络环境调整超时时间,如 `CheckAllClientsConnection(30)` 设置30秒超时
+ - **快速检查**:使用较短的超时时间进行快速检查,如 `CheckAllClientsConnection(5)` 设置5秒超时
+
+**影响范围**:
+- 协议客户端管理器的职责分离
+- 接口定义的完整性
+- 调用方代码的使用方式
+- 代码可读性和可维护性
+- 单元测试的便利性
+- 蜂窝网络服务的协议客户端启动流程
+
+## 2025-01-02
+
+### SIPProtocolParser.GeneralParse方法严谨性修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/BuildProtocolParser/SIPProtocolParser.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - 变量名错误:`regisMccLine` 实际匹配的是MNC,`regisMncLine` 实际匹配的是MCC
+ - 空引用风险:使用了 `!` 操作符但没有进行空值检查
+ - 重复赋值:对 `log.SIP.Plmn` 进行了两次赋值,第二次会覆盖第一次
+ - 正则表达式不够严谨:缺少编译选项
+ - 返回值检查错误:`StringToId` 返回0表示空字符串,不是-1
+
+2. **修复方案**:
+ - **修复变量名错误**:将变量名与实际匹配内容对应
+ - **增强空值检查**:添加参数验证和空值检查
+ - **修复重复赋值**:分别设置PLMN和IMSI属性
+ - **优化正则表达式**:添加 `RegexOptions.Compiled` 提高性能
+ - **修复返回值检查**:使用正确的返回值判断逻辑
+ - **添加参数验证**:验证Groups数量和参数有效性
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 变量名错误
+ var regisMccLine = log!.Data.Select(line => _regMnc.Match(line))... // 实际匹配MNC
+ var regisMncLine = log!.Data.Select(line => _regMcc.Match(line))... // 实际匹配MCC
+
+ // 修复后 - 变量名正确
+ var mncValue = log.Data?.Select(line => _regMnc.Match(line))... // 匹配MNC
+ var mccValue = log.Data?.Select(line => _regMcc.Match(line))... // 匹配MCC
+
+ // 修复前 - 重复赋值
+ log.SIP.Plmn = $"{regisMncLine}{regisMccLine!.Substring(1)}";
+ log.SIP.Plmn = regisIMSILine; // 覆盖了上面的赋值
+
+ // 修复后 - 分别设置不同属性
+ if (!string.IsNullOrEmpty(mccValue) && !string.IsNullOrEmpty(mncValue))
+ {
+ log.SIP.Plmn = $"{mccValue}{mncValue}";
+ }
+ if (!string.IsNullOrEmpty(imsiValue))
+ {
+ log.SIP.IMSI = imsiValue; // 使用正确的IMSI属性
+ }
+
+ // 修复前 - 返回值检查错误
+ if (info == -1) return;
+
+ // 修复后 - 正确的返回值检查
+ if (info == 0) return; // StringToId返回0表示空字符串
+ ```
+
+4. **设计优势**:
+ - **逻辑正确性**:修复了变量名错误和重复赋值问题
+ - **空值安全**:添加了完整的空值检查和参数验证
+ - **性能优化**:正则表达式使用编译选项提高性能
+ - **代码严谨性**:添加了Groups数量验证和参数有效性检查
+ - **错误预防**:使用空条件操作符避免空引用异常
+ - **属性正确性**:使用正确的SIP属性设置PLMN和IMSI
+
+5. **修复的关键问题**:
+ - **变量命名混乱**:MCC和MNC变量名与实际匹配内容不匹配
+ - **空引用风险**:使用 `!` 操作符但没有验证对象不为空
+ - **数据覆盖**:对同一属性进行两次赋值导致数据丢失
+ - **返回值误解**:对 `StringToId` 方法返回值理解错误
+ - **正则表达式性能**:缺少编译选项影响性能
+ - **参数验证缺失**:没有验证正则匹配结果的Groups数量
+ - **日志跟踪缺失**:缺少详细的错误日志记录,难以调试问题
+
+6. **日志跟踪增强**:
+ - **添加ILogger支持**:在构造函数中创建类型安全的Logger实例
+ - **参数验证日志**:记录BuildProtocolLog参数为空的情况
+ - **StringToId失败日志**:记录StringToId返回0(空字符串)的情况
+ - **Groups数量验证日志**:记录正则匹配Groups数量不足的详细信息
+ - **性能优化**:使用私有Logger字段避免重复创建Logger实例
+
+**影响范围**:
+- SIP协议解析的准确性
+- 协议日志数据的完整性
+- 代码的稳定性和可靠性
+- 性能优化和错误预防
+- 代码可读性和维护性
+- 调试和问题排查能力显著提升
+
+### TimeStampHelper时区初始化异常修复
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs`
+
+**修改内容**:
+
+1. **问题描述**:
+ - `TimeStampHelper`类在静态初始化时抛出`System.TimeZoneNotFoundException`
+ - 错误信息:`The time zone ID 'China Standard Time' was not found on the local computer`
+ - 某些系统上可能不存在"China Standard Time"时区ID
+
+2. **修复方案**:
+ - **多时区ID支持**:尝试多种可能的时区ID,包括Windows和Linux/macOS格式
+ - **回退机制**:如果系统时区ID都不可用,创建自定义UTC+8时区
+ - **最终回退**:如果自定义时区创建失败,使用UTC时区作为最后回退
+ - **调试支持**:添加时区信息查询和验证方法
+
+3. **具体修复**:
+ ```csharp
+ // 修复前 - 直接使用单一时区ID
+ private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
+
+ // 修复后 - 支持多种时区ID和回退机制
+ private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
+
+ private static TimeZoneInfo GetChinaStandardTime()
+ {
+ // 尝试多种可能的时区ID
+ string[] timeZoneIds = {
+ "China Standard Time", // Windows
+ "Asia/Shanghai", // Linux/macOS
+ "Asia/Chongqing", // 备选
+ "Asia/Harbin", // 备选
+ "Asia/Urumqi" // 备选
+ };
+
+ foreach (string timeZoneId in timeZoneIds)
+ {
+ try
+ {
+ return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
+ }
+ catch (TimeZoneNotFoundException)
+ {
+ continue;
+ }
+ }
+
+ // 创建自定义UTC+8时区
+ try
+ {
+ return TimeZoneInfo.CreateCustomTimeZone(
+ "China Standard Time",
+ TimeSpan.FromHours(8),
+ "China Standard Time",
+ "China Standard Time");
+ }
+ catch
+ {
+ return TimeZoneInfo.Utc; // 最终回退
+ }
+ }
+ ```
+
+4. **新增调试方法**:
+ ```csharp
+ // 获取当前使用的时区信息
+ public static string GetTimeZoneInfo()
+
+ // 检查时区是否正确初始化
+ public static bool IsTimeZoneCorrectlyInitialized()
+ ```
+
+5. **设计优势**:
+ - **跨平台兼容**:支持Windows、Linux、macOS等不同操作系统
+ - **健壮性**:多层回退机制确保在任何环境下都能正常工作
+ - **调试友好**:提供时区信息查询方法便于问题排查
+ - **向后兼容**:保持原有API不变,不影响现有代码
+ - **性能优化**:静态初始化,避免重复计算
+
+6. **修复的关键问题**:
+ - **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
+ - **跨平台兼容性**:不同操作系统使用不同的时区ID格式
+ - **初始化失败**:静态构造函数异常导致整个类无法使用
+ - **调试困难**:缺少时区状态查询方法
+
+**影响范围**:
+- 时间戳工具类的跨平台兼容性
+- 系统启动时的稳定性
+- 时间转换功能的可靠性
+- 调试和问题排查能力
+
+**后续优化**:
+- 网络启动性能监控
+- 调试和问题定位
+- 日志记录详细程度
+- 性能优化分析
+
+### ProtocolWsClientManager方法参数优化
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **简化构造函数**
+ - 移除 `ProtocolClientConfig[] configs` 参数
+ - 构造函数只保留必要的依赖:`ILogger`、`IProtocolLogObserver`、`ILoggerFactory`
+ - 移除私有字段 `_configs`,不再在构造函数中存储配置
+
+2. **修改StartAllClients方法签名**
+ - 添加 `ProtocolClientConfig[] configs` 参数
+ - 方法接收配置数组作为参数,而不是依赖构造函数中的配置
+ - 添加参数验证,检查 `configs` 是否为 null 或空数组
+
+3. **优化方法逻辑**
+ - 将配置验证移到方法开始处
+ - 使用传入的 `configs` 参数替代私有字段
+ - 保持原有的客户端创建和启动逻辑不变
+
+4. **具体实现**:
+ ```csharp
+ // 构造函数简化
+ public ProtocolWsClientManager(
+ ILogger logger,
+ IProtocolLogObserver protocolLogObserver,
+ ILoggerFactory loggerFactory) // 移除 configs 参数
+
+ // StartAllClients方法修改
+ public void StartAllClients(ProtocolClientConfig[] configs) // 添加参数
+ {
+ if (configs == null || configs.Length == 0) // 参数验证
+ {
+ _logger.LogWarning("没有可用的协议客户端配置");
+ return;
+ }
+
+ // 使用传入的 configs 参数
+ _logger.LogInformation("开始启动所有协议客户端,配置数量: {ConfigCount}", configs.Length);
+ foreach (var config in configs) // 遍历传入的配置
+ ```
+
+5. **设计优势**:
+ - **更灵活的使用方式**:可以在不同时间传入不同的配置
+ - **减少内存占用**:不需要在构造函数中存储配置数组
+ - **简化构造函数**:降低构造函数的复杂度
+ - **更好的测试性**:可以更容易地测试不同的配置组合
+ - **符合单一职责原则**:构造函数只负责初始化,方法负责执行具体操作
+
+**影响范围**:
+- 协议客户端管理器使用方式
+- 配置传递方式
+- 调用方代码适配
+- 测试用例更新
+
+### ProtocolWsClientManager采用面向接口编程
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs` (新建)
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **创建IProtocolWsClientManager接口**
+ - 在CoreAgent.ProtocolClient项目中定义接口契约
+ - 继承 `IDisposable` 接口
+ - 定义 `StartAllClients` 和 `StopAllClients` 方法
+ - 使用 `ProtocolClientConfig[]` 作为参数类型
+
+2. **修改ProtocolWsClientManager实现类**
+ - 实现 `IProtocolWsClientManager` 接口
+ - 添加 `using CoreAgent.ProtocolClient.Interfaces;` 引用
+ - 保持原有的实现逻辑不变
+
+3. **具体实现**:
+ ```csharp
+ // 接口定义
+ public interface IProtocolWsClientManager : IDisposable
+ {
+ void StartAllClients(ProtocolClientConfig[] configs);
+ void StopAllClients();
+ }
+
+ // 实现类
+ public class ProtocolWsClientManager : IProtocolWsClientManager
+ {
+ // 原有实现保持不变
+ }
+ ```
+
+4. **设计优势**:
+ - **依赖倒置**:高层模块依赖抽象,不依赖具体实现
+ - **易于测试**:可以轻松创建Mock实现进行单元测试
+ - **松耦合**:降低组件间的耦合度
+ - **可扩展性**:可以轻松添加新的实现类
+ - **符合SOLID原则**:遵循依赖倒置原则和开闭原则
+ - **便于依赖注入**:可以注册接口而不是具体实现
+
+5. **接口设计原则**:
+ - **单一职责**:接口只定义协议客户端管理的核心功能
+ - **简洁明了**:只包含必要的方法定义
+ - **易于理解**:方法名称和参数清晰明确
+ - **向后兼容**:保持与原有API的兼容性
+
+**影响范围**:
+- 依赖注入配置更新
+- 服务注册方式调整
+- 单元测试Mock创建
+- 调用方代码适配(使用接口类型)
+- 项目引用关系调整(CoreAgent.ProtocolClient项目包含接口定义)
+
+### CellularNetworkService集成IProtocolWsClientManager
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs`
+
+**修改内容**:
+
+1. **添加IProtocolWsClientManager依赖注入**
+ - 在构造函数中添加 `IProtocolWsClientManager protocolWsClientManager` 参数
+ - 添加私有字段 `_protocolWsClientManager` 存储依赖
+ - 添加空值检查和异常抛出
+
+2. **StartNetworkAsync方法添加第12步**
+ - 在步骤11(更新网络状态)之后添加第12步
+ - 调用 `protocolConfigFactory.GetAllConfigs()` 获取配置数组
+ - 调用 `_protocolWsClientManager.StartAllClients(protocolConfigs)` 启动所有协议客户端
+ - 添加时间跟踪和错误处理
+ - 如果启动失败,立即返回失败结果
+
+3. **StopAsync方法集成协议客户端停止**
+ - 在步骤4(禁用网络配置)之后添加步骤5(停止所有协议客户端)
+ - 调用 `_protocolWsClientManager.StopAllClients()` 停止所有协议客户端
+ - 添加错误处理,但不中断停止流程
+ - 重新编号后续步骤(6-9步)
+
+4. **修复StopWebSocketTransportAsync方法**
+ - 修正方法实现,使用 `CloseAsync()` 而不是 `DisconnectAsync()`
+ - 修正日志信息和返回值逻辑
+ - 确保方法名称和实现一致
+
+5. **具体实现**:
+ ```csharp
+ // 构造函数添加依赖
+ public CellularNetworkService(
+ // ... 其他参数
+ IProtocolWsClientManager protocolWsClientManager)
+
+ // StartNetworkAsync第12步
+ // 12. 启动所有协议客户端
+ var protocolConfigs = protocolConfigFactory.GetAllConfigs();
+ _protocolWsClientManager.StartAllClients(protocolConfigs);
+
+ // StopAsync步骤5
+ // 5. 停止所有协议客户端
+ _protocolWsClientManager.StopAllClients();
+ ```
+
+6. **设计优势**:
+ - **完整的生命周期管理**:启动和停止时都正确处理协议客户端
+ - **错误隔离**:启动失败时立即停止,停止失败时继续执行
+ - **时间跟踪**:为协议客户端操作添加详细的时间记录
+ - **依赖注入**:使用接口编程,便于测试和扩展
+ - **日志完整**:提供详细的启动和停止日志记录
+
+**影响范围**:
+- 蜂窝网络服务依赖注入配置
+- 协议客户端生命周期管理
+- 网络启动和停止流程
+- 服务注册配置更新
+
+### LogLayerHelp类名规范化
+
+**修改时间**: 2024年
+**修改文件**:
+- `
+
+### ProtocolWsClientManager方法返回类型优化
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
+- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
+
+**修改内容**:
+
+1. **StartAllClients方法返回类型修改**
+ - 将返回类型从 `void` 改为 `bool`
+ - 添加连接状态检查逻辑,使用 `client.IsConnected` 判断连接状态
+ - 统计已连接的客户端数量
+ - 返回 `bool` 值表示是否所有客户端都成功启动并连接
+ - 添加详细的日志记录,包括连接状态信息
+
+2. **StopAllClients方法返回类型修改**
+ - 将返回类型从 `void` 改为 `bool`
+ - 添加断开连接状态检查逻辑,使用 `client.IsConnected` 判断连接状态
+ - 记录停止前的连接状态和停止后的连接状态
+ - 统计已断开连接的客户端数量
+ - 返回 `bool` 值表示是否所有客户端都成功停止并断开连接
+ - 添加详细的日志记录,包括连接状态变化信息
+
+3. **GetAllClientsStatus方法修复**
+ - 修复语法错误,完善方法实现
+ - 添加线程安全锁保护
+ - 遍历所有客户端并记录其状态信息
+ - 包括客户端名称、连接状态(IsConnected)和客户端状态(State)
+ - 添加空客户端检查
+
+4. **接口定义更新**
+ - 更新 `IProtocolWsClientManager` 接口中的方法签名
+ - `StartAllClients` 方法返回 `bool` 类型
+ - `StopAllClients` 方法返回 `bool` 类型
+ - 添加详细的XML文档注释说明返回值含义
+
+5. **具体实现**:
+ ```csharp
+ // StartAllClients方法
+ public bool StartAllClients(ProtocolClientConfig[] configs)
+ {
+ // 检查连接状态
+ if (existingClient.IsConnected)
+ {
+ connectedCount++;
+ }
+
+ var allConnected = connectedCount == configs.Length;
+ return allConnected;
+ }
+
+ // StopAllClients方法
+ public bool StopAllClients()
+ {
+ var client = kvp.Value;
+ var wasConnected = client.IsConnected;
+
+ client.Stop();
+
+ // 检查连接状态
+ if (!client.IsConnected)
+ {
+ disconnectedCount++;
+ }
+
+ var allDisconnected = disconnectedCount == _clients.Count;
+ return allDisconnected;
+ }
+
+ // GetAllClientsStatus方法
+ public void GetAllClientsStatus()
+ {
+ foreach (var kvp in _clients)
+ {
+ var client = kvp.Value;
+ _logger.LogInformation("客户端状态 - 名称: {ClientName}, 连接状态: {IsConnected}, 客户端状态: {State}",
+ kvp.Key, client.IsConnected, client.State);
+ }
+ }
+ ```
+
+6. **设计优势**:
+ - **状态可追踪**:通过返回值可以明确知道操作是否完全成功
+ - **连接状态监控**:使用 `IsConnected` 属性准确判断连接状态
+ - **详细日志记录**:提供完整的操作过程和状态变化日志
+ - **线程安全**:使用锁保护共享资源访问
+ - **错误处理完善**:提供详细的错误信息和状态统计
+ - **接口一致性**:接口和实现保持完全一致
+ - **向后兼容**:保持方法签名的一致性,只改变返回类型
+
+7. **返回值含义**:
+ - `StartAllClients` 返回 `true`:所有客户端都成功启动并连接
+ - `StartAllClients` 返回 `false`:部分或全部客户端启动失败或未连接
+ - `StopAllClients` 返回 `true`:所有客户端都成功停止并断开连接
+ - `StopAllClients` 返回 `false`:部分或全部客户端停止失败或仍保持连接
+
+**影响范围**:
+- 协议客户端管理器接口契约
+- 调用方代码需要处理返回值
+- 网络启动和停止流程的状态判断
+- 日志记录详细程度提升
+- 错误处理和状态监控能力增强
+
+### WebSocket传输服务依赖注入修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Extensions/WebSocketTransportExtensions.cs`
+- `CoreAgent.API/Startup.cs`
+
+**修改内容**:
+
+1. **修复依赖注入顺序问题**
+ - 将 `RegisterDefaultMiddleware` 移到 `RegisterCoreServices` 之前调用
+ - 确保中间件在核心服务注册之前就已经注册到容器中
+ - 解决 `provider.GetServices()` 无法找到服务的问题
+
+2. **修复CacheMiddleware注册方式**
+ - 将 `CacheMiddleware` 的注册方式从手动工厂方法改为使用 `AddWebSocketMiddleware`
+ - 简化注册逻辑,确保依赖注入容器能正确处理构造函数参数
+ - 移除复杂的工厂方法注册,使用标准的依赖注入模式
+
+3. **添加IMemoryCache服务注册**
+ - 在 `Startup.cs` 的 `ConfigureServices` 方法中添加 `services.AddMemoryCache()`
+ - 确保 `CacheMiddleware` 能够正确获取 `IMemoryCache` 依赖
+ - 在 WebSocket 传输服务注册之前添加内存缓存服务
+
+4. **具体实现**:
+ ```csharp
+ // WebSocketTransportExtensions.cs - 调整注册顺序
+ public static IServiceCollection AddWebSocketTransport(...)
+ {
+ // 注册配置
+ services.Configure(...);
+
+ // 注册默认中间件(在核心服务之前)
+ RegisterDefaultMiddleware(services);
+
+ // 注册核心服务
+ RegisterCoreServices(services);
+
+ return services;
+ }
+
+ // Startup.cs - 添加内存缓存服务
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // ... 其他服务注册 ...
+
+ // 添加内存缓存服务(WebSocket中间件需要)
+ services.AddMemoryCache();
+
+ // 添加 WebSocket 传输服务
+ services.AddWebSocketTransport(Configuration, "WebSocket");
+ }
+ ```
+
+5. **设计优势**:
+ - **依赖顺序正确**:确保中间件在核心服务之前注册
+ - **简化注册逻辑**:使用标准的依赖注入模式
+ - **完整的服务注册**:包含所有必要的依赖服务
+ - **错误预防**:避免运行时依赖注入异常
+ - **代码清晰**:注册逻辑更加直观和易于理解
+
+6. **修复的问题**:
+ - `System.InvalidOperationException: Cannot resolve scoped service 'System.Collections.Generic.IEnumerable`1[CoreAgent.WebSocketTransport.Middleware.IMessageMiddleware]' from root provider`
+ - 依赖注入容器无法找到 `IMessageMiddleware` 服务
+ - `CacheMiddleware` 无法获取 `IMemoryCache` 依赖
+
+**影响范围**:
+- WebSocket传输服务的依赖注入配置
+- 中间件注册和初始化顺序
+- 应用程序启动时的服务注册
+- 内存缓存服务的可用性
+- 错误处理和异常预防
+
+### CacheMiddleware构造函数依赖注入修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Middleware/CacheMiddleware.cs`
+
+**修改内容**:
+
+1. **修复构造函数参数类型**
+ - 将构造函数参数从 `WebSocketConfig config` 改为 `IOptions config`
+ - 添加 `using Microsoft.Extensions.Options;` 引用
+ - 在构造函数中通过 `config?.Value` 获取配置值
+
+2. **增强空值检查**
+ - 为所有构造函数参数添加空值检查和异常抛出
+ - 使用 `ArgumentNullException` 确保参数有效性
+ - 提供更明确的错误信息
+
+3. **具体实现**:
+ ```csharp
+ // 修复前
+ public CacheMiddleware(IMemoryCache cache, ILogger logger, WebSocketConfig config)
+ {
+ _cache = cache;
+ _logger = logger;
+ _config = config;
+ }
+
+ // 修复后
+ public CacheMiddleware(IMemoryCache cache, ILogger logger, IOptions config)
+ {
+ _cache = cache ?? throw new ArgumentNullException(nameof(cache));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _config = config?.Value ?? throw new ArgumentNullException(nameof(config));
+ }
+ ```
+
+4. **设计优势**:
+ - **正确的依赖注入模式**:使用 `IOptions` 模式获取配置
+ - **强化的错误处理**:提供详细的空值检查和异常信息
+ - **类型安全**:确保配置对象正确获取
+ - **符合最佳实践**:遵循 .NET 依赖注入的标准模式
+
+5. **修复的问题**:
+ - `Unable to resolve service for type 'CoreAgent.WebSocketTransport.Models.WebSocketConfig'`
+ - 依赖注入容器无法直接解析 `WebSocketConfig` 类型
+ - 中间件构造函数参数类型不匹配
+
+**影响范围**:
+- CacheMiddleware的依赖注入配置
+- WebSocket传输服务的启动
+- 配置对象的正确获取
+- 错误处理和异常预防
+
+### WebSocket中间件生命周期修复
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.WebSocketTransport/Extensions/WebSocketTransportExtensions.cs`
+
+**修改内容**:
+
+1. **修复生命周期不匹配问题**
+ - 将中间件注册从 `AddScoped` 改为 `AddTransient`
+ - 解决单例服务无法解析作用域服务的问题
+ - 确保中间件每次使用都创建新实例
+
+2. **生命周期分析**
+ - `IWebSocketTransport` 注册为 `Singleton`(单例)
+ - 中间件注册为 `Transient`(瞬时)
+ - 单例服务可以安全地解析瞬时服务
+ - 每次获取中间件都会创建新实例
+
+3. **具体实现**:
+ ```csharp
+ // 修复前
+ services.AddScoped();
+
+ // 修复后
+ services.AddTransient();
+ ```
+
+4. **设计优势**:
+ - **生命周期兼容**:瞬时服务可以被单例服务安全解析
+ - **性能优化**:中间件每次使用都是新实例,避免状态污染
+ - **线程安全**:瞬时服务天然线程安全
+ - **内存管理**:中间件使用完毕后自动释放
+
+5. **修复的问题**:
+ - `Cannot resolve scoped service 'System.Collections.Generic.IEnumerable`1[CoreAgent.WebSocketTransport.Middleware.IMessageMiddleware]' from root provider`
+ - 单例服务无法解析作用域服务的依赖注入异常
+ - 生命周期不匹配导致的运行时错误
+
+6. **生命周期说明**:
+ - **Singleton**: 整个应用程序生命周期内只有一个实例
+ - **Scoped**: 每个请求作用域内有一个实例
+ - **Transient**: 每次请求都创建新实例
+ - 单例服务只能解析瞬时服务,不能解析作用域服务
+
+**影响范围**:
+- WebSocket中间件的生命周期管理
+- 依赖注入容器的服务解析
+- 应用程序启动时的服务注册
+- 中间件的实例化策略
+- 性能和内存使用优化
+
+### NetworkConfigCopier方法Bug修复和日志增强
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+- `CoreAgent.Domain/Models/Network/NetworkConfigCopyResult.cs`
+- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
+
+**修改内容**:
+
+1. **返回类型统一化**
+ - **统一返回类型**:所有方法都返回 `NetworkConfigCopyResult` 类型,不再抛出异常
+ - **泛型结果类型**:创建 `NetworkConfigCopyResult` 泛型类,支持返回数据
+ - **接口更新**:更新 `INetworkConfigCopier` 接口以匹配新的返回类型
+ - **错误处理改进**:使用结果对象而不是异常来处理错误情况
+
+2. **CreateCellularNetworkConfigurationFile方法修复**
+ - **返回类型修改**:从 `Task` 改为 `Task`
+ - **路径构建Bug修复**:修复 `Path.Combine` 使用不当的问题,正确构建文件路径
+ - **参数验证增强**:添加完整的参数空值检查,返回失败结果而不是抛出异常
+ - **目录创建**:确保目标目录存在,避免文件写入失败
+ - **详细日志**:添加每个步骤的详细日志记录,包括开始、成功、警告和错误信息
+
+3. **CreateRadioAccessNetworkConfigurationFile方法修复**
+ - **返回类型修改**:从 `Task` 改为 `Task`
+ - **参数命名规范化**:使用小写开头的参数名,符合C#命名约定
+ - **目录创建逻辑**:添加目录存在性检查和自动创建
+ - **错误处理增强**:返回失败结果而不是抛出异常
+ - **参数验证**:验证文件路径和配置内容不为空
+
+4. **CreateCoreNetworkImsConfigurationFiles方法重构**
+ - **返回类型修改**:从 `Task>` 改为 `Task>>`
+ - **方法重命名**:避免与RAN配置文件创建方法名冲突
+ - **参数验证完善**:验证RuntimeCode、配置列表和AppSettings
+ - **配置项验证**:验证每个配置项的PLMN和配置内容
+ - **目录创建**:为CN和IMS配置文件分别创建目录
+ - **错误隔离**:单个配置项失败不影响其他配置项处理
+ - **详细日志**:记录每个配置项的处理过程和结果
+
+5. **DeleteCellularNetworkConfigurationFile方法增强**
+ - **返回类型修改**:从 `bool` 改为 `NetworkConfigCopyResult`
+ - **文件存在性检查**:删除前检查文件是否存在,避免异常
+ - **错误隔离**:单个文件删除失败不影响其他文件删除
+ - **删除统计**:统计成功删除的文件数量
+ - **详细日志**:记录每个文件的删除状态和结果
+ - **参数验证**:验证NetworkConfiguration参数不为空
+
+6. **NetworkConfigCopyResult类扩展**
+ - **泛型支持**:添加 `NetworkConfigCopyResult` 泛型类
+ - **数据返回**:支持返回操作结果的同时返回数据
+ - **继承关系**:泛型类继承自基础类,保持类型安全
+ - **静态工厂方法**:提供 `Success(T data)` 和 `Failure(string errorMessage)` 方法
+
+7. **具体修复的Bug**:
+ ```csharp
+ // 修复前 - 路径构建错误
+ string RanConfigPath = $"{appSettings.RanConfigDirectory}{Path.Combine("RAN", $"{cellular.RuntimeCode}.cfg")}";
+
+ // 修复后 - 正确的路径构建
+ string ranConfigPath = Path.Combine(appSettings.RanConfigDirectory, "RAN", $"{cellular.RuntimeCode}.cfg");
+
+ // 修复前 - 抛出异常
+ throw new ArgumentNullException(nameof(cellular));
+
+ // 修复后 - 返回失败结果
+ return NetworkConfigCopyResult.Failure("CellularNetworkConfiguration 参数为空");
+
+ // 修复前 - 返回原始类型
+ public async Task CreateCellularNetworkConfigurationFile(...)
+
+ // 修复后 - 返回结果类型
+ public async Task CreateCellularNetworkConfigurationFile(...)
+ ```
+
+8. **设计优势**:
+ - **统一错误处理**:所有方法都使用结果对象,避免异常传播
+ - **类型安全**:泛型结果类型确保类型安全
+ - **Bug修复**:修复路径构建、方法名冲突、文件删除等关键Bug
+ - **错误处理完善**:添加完整的错误处理和错误隔离机制
+ - **日志详细**:提供完整的操作跟踪和调试信息
+ - **参数验证**:确保所有输入参数的有效性
+ - **目录管理**:自动创建必要的目录结构
+ - **错误隔离**:单个操作失败不影响整体流程
+ - **命名规范**:遵循C#命名约定,提高代码可读性
+ - **方法职责清晰**:每个方法都有明确的职责和边界
+
+9. **修复的关键问题**:
+ - **异常处理不一致**:统一使用结果对象而不是异常
+ - **路径构建错误**:`Path.Combine` 使用不当导致路径错误
+ - **方法名冲突**:两个不同功能的方法使用相同名称
+ - **文件删除异常**:删除不存在的文件导致异常
+ - **目录不存在**:目标目录不存在导致文件写入失败
+ - **错误传播**:单个错误导致整个操作失败
+ - **日志缺失**:缺少关键操作的日志记录
+
+**影响范围**:
+- 网络配置文件创建和删除的稳定性
+- 错误处理和异常预防
+- 日志记录和调试能力
+- 代码可读性和维护性
+- 文件系统操作的可靠性
+- 配置管理的完整性
+- 接口契约的一致性
+- 调用方代码的错误处理方式
+
+### NetworkConfigCopier返回类型优化和GeneralCellularNetworkService适配
+
+**修改时间**: 2024年
+**修改文件**:
+- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
+- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
+- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
+
+**修改内容**:
+
+1. **CreateCellularNetworkConfigurationFile返回类型优化**
+ - **返回类型修改**:从 `Task` 改为 `Task>`
+ - **数据返回**:成功时返回包含 `NetworkConfiguration` 对象的结果
+ - **接口更新**:更新接口定义以匹配新的返回类型
+ - **调用方适配**:修改调用方代码以正确使用返回的数据
+
+2. **GeneralCellularNetworkService调用修复**
+ - **返回类型适配**:修改调用方式以适配新的泛型返回类型
+ - **数据获取**:直接从结果对象的 `Data` 属性获取 `NetworkConfiguration`
+ - **错误处理改进**:使用 `IsSuccess` 属性检查操作是否成功
+ - **代码简化**:移除手动创建 `NetworkConfiguration` 的代码
+
+3. **接口定义更新**
+ - **泛型支持**:接口方法返回 `Task>`
+ - **文档更新**:更新XML文档注释说明返回的数据类型
+
+4. **具体修改**:
+ ```csharp
+ // NetworkConfigCopier.cs - 返回类型修改
+ // 修复前
+ public async Task CreateCellularNetworkConfigurationFile(...)
+ {
+ // ... 创建NetworkConfiguration对象
+ return NetworkConfigCopyResult.Success(); // 没有返回数据
+ }
+
+ // 修复后
+ public async Task> CreateCellularNetworkConfigurationFile(...)
+ {
+ // ... 创建NetworkConfiguration对象
+ return NetworkConfigCopyResult.Success(network); // 返回数据
+ }
+
+ // GeneralCellularNetworkService.cs - 调用修复
+ // 修复前 - 手动创建配置对象
+ var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!createResult.IsSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
+ }
+
+ var config = NetworkConfiguration.Create(cellular.RuntimeCode,
+ Path.Combine(_context.GetAppSettings().RanConfigDirectory, "RAN", $"{cellular.RuntimeCode}.cfg"),
+ new List());
+
+ // 修复后 - 直接使用返回的数据
+ var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
+ if (!createResult.IsSuccess)
+ {
+ return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
+ }
+
+ var config = createResult.Data; // 直接获取返回的NetworkConfiguration对象
+ ```
+
+5. **设计优势**:
+ - **数据完整性**:返回完整的 `NetworkConfiguration` 对象,包含所有配置信息
+ - **类型安全**:泛型结果类型确保类型安全
+ - **代码简化**:调用方无需手动创建配置对象
+ - **错误处理统一**:保持统一的错误处理模式
+ - **接口一致性**:接口和实现保持完全一致
+
+6. **修复的关键问题**:
+ - **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
+ - **跨平台兼容性**:不同操作系统使用不同的时区ID格式
+ - **初始化失败**:静态构造函数异常导致整个类无法使用
+
+**影响范围**:
+- 时区选择的简化和优化
+- 性能的提升
+- 代码简洁性的增强
+- Windows依赖的完全移除
+
+## 2024年修改记录
+
+### 修复NetworkProtocolLogObserver中的StopChannelManager方法并支持重新创建
+
+**修改时间**: 2024年12月
+
+
## 2025-01-02
### 日志规范修改 - GeneralCellularNetworkService和CellularNetworkContext
@@ -11802,4 +18273,76 @@ feat: 优化NetworkProtocolLogObserver并发处理和ProtocolWsClientManager资
- 协议日志处理的并发安全性
- 协议客户端的资源管理
- 系统稳定性和性能
-- 代码可维护性
\ No newline at end of file
+- 代码可维护性
+
+## 2025-01-02
+
+### 添加SetTxGainAsync方法 - 实现发送增益设置接口
+
+**修改时间**: 2025年1月2日
+**修改文件**:
+- `CoreAgent.Domain/Interfaces/Network/IRanAPICommandHandler.cs`
+- `CoreAgent.Infrastructure/Services/Network/RanAPICommandHandler.cs`
+
+**修改内容**:
+
+1. **接口扩展**:
+ - 在 `IRanAPICommandHandler` 接口中添加 `SetTxGainAsync` 方法
+ - 方法签名:`Task SetTxGainAsync(RanIPEndPoint ranEndPoint, int setGain, int port)`
+
+2. **实现方法**:
+ ```csharp
+ public async Task SetTxGainAsync(RanIPEndPoint ranEndPoint, int setGain, int port)
+ {
+ if (ranEndPoint == null)
+ {
+ _logger.LogWarning("RAN 端点未配置");
+ return false;
+ }
+
+ try
+ {
+ var cmd_prefix = $"{_appSettings.WebSocketJsPath} {ranEndPoint.ComAddr}";
+ var cmd_setGain = cmd_prefix + $@" '{{""message"":""rf"",""tx_gain"":{setGain},""tx_channel_index"":{port}}}'";
+
+ _logger.LogInformation("开始设置发送增益,地址: {ComAddr}, 增益值: {SetGain}, 端口: {Port}", ranEndPoint.ComAddr, setGain, port);
+
+ var result = await _commandExecutor.ExecuteCommandAsync(cmd_setGain, new CancellationTokenSource());
+
+ if (result.IsSuccess)
+ {
+ _logger.LogInformation("发送增益设置成功,地址: {ComAddr}, 增益值: {SetGain}, 端口: {Port}", ranEndPoint.ComAddr, setGain, port);
+ return true;
+ }
+ else
+ {
+ _logger.LogWarning("发送增益设置失败,地址: {ComAddr}, 增益值: {SetGain}, 端口: {Port}, 错误: {Error}",
+ ranEndPoint.ComAddr, setGain, port, result.Error);
+ return false;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "设置发送增益时发生异常,地址: {ComAddr}, 增益值: {SetGain}, 端口: {Port}", ranEndPoint.ComAddr, setGain, port);
+ return false;
+ }
+ }
+ ```
+
+3. **命令格式**:
+ - 严格按照您提供的格式实现:`cmd_setGain = cmd_prefix + " '{"message":"rf","tx_gain":" + setGain.ToString() + ","tx_channel_index":" + port.ToString() + "}'"`
+ - 使用C#字符串插值语法:`$@" '{{""message"":""rf"",""tx_gain"":{setGain},""tx_channel_index"":{port}}}'"`
+
+4. **功能特点**:
+ - **参数验证**:检查RAN端点是否为null
+ - **命令构建**:按照指定格式构建WebSocket命令
+ - **执行命令**:使用系统命令执行器执行命令
+ - **结果处理**:根据执行结果返回成功或失败
+ - **异常处理**:捕获并记录异常信息
+ - **日志记录**:详细记录操作过程和结果
+
+**影响范围**:
+- RAN API命令处理器功能扩展
+- 发送增益设置功能
+- 接口定义的完整性
+