Browse Source

修复NetworkConfigCopier方法返回类型,改为元组返回模式

- 修改CreateCellularNetworkConfigurationFile返回类型为元组
- 修改CreateCoreNetworkImsConfigurationFiles返回类型为元组
- 更新接口定义和调用代码
- 简化错误处理,提高代码可读性
- 更新modify.md记录修改内容
feature/protocol-log-Perfect
root 4 days ago
parent
commit
f8a2f31a58
  1. 4
      CoreAgent.Domain/CoreAgent.Domain.csproj
  2. 29
      CoreAgent.Domain/Entities/NetworkConfiguration.cs
  3. 25
      CoreAgent.Domain/Interfaces/Network/IGeneralCellularNetworkService.cs
  4. 17
      CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs
  5. 60
      CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs
  6. 2
      CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs
  7. 512
      CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs
  8. 339
      CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs
  9. 615
      modify.md

4
CoreAgent.Domain/CoreAgent.Domain.csproj

@ -20,8 +20,4 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\Network\" />
</ItemGroup>
</Project>

29
CoreAgent.Domain/Entities/NetworkConfiguration.cs

@ -31,18 +31,9 @@ namespace CoreAgent.Domain.Entities
[JsonPropertyName("coreOrImsConfigs")]
public List<CoreImsConfig> CoreOrImsConfigs { get; set; }
/// <summary>
/// APN配置
/// </summary>
[Required(ErrorMessage = "APN配置不能为空")]
[JsonPropertyName("apn")]
public string Apn { get; set; }
/// <summary>
/// 频段配置
/// </summary>
[Required(ErrorMessage = "频段配置不能为空")]
[MinLength(1, ErrorMessage = "至少需要一个频段配置")]
[JsonPropertyName("band")]
public List<string> Band { get; set; }
@ -69,8 +60,8 @@ namespace CoreAgent.Domain.Entities
string configKey,
string ragConfig,
List<CoreImsConfig> coreOrImsConfigs,
string apn,
List<string> band,
string apn=null,
List<string> band=null,
string comment = null)
{
var config = new NetworkConfiguration
@ -118,13 +109,13 @@ namespace CoreAgent.Domain.Entities
}
// 验证频段格式
foreach (var band in Band)
{
if ((!band.StartsWith("B") && !band.StartsWith("N")) || !int.TryParse(band.Substring(1), out _))
{
yield return new ValidationResult($"频段格式不正确: {band},应为 B1、B2 或者 N78 等格式");
}
}
//foreach (var band in Band)
//{
// if ((!band.StartsWith("B") && !band.StartsWith("N")) || !int.TryParse(band.Substring(1), out _))
// {
// yield return new ValidationResult($"频段格式不正确: {band},应为 B1、B2 或者 N78 等格式");
// }
//}
}
/// <summary>
@ -181,7 +172,6 @@ namespace CoreAgent.Domain.Entities
/// 核心网配置文件路径
/// </summary>
[Required(ErrorMessage = "核心网配置路径不能为空")]
//[RegularExpression(@"^Config/CoreNetwork/.*\.cfg$", ErrorMessage = "核心网配置路径格式不正确")]
[JsonPropertyName("coreNetworkConfig")]
public string CoreNetworkConfig { get; set; }
@ -189,7 +179,6 @@ namespace CoreAgent.Domain.Entities
/// IMS配置文件路径
/// </summary>
[Required(ErrorMessage = "IMS配置路径不能为空")]
//[RegularExpression(@"^Config/Ims/.*\.cfg$", ErrorMessage = "IMS配置路径格式不正确")]
[JsonPropertyName("imsConfig")]
public string ImsConfig { get; set; }
}

25
CoreAgent.Domain/Interfaces/Network/IGeneralCellularNetworkService.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.Domain.Models.Network;
namespace CoreAgent.Domain.Interfaces.Network
{
public interface IGeneralCellularNetworkService
{
/// <summary>
/// 启动蜂窝网络
/// </summary>
/// <param name="Key">网络配置映射Key</param>
/// <returns>启动结果</returns>
Task<CellularNetworkOperationResult> StartAsync(CellularNetworkConfiguration cellular);
/// <summary>
/// 停止蜂窝网络
/// </summary>
/// <returns>停止结果</returns>
Task<CellularNetworkOperationResult> StopAsync(string RuntimeCode);
}
}

17
CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs

@ -9,13 +9,28 @@ namespace CoreAgent.Domain.Interfaces.Network;
/// </summary>
public interface INetworkConfigCopier
{
/// <summary>
/// 创建蜂窝网络配置文件
/// </summary>
/// <param name="cellular">蜂窝网络配置</param>
/// <param name="appSettings">应用设置</param>
/// <returns>(IsSuccess: 是否成功, ErrorMessage: 错误信息, NetworkConfiguration: 网络配置对象)</returns>
Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)> CreateCellularNetworkConfigurationFile(CellularNetworkConfiguration cellular, AppSettings appSettings);
/// <summary>
/// 删除蜂窝网络配置文件
/// </summary>
/// <param name="configuration">网络配置</param>
/// <returns>删除结果</returns>
NetworkConfigCopyResult DeleteCellularNetworkConfigurationFile(NetworkConfiguration configuration);
/// <summary>
/// 将配置值复制到临时目录并更新配置路径
/// </summary>
/// <param name="networkConfig">网络配置</param>
/// <param name="appSettings">应用设置</param>
/// <returns>复制结果</returns>
Task<NetworkConfigCopyResult> CopyConfigValuesToTempAsync(NetworkConfiguration networkConfig, AppSettings appSettings);
NetworkConfigCopyResult CopyConfigValuesToTempAsync(NetworkConfiguration networkConfig, AppSettings appSettings);
/// <summary>
/// 获取所有配置文件的 IP 端点信息

60
CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs

@ -0,0 +1,60 @@
using System.ComponentModel.DataAnnotations;
namespace CoreAgent.Domain.Models.Network;
/// <summary>
/// 蜂窝网络配置实体
/// </summary>
public class CellularNetworkConfiguration
{
/// <summary>
/// 设备代码
/// </summary>
[Required(ErrorMessage = "设备代码不能为空")]
public string DeviceCode { get; set; }
/// <summary>
/// 运行时代码
/// </summary>
[Required(ErrorMessage = "运行时代码不能为空")]
public string RuntimeCode { get; set; }
/// <summary>
/// 无线接入网配置
/// </summary>
public string RadioAccessNetworkConfiguration { get; set; }
/// <summary>
/// 核心网IMS配置集合
/// </summary>
public List<CoreNetworkImsConfiguration> CoreNetworkImsConfigurations { get; set; } = new List<CoreNetworkImsConfiguration>();
}
/// <summary>
/// 核心网IMS配置对象
/// </summary>
public class CoreNetworkImsConfiguration
{
/// <summary>
/// 索引
/// </summary>
public int Index { get; set; }
/// <summary>
/// PLMN标识
/// </summary>
[Required(ErrorMessage = "PLMN标识不能为空")]
public string Plmn { get; set; }
/// <summary>
/// 核心网配置
/// </summary>
public string CoreNetworkConfiguration { get; set; }
/// <summary>
/// IMS服务配置
/// </summary>
public string ImsServiceConfiguration { get; set; }
}

2
CoreAgent.Infrastructure/Services/Network/CellularNetworkService.cs

@ -307,7 +307,7 @@ public class CellularNetworkService : ICellularNetworkService
var step3Start = DateTime.UtcNow;
_logger.LogDebug("步骤3开始:复制配置值到临时目录");
var copyResult = await _configCopier.CopyConfigValuesToTempAsync(config, _context.GetAppSettings());
var copyResult = _configCopier.CopyConfigValuesToTempAsync(config, _context.GetAppSettings());
if (!copyResult.IsSuccess)
{
var message = $"复制配置值到临时目录失败: {copyResult.ErrorMessage}";

512
CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs

@ -0,0 +1,512 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.Domain.Entities;
using CoreAgent.Domain.Interfaces.Network;
using CoreAgent.Domain.Interfaces.System.Command;
using CoreAgent.Domain.Interfaces;
using CoreAgent.Domain.Models.Network;
using CoreAgent.ProtocolClient.Interfaces;
using CoreAgent.ProtocolClient.ProtocolEngineCore;
using CoreAgent.WebSocketTransport.Interfaces;
using Microsoft.Extensions.Logging;
namespace CoreAgent.Infrastructure.Services.Network
{
/// <summary>
/// 蜂窝网络服务实现
/// </summary>
public class GeneralCellularNetworkService : IGeneralCellularNetworkService
{
private readonly ILogger<CellularNetworkService> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly ISystemCommandExecutor _commandExecutor;
private readonly INetworkConfigurationService _configService;
private readonly ICellularNetworkContext _context;
private readonly INetworkConfigCopier _configCopier;
private readonly INetworkInterfaceManager _interfaceManager;
private readonly INetworkStatusMonitor _statusMonitor;
private readonly IWebSocketTransport _webSocketTransport;
private readonly IProtocolLogObserver _protocolLogObserver;
private readonly IProtocolWsClientManager _protocolWsClientManager;
private static readonly SemaphoreSlim _startLock = new(1, 1);
private const int LockTimeoutSeconds = 60;
public GeneralCellularNetworkService(
ILogger<CellularNetworkService> logger,
ILoggerFactory loggerFactory,
ISystemCommandExecutor commandExecutor,
INetworkConfigurationService configService,
ICellularNetworkContext context,
INetworkConfigCopier configCopier,
INetworkInterfaceManager interfaceManager,
INetworkStatusMonitor statusMonitor,
IWebSocketTransport webSocketTransport,
IProtocolLogObserver protocolLogObserver,
IProtocolWsClientManager protocolWsClientManager)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
_commandExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor));
_configService = configService ?? throw new ArgumentNullException(nameof(configService));
_context = context ?? throw new ArgumentNullException(nameof(context));
_configCopier = configCopier ?? throw new ArgumentNullException(nameof(configCopier));
_interfaceManager = interfaceManager ?? throw new ArgumentNullException(nameof(interfaceManager));
_statusMonitor = statusMonitor;
_webSocketTransport = webSocketTransport ?? throw new ArgumentNullException(nameof(webSocketTransport));
_protocolLogObserver = protocolLogObserver ?? throw new ArgumentNullException(nameof(protocolLogObserver));
_protocolWsClientManager = protocolWsClientManager ?? throw new ArgumentNullException(nameof(protocolWsClientManager));
}
/// <summary>
/// 启动蜂窝网络
/// </summary>
/// <param name="key">网络配置键</param>
/// <returns>启动结果</returns>
public async Task<CellularNetworkOperationResult> StartAsync(CellularNetworkConfiguration cellular)
{
string key = cellular.RuntimeCode;
// 1. 参数验证
if (string.IsNullOrEmpty(key))
{
_logger.LogError("启动蜂窝网络失败:配置键为空");
return CellularNetworkOperationResult.Failure("启动蜂窝网络失败:配置键为空");
}
try
{
// 2. 获取启动锁,防止并发启动
if (!await _startLock.WaitAsync(TimeSpan.FromSeconds(LockTimeoutSeconds)))
{
_logger.LogWarning("蜂窝网络启动操作被锁定,可能已有其他启动操作正在进行");
return CellularNetworkOperationResult.Failure("蜂窝网络启动操作被锁定,可能已有其他启动操作正在进行");
}
// 3. 状态检查
// 如果是第二次启动(已初始化),需要先检查网络状态
if (_context.IsInitialized)
{
// 检查网络状态,确保没有冲突的配置
var stateCheckResult = CheckNetworkState(key);
if (!stateCheckResult.IsSuccess)
{
return stateCheckResult;
}
}
// 4. 初始化网络上下文
_context.Initialize(key);
// 5. 启动网络
var result = await StartNetworkAsync(cellular);
if (!result.IsSuccess)
{
_logger.LogWarning("启动蜂窝网络内部操作失败,重置上下文");
_context.Reset();
return result;
}
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "启动蜂窝网络失败");
// 重置上下文
_context.Reset();
return CellularNetworkOperationResult.Failure($"启动蜂窝网络失败: {ex.Message}");
}
finally
{
_startLock.Release();
}
}
/// <summary>
/// 停止蜂窝网络
/// </summary>
/// <param name="key">网络配置键</param>
/// <returns>停止操作的结果</returns>
public async Task<CellularNetworkOperationResult> StopAsync(string key)
{
string neConfigKey = _context.GetNeConfigKey();
try
{
// 检查是否是相同的配置
if (key != neConfigKey)
{
var message = $"停止操作失败:当前运行配置 {neConfigKey} 与请求停止的配置 {key} 不匹配";
_logger.LogWarning(message);
return CellularNetworkOperationResult.Failure(message);
}
// 1. 检查当前网络状态
var state = _context.GetNetworkState();
if (state.CurrentStatus == NetworkStatus.Disconnected || state.CurrentStatus == NetworkStatus.Unknown)
{
_logger.LogWarning("蜂窝网络已经处于断开或未知状态,无需停止");
_context.Reset(); // 重置上下文状态
return CellularNetworkOperationResult.Success(NetworkStatus.Disconnected);
}
// 2. 检查 RAN 退出状态(仅当配置类型为 BothRagAndCore 或 RagOnly 时)
if (_context.CurrentConfigType is NetworkConfigType.BothRagAndCore or NetworkConfigType.RagOnly)
{
var isRanQuit = await _statusMonitor.CheckRanQuitAsync(_context.NetworkIPEndPointManager.GetRanEndPoint());
if (!isRanQuit)
{
_logger.LogWarning("RAN 退出状态检查失败");
return CellularNetworkOperationResult.Failure("RAN 退出状态检查失败");
}
}
// 3. 执行网络接口初始化命令
var initResult = await _interfaceManager.ExecuteInitializeCommandsAsync();
if (!initResult.IsSuccess)
{
_logger.LogWarning("执行初始化命令失败: {ErrorMessage}", initResult.ErrorMessage);
}
// 4. 禁用网络配置
var disableResult = await _interfaceManager.DisableAsync(neConfigKey);
if (!disableResult.IsSuccess)
{
return CellularNetworkOperationResult.Failure(disableResult.ErrorMessage);
}
// 5. 停止所有协议客户端
try
{
_logger.LogInformation("开始停止所有协议客户端");
_protocolWsClientManager.StopAllClients();
_logger.LogInformation("所有协议客户端停止完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "停止协议客户端失败");
// 不返回失败,继续执行后续步骤
}
// 6. 停止 WebSocket 传输连接
var webSocketConnected = await StopWebSocketTransportAsync();
if (!webSocketConnected)
{
_logger.LogError("WebSocket 传输连接停止失败,服务端可能未启动");
return CellularNetworkOperationResult.Failure("WebSocket 传输连接停止失败");
}
_logger.LogInformation("WebSocket 传输连接停止成功");
// 7. 收集所有网络端点信息
var endPoints = new NetworkIPEndPointCollection
{
RanEndPoint = _context.NetworkIPEndPointManager.GetRanEndPoint(),
ImsEndPoints = _context.NetworkIPEndPointManager.GetImsEndPoints(),
CnEndPoints = _context.NetworkIPEndPointManager.GetCnEndPoints()
};
// 8. 检查网络端点连接状态
_logger.LogInformation("开始检查所有网络端点的连接状态");
var statusCheckResult = await _statusMonitor.CheckAllEndPointsStatusAsync(endPoints, _context.CurrentConfigType, isStartOperation: false);
if (!statusCheckResult.IsSuccess)
{
_logger.LogWarning("网络端点仍然处于连接状态,停止操作失败");
return CellularNetworkOperationResult.Failure("网络端点仍然处于连接状态,停止操作失败");
}
// 9. 重置上下文并返回成功结果
_context.Reset();
return CellularNetworkOperationResult.Success(NetworkStatus.Disconnected);
}
catch (Exception ex)
{
_logger.LogError(ex, "停止蜂窝网络配置 {ConfigKey} 失败", neConfigKey);
return CellularNetworkOperationResult.Failure($"停止蜂窝网络失败: {ex.Message}");
}
}
/// <summary>
/// 检查网络状态
/// </summary>
/// <param name="newConfigKey">新的网络配置键</param>
/// <returns>检查结果</returns>
private CellularNetworkOperationResult CheckNetworkState(string newConfigKey)
{
try
{
// 获取当前网络状态
var currentKey = _context.GetNeConfigKey();
var currentState = _context.GetNetworkState();
// 检查是否已初始化
if (!string.IsNullOrEmpty(currentKey))
{
// 检查是否是相同的配置
if (currentKey == newConfigKey)
{
// 检查当前状态
if (currentState.CurrentStatus == NetworkStatus.Connected)
{
return CellularNetworkOperationResult.Failure("当前网络配置已经处于连接状态");
}
}
else
{
// 检查当前网络状态
if (currentState.CurrentStatus == NetworkStatus.Connected)
{
var message = $"检测到不同的网络配置,当前运行配置: {currentKey},新配置: {newConfigKey},请先停止当前网络";
_logger.LogWarning(message);
return CellularNetworkOperationResult.Failure(message);
}
}
}
return CellularNetworkOperationResult.Success(NetworkStatus.Unknown);
}
catch (Exception ex)
{
_logger.LogError(ex, "检查网络状态失败");
return CellularNetworkOperationResult.Failure($"检查网络状态失败: {ex.Message}");
}
}
private async Task<CellularNetworkOperationResult> StartNetworkAsync(CellularNetworkConfiguration cellular)
{
string key =cellular.RuntimeCode;
var startTime = DateTime.UtcNow;
_logger.LogInformation("开始启动网络配置 {ConfigKey},开始时间: {StartTime}", key, startTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
// 1. 获取并验证网络配置
var step1Start = DateTime.UtcNow;
_logger.LogDebug("步骤1开始:获取并验证网络配置");
var (isSuccess, errorMsg, config) = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
if (!isSuccess)
{
var message = $"创建网络配置文件失败: {errorMsg}";
_logger.LogError(message);
return CellularNetworkOperationResult.Failure(message);
}
var step1Duration = (DateTime.UtcNow - step1Start).TotalMilliseconds;
_logger.LogDebug("步骤1完成:获取并验证网络配置,耗时: {Duration}ms", step1Duration.ToString("F2"));
// 2. 执行网络接口初始化命令
var step2Start = DateTime.UtcNow;
_logger.LogDebug("步骤2开始:执行网络接口初始化命令");
var initResult = await _interfaceManager.ExecuteInitializeCommandsAsync(true);
if (!initResult.IsSuccess)
{
_logger.LogWarning("执行初始化命令失败: {ErrorMessage}", initResult.ErrorMessage);
}
var step2Duration = (DateTime.UtcNow - step2Start).TotalMilliseconds;
_logger.LogDebug("步骤2完成:执行网络接口初始化命令,耗时: {Duration}ms", step2Duration.ToString("F2"));
// 3. 复制配置值到临时目录
var step3Start = DateTime.UtcNow;
_logger.LogDebug("步骤3开始:复制配置值到临时目录");
var copyResult = _configCopier.CopyConfigValuesToTempAsync(config, _context.GetAppSettings());
if (!copyResult.IsSuccess)
{
var message = $"复制配置值到临时目录失败: {copyResult.ErrorMessage}";
_logger.LogError(message);
return CellularNetworkOperationResult.Failure(message);
}
var step3Duration = (DateTime.UtcNow - step3Start).TotalMilliseconds;
_logger.LogDebug("步骤3完成:复制配置值到临时目录,耗时: {Duration}ms", step3Duration.ToString("F2"));
// 4. 获取并验证 IP 端点信息
var step4Start = DateTime.UtcNow;
_logger.LogDebug("步骤4开始:获取并验证 IP 端点信息");
var (endPoints, hasAnyEndPoint) = await _configCopier.GetComAddrInfoAsync(config);
if (!hasAnyEndPoint)
{
var message = "未获取到任何有效的 IP 端点信息";
_logger.LogError(message);
return CellularNetworkOperationResult.Failure(message);
}
var step4Duration = (DateTime.UtcNow - step4Start).TotalMilliseconds;
_logger.LogDebug("步骤4完成:获取并验证 IP 端点信息,耗时: {Duration}ms", step4Duration.ToString("F2"));
// 5. 更新 IP 端点管理器
var step5Start = DateTime.UtcNow;
_logger.LogDebug("步骤5开始:更新 IP 端点管理器");
_context.NetworkIPEndPointManager.UpdateEndPoints(endPoints);
var step5Duration = (DateTime.UtcNow - step5Start).TotalMilliseconds;
_logger.LogDebug("步骤5完成:更新 IP 端点管理器,耗时: {Duration}ms", step5Duration.ToString("F2"));
// 6. 创建协议客户端配置
var step6Start = DateTime.UtcNow;
_logger.LogDebug("步骤6开始:创建协议客户端配置");
var protocolConfigFactory = new ProtocolClientConfigFactory(_loggerFactory.CreateLogger<ProtocolClientConfigFactory>(), _context);
var configCreated = protocolConfigFactory.CreateFromEntities();
if (configCreated)
{
_logger.LogInformation("协议客户端配置创建成功,共创建 {ConfigCount} 个配置", protocolConfigFactory.ConfigCount);
}
else
{
_logger.LogWarning("协议客户端配置创建失败");
return CellularNetworkOperationResult.Failure("协议客户端配置创建失败");
}
var step6Duration = (DateTime.UtcNow - step6Start).TotalMilliseconds;
_logger.LogDebug("步骤6完成:创建协议客户端配置,耗时: {Duration}ms", step6Duration.ToString("F2"));
// 7. 启动 WebSocket 传输连接
var step7Start = DateTime.UtcNow;
_logger.LogDebug("步骤7开始:启动 WebSocket 传输连接");
var webSocketConnected = await StartWebSocketTransportAsync();
if (!webSocketConnected)
{
_logger.LogError("WebSocket 传输连接启动失败,服务端可能未启动");
return CellularNetworkOperationResult.Failure("WebSocket 传输连接启动失败,服务端可能未启动");
}
_logger.LogInformation("WebSocket 传输连接启动成功");
var step7Duration = (DateTime.UtcNow - step7Start).TotalMilliseconds;
_logger.LogDebug("步骤7完成:启动 WebSocket 传输连接,耗时: {Duration}ms", step7Duration.ToString("F2"));
// 8. 启动网络配置
var step8Start = DateTime.UtcNow;
_logger.LogDebug("步骤8开始:启动网络配置");
_logger.LogInformation("正在启动蜂窝网络配置: {ConfigKey}", key);
var enableResult = await _interfaceManager.EnableAsync(config);
if (!enableResult.IsSuccess)
{
var message = $"启动网络配置失败: {enableResult.ErrorMessage}";
_logger.LogError(message);
return CellularNetworkOperationResult.Failure(message);
}
var step8Duration = (DateTime.UtcNow - step8Start).TotalMilliseconds;
_logger.LogDebug("步骤8完成:启动网络配置,耗时: {Duration}ms", step8Duration.ToString("F2"));
// 9. 更新网络配置类型
var step9Start = DateTime.UtcNow;
_logger.LogDebug("步骤9开始:更新网络配置类型");
_context.UpdateNetworkConfigType(enableResult.ConfigType);
_logger.LogInformation("更新网络配置类型: {ConfigType}", enableResult.ConfigType);
var step9Duration = (DateTime.UtcNow - step9Start).TotalMilliseconds;
_logger.LogDebug("步骤9完成:更新网络配置类型,耗时: {Duration}ms", step9Duration.ToString("F2"));
// 10. 检查网络端点连接状态
var step10Start = DateTime.UtcNow;
_logger.LogDebug("步骤10开始:检查网络端点连接状态");
_logger.LogInformation("开始检查所有网络端点的连接状态");
var statusCheckResult = await _statusMonitor.CheckAllEndPointsStatusAsync(endPoints, enableResult.ConfigType, isStartOperation: true);
if (!statusCheckResult.IsSuccess)
{
var errorMessage = string.Join("; ", statusCheckResult.ErrorMessage);
_logger.LogWarning("网络端点状态检查未通过: {ErrorMessage}", errorMessage);
return CellularNetworkOperationResult.Failure($"网络端点状态检查失败: {errorMessage}");
}
_logger.LogInformation("网络端点状态检查完成,所有端点状态正常");
var step10Duration = (DateTime.UtcNow - step10Start).TotalMilliseconds;
_logger.LogDebug("步骤10完成:检查网络端点连接状态,耗时: {Duration}ms", step10Duration.ToString("F2"));
// 11. 更新网络状态
var step11Start = DateTime.UtcNow;
_logger.LogDebug("步骤11开始:更新网络状态");
var state = _context.GetNetworkState();
state.MarkAsStarted();
var step11Duration = (DateTime.UtcNow - step11Start).TotalMilliseconds;
_logger.LogDebug("步骤11完成:更新网络状态,耗时: {Duration}ms", step11Duration.ToString("F2"));
// 12. 启动所有协议客户端
var step12Start = DateTime.UtcNow;
_logger.LogDebug("步骤12开始:启动所有协议客户端");
try
{
var protocolConfigs = protocolConfigFactory.GetAllConfigs();
_protocolWsClientManager.StartAllClients(protocolConfigs);
_logger.LogInformation("所有协议客户端启动完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "启动协议客户端失败");
return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
}
var step12Duration = (DateTime.UtcNow - step12Start).TotalMilliseconds;
_logger.LogDebug("步骤12完成:启动所有协议客户端,耗时: {Duration}ms", step12Duration.ToString("F2"));
var endTime = DateTime.UtcNow;
var duration = endTime - startTime;
_logger.LogInformation("蜂窝网络配置 {ConfigKey} 启动成功,当前状态: {Status},总耗时: {Duration}ms",
key, state.CurrentStatus, duration.TotalMilliseconds.ToString("F2"));
// 删除临时配置文件
var deleteResult = _configCopier.DeleteCellularNetworkConfigurationFile(config);
if (!deleteResult.IsSuccess)
{
_logger.LogWarning("删除临时配置文件失败: {ErrorMessage}", deleteResult.ErrorMessage);
// 不返回失败,因为网络已经启动成功
}
else
{
_logger.LogInformation("临时配置文件删除成功");
}
return CellularNetworkOperationResult.Success(state.CurrentStatus);
}
/// <summary>
/// 启动 WebSocket 传输连接
/// </summary>
/// <returns>连接是否成功</returns>
private async Task<bool> StartWebSocketTransportAsync()
{
try
{
_logger.LogInformation("开始启动 WebSocket 传输连接");
await _webSocketTransport.ConnectAsync();
return _webSocketTransport.IsConnected;
}
catch (Exception ex)
{
_logger.LogError(ex, "WebSocket 传输连接启动失败");
return false;
}
}
/// <summary>
/// 停止 WebSocket 传输连接
/// </summary>
/// <returns>停止是否成功</returns>
private async Task<bool> StopWebSocketTransportAsync()
{
try
{
_logger.LogInformation("开始停止 WebSocket 传输连接");
await _webSocketTransport.CloseAsync();
return !_webSocketTransport.IsConnected;
}
catch (Exception ex)
{
_logger.LogError(ex, "WebSocket 传输连接停止失败");
return false;
}
}
}
}

339
CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs

@ -1,9 +1,11 @@
using System.Collections.Generic;
using CoreAgent.Domain.Entities;
using CoreAgent.Domain.Interfaces;
using CoreAgent.Domain.Interfaces.Network;
using CoreAgent.Domain.Interfaces.System.Command;
using CoreAgent.Domain.Models.Network;
using CoreAgent.Domain.Models.System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace CoreAgent.Infrastructure.Services.Network;
@ -25,11 +27,345 @@ public class NetworkConfigCopier : INetworkConfigCopier
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_commandExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor));
}
public async Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)> CreateCellularNetworkConfigurationFile(CellularNetworkConfiguration cellular, AppSettings appSettings)
{
_logger.LogInformation("开始创建蜂窝网络配置文件,RuntimeCode: {RuntimeCode}", cellular?.RuntimeCode);
try
{
// 参数验证
if (cellular == null)
{
_logger.LogError("CellularNetworkConfiguration 参数为空");
return (false, "CellularNetworkConfiguration 参数为空", null);
}
if (appSettings == null)
{
_logger.LogError("AppSettings 参数为空");
return (false, "AppSettings 参数为空", null);
}
if (string.IsNullOrWhiteSpace(cellular.RuntimeCode))
{
_logger.LogError("RuntimeCode 为空");
return (false, "RuntimeCode 不能为空", null);
}
// 确保目录存在
EnsureDirectoriesExist(appSettings);
string ranConfigPath = Path.Combine(appSettings.RanConfigDirectory, "RAN", $"{cellular.RuntimeCode}.cfg");
_logger.LogInformation("RAN配置文件路径: {RanConfigPath}", ranConfigPath);
List<CoreImsConfig> list = new List<CoreImsConfig>();
// 创建RAN配置文件
if (!string.IsNullOrWhiteSpace(cellular.RadioAccessNetworkConfiguration))
{
_logger.LogInformation("开始创建RAN配置文件");
var ranResult = await CreateRadioAccessNetworkConfigurationFile(ranConfigPath, cellular.RadioAccessNetworkConfiguration);
if (!ranResult.IsSuccess)
{
return (false, $"创建RAN配置文件失败: {ranResult.ErrorMessage}", null);
}
_logger.LogInformation("RAN配置文件创建成功: {RanConfigPath}", ranConfigPath);
}
else
{
_logger.LogWarning("RadioAccessNetworkConfiguration 为空,跳过RAN配置文件创建");
}
// 创建核心网络和IMS配置文件
if (cellular.CoreNetworkImsConfigurations != null && cellular.CoreNetworkImsConfigurations.Count > 0)
{
_logger.LogInformation("开始创建核心网络和IMS配置文件,配置数量: {Count}", cellular.CoreNetworkImsConfigurations.Count);
var (coreImsSuccess, coreImsError, coreImsConfigs) = await CreateCoreNetworkImsConfigurationFiles(cellular.RuntimeCode, cellular.CoreNetworkImsConfigurations, appSettings);
if (!coreImsSuccess)
{
return (false, $"创建核心网络和IMS配置文件失败: {coreImsError}", null);
}
list = coreImsConfigs;
_logger.LogInformation("核心网络和IMS配置文件创建完成,成功创建: {Count} 个配置", list.Count);
}
else
{
_logger.LogWarning("CoreNetworkImsConfigurations 为空或数量为0,跳过核心网络和IMS配置文件创建");
}
NetworkConfiguration network = NetworkConfiguration.Create(cellular.RuntimeCode, ranConfigPath, list);
_logger.LogInformation("蜂窝网络配置文件创建完成,RuntimeCode: {RuntimeCode}", cellular.RuntimeCode);
return (true, null, network);
}
catch (Exception ex)
{
_logger.LogError(ex, "创建蜂窝网络配置文件时发生错误,RuntimeCode: {RuntimeCode}", cellular?.RuntimeCode);
return (false, $"创建蜂窝网络配置文件时发生错误: {ex.Message}", null);
}
}
public async Task<NetworkConfigCopyResult> CreateRadioAccessNetworkConfigurationFile(string ranConfigPath, string radioAccessNetworkConfiguration)
{
_logger.LogInformation("开始创建RAN配置文件: {RanConfigPath}", ranConfigPath);
try
{
// 参数验证
if (string.IsNullOrWhiteSpace(ranConfigPath))
{
_logger.LogError("RAN配置文件路径为空");
return NetworkConfigCopyResult.Failure("RAN配置文件路径不能为空");
}
if (string.IsNullOrWhiteSpace(radioAccessNetworkConfiguration))
{
_logger.LogError("RAN配置内容为空");
return NetworkConfigCopyResult.Failure("RAN配置内容不能为空");
}
// 确保目录存在
var directory = Path.GetDirectoryName(ranConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
_logger.LogInformation("创建目录: {Directory}", directory);
}
// 写入配置文件
await File.WriteAllTextAsync(ranConfigPath, radioAccessNetworkConfiguration);
_logger.LogInformation("RAN配置文件创建成功: {RanConfigPath}", ranConfigPath);
return NetworkConfigCopyResult.Success();
}
catch (Exception ex)
{
_logger.LogError(ex, "创建RAN配置文件时发生错误: {RanConfigPath}", ranConfigPath);
return NetworkConfigCopyResult.Failure($"创建RAN配置文件时发生错误: {ex.Message}");
}
}
public async Task<(bool IsSuccess, string ErrorMessage, List<CoreImsConfig> CoreImsConfigs)> CreateCoreNetworkImsConfigurationFiles(string runtimeCode, List<CoreNetworkImsConfiguration> coreNetworkImsConfigurations, AppSettings appSettings)
{
_logger.LogInformation("开始创建核心网络和IMS配置文件,RuntimeCode: {RuntimeCode}, 配置数量: {Count}", runtimeCode, coreNetworkImsConfigurations?.Count ?? 0);
try
{
// 参数验证
if (string.IsNullOrWhiteSpace(runtimeCode))
{
_logger.LogError("RuntimeCode 为空");
return (false, "RuntimeCode 不能为空", null);
}
if (coreNetworkImsConfigurations == null || coreNetworkImsConfigurations.Count == 0)
{
_logger.LogWarning("CoreNetworkImsConfigurations 为空或数量为0");
return (true, null, new List<CoreImsConfig>());
}
if (appSettings == null)
{
_logger.LogError("AppSettings 参数为空");
return (false, "AppSettings 参数为空", null);
}
List<CoreImsConfig> list = new List<CoreImsConfig>();
foreach (var item in coreNetworkImsConfigurations)
{
try
{
_logger.LogInformation("处理配置项,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
// 验证配置项
if (string.IsNullOrWhiteSpace(item.Plmn))
{
_logger.LogError("配置项PLMN为空,Index: {Index}", item.Index);
continue;
}
if (string.IsNullOrWhiteSpace(item.CoreNetworkConfiguration))
{
_logger.LogWarning("核心网络配置内容为空,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
}
if (string.IsNullOrWhiteSpace(item.ImsServiceConfiguration))
{
_logger.LogWarning("IMS服务配置内容为空,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
}
// 构建文件路径
string cnConfigPath = Path.Combine(appSettings.MmeConfigDirectory, "Cn", $"{runtimeCode}_{item.Plmn}.cfg");
string imsConfigPath = Path.Combine(appSettings.MmeConfigDirectory, "Ims", $"{runtimeCode}_{item.Plmn}.cfg");
_logger.LogInformation("CN配置文件路径: {CnConfigPath}", cnConfigPath);
_logger.LogInformation("IMS配置文件路径: {ImsConfigPath}", imsConfigPath);
// 确保目录存在
var cnDirectory = Path.GetDirectoryName(cnConfigPath);
var imsDirectory = Path.GetDirectoryName(imsConfigPath);
if (!string.IsNullOrEmpty(cnDirectory) && !Directory.Exists(cnDirectory))
{
Directory.CreateDirectory(cnDirectory);
_logger.LogInformation("创建CN目录: {Directory}", cnDirectory);
}
if (!string.IsNullOrEmpty(imsDirectory) && !Directory.Exists(imsDirectory))
{
Directory.CreateDirectory(imsDirectory);
_logger.LogInformation("创建IMS目录: {Directory}", imsDirectory);
}
// 写入配置文件
if (!string.IsNullOrWhiteSpace(item.CoreNetworkConfiguration))
{
await File.WriteAllTextAsync(cnConfigPath, item.CoreNetworkConfiguration);
_logger.LogInformation("CN配置文件创建成功: {CnConfigPath}", cnConfigPath);
}
if (!string.IsNullOrWhiteSpace(item.ImsServiceConfiguration))
{
await File.WriteAllTextAsync(imsConfigPath, item.ImsServiceConfiguration);
_logger.LogInformation("IMS配置文件创建成功: {ImsConfigPath}", imsConfigPath);
}
// 添加到结果列表
list.Add(new CoreImsConfig
{
Index = item.Index,
Plmn = item.Plmn,
CoreNetworkConfig = cnConfigPath,
ImsConfig = imsConfigPath
});
_logger.LogInformation("配置项处理完成,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
}
catch (Exception ex)
{
_logger.LogError(ex, "处理配置项时发生错误,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
// 继续处理下一个配置项,不中断整个流程
}
}
_logger.LogInformation("核心网络和IMS配置文件创建完成,成功创建: {Count} 个配置", list.Count);
return (true, null, list);
}
catch (Exception ex)
{
_logger.LogError(ex, "创建核心网络和IMS配置文件时发生错误,RuntimeCode: {RuntimeCode}", runtimeCode);
return (false, $"创建核心网络和IMS配置文件时发生错误: {ex.Message}", null);
}
}
public NetworkConfigCopyResult DeleteCellularNetworkConfigurationFile(NetworkConfiguration configuration)
{
_logger.LogInformation("开始删除蜂窝网络配置文件");
try
{
// 参数验证
if (configuration == null)
{
_logger.LogError("NetworkConfiguration 参数为空");
return NetworkConfigCopyResult.Failure("NetworkConfiguration 参数为空");
}
int deletedFilesCount = 0;
// 删除RAG配置文件
if (!string.IsNullOrWhiteSpace(configuration.RagConfig))
{
try
{
if (File.Exists(configuration.RagConfig))
{
File.Delete(configuration.RagConfig);
_logger.LogInformation("RAG配置文件删除成功: {RagConfig}", configuration.RagConfig);
deletedFilesCount++;
}
else
{
_logger.LogWarning("RAG配置文件不存在: {RagConfig}", configuration.RagConfig);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "删除RAG配置文件时发生错误: {RagConfig}", configuration.RagConfig);
// 继续处理其他文件,不中断整个流程
}
}
else
{
_logger.LogInformation("RAG配置文件路径为空,跳过删除");
}
// 删除核心网络和IMS配置文件
if (configuration.CoreOrImsConfigs != null && configuration.CoreOrImsConfigs.Count > 0)
{
_logger.LogInformation("开始删除核心网络和IMS配置文件,配置数量: {Count}", configuration.CoreOrImsConfigs.Count);
foreach (var item in configuration.CoreOrImsConfigs)
{
try
{
// 删除CN配置文件
if (!string.IsNullOrWhiteSpace(item.CoreNetworkConfig))
{
if (File.Exists(item.CoreNetworkConfig))
{
File.Delete(item.CoreNetworkConfig);
_logger.LogInformation("CN配置文件删除成功: {CoreNetworkConfig}", item.CoreNetworkConfig);
deletedFilesCount++;
}
else
{
_logger.LogWarning("CN配置文件不存在: {CoreNetworkConfig}", item.CoreNetworkConfig);
}
}
// 删除IMS配置文件
if (!string.IsNullOrWhiteSpace(item.ImsConfig))
{
if (File.Exists(item.ImsConfig))
{
File.Delete(item.ImsConfig);
_logger.LogInformation("IMS配置文件删除成功: {ImsConfig}", item.ImsConfig);
deletedFilesCount++;
}
else
{
_logger.LogWarning("IMS配置文件不存在: {ImsConfig}", item.ImsConfig);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "删除配置文件时发生错误,Index: {Index}, Plmn: {Plmn}", item.Index, item.Plmn);
// 继续处理下一个配置项,不中断整个流程
}
}
}
else
{
_logger.LogInformation("核心网络和IMS配置文件列表为空,跳过删除");
}
_logger.LogInformation("蜂窝网络配置文件删除完成,共删除 {DeletedFilesCount} 个文件", deletedFilesCount);
return NetworkConfigCopyResult.Success();
}
catch (Exception ex)
{
_logger.LogError(ex, "删除蜂窝网络配置文件时发生错误");
return NetworkConfigCopyResult.Failure($"删除蜂窝网络配置文件时发生错误: {ex.Message}");
}
}
/// <summary>
/// 将配置值复制到临时目录并更新配置路径
/// </summary>
public async Task<NetworkConfigCopyResult> CopyConfigValuesToTempAsync(NetworkConfiguration networkConfig, AppSettings appSettings)
public NetworkConfigCopyResult CopyConfigValuesToTempAsync(NetworkConfiguration networkConfig, AppSettings appSettings)
{
try
{
@ -67,7 +403,6 @@ public class NetworkConfigCopier : INetworkConfigCopier
}
}
}
return NetworkConfigCopyResult.Success();
}
catch (Exception ex)

615
modify.md

@ -2,108 +2,164 @@
## 2024年修改记录
### 创建获取SN接口 - 遵循DDD设计架构
### 创建蜂窝网络配置实体类
**修改时间**: 2024年
**修改文件**:
- `CoreAgent.Domain/Models/System/DeviceInfo.cs` (新建)
- `CoreAgent.Domain/Interfaces/System/IDeviceService.cs` (新建)
- `CoreAgent.Infrastructure/Services/System/DeviceService.cs` (新建)
- `CoreAgent.Application/Commands/System/GetSerialNumberCommand.cs` (新建)
- `CoreAgent.Application/Handlers/System/GetSerialNumberCommandHandler.cs` (新建)
- `CoreAgent.API/Controllers/SystemController.cs` (新建)
- `CoreAgent.Infrastructure/Extensions/ServiceCollection/CommandServiceExtensions.cs`
- `CoreAgent.Domain/Models/Network/CellularNetworkConfiguration.cs` (新建)
**修改内容**:
1. **领域层设计**
- 创建 `DeviceInfo` 模型,包含 `SerialNumber``Timestamp` 字段
- 定义 `IDeviceService` 接口,提供获取SN的异步和同步方法
- 遵循DDD设计原则,接口定义在领域层
2. **基础设施层实现**
- 实现 `DeviceService` 类,使用 `ISystemCommandExecutor` 执行系统命令
- 支持Windows和Linux系统,自动选择对应的命令
- Windows: `wmic bios get serialnumber /value`
- Linux: `cat /sys/class/dmi/id/product_serial`
- 提供完善的错误处理和日志记录
3. **应用层设计**
- 创建 `GetSerialNumberCommand` 命令类,返回 `ApiActionResult<DeviceInfo>`
- 实现 `GetSerialNumberCommandHandler` 处理器,返回包装后的结果
- 使用MediatR模式处理命令
- 在应用层统一处理成功和错误响应
4. **API层设计**
- 创建 `SystemController` 控制器,继承 `BaseApiController`
- 提供 `GET /api/v1/system/serial-number` 接口
- 直接返回应用层的 `ApiActionResult<DeviceInfo>` 结果
- 简化控制器逻辑,错误处理在应用层完成
5. **依赖注入配置**
- 在 `CommandServiceExtensions` 中注册 `IDeviceService`
- 使用作用域生命周期管理
6. **具体实现**:
1. **创建CellularNetworkConfiguration实体类**
- 包含设备代码(DeviceCode)字符串属性
- 包含运行时代码(RuntimeCode)字符串属性
- 包含无线接入网配置(RadioAccessNetworkConfiguration)字符串属性
- 包含核心网IMS配置集合(List<CoreNetworkImsConfiguration>)属性
2. **创建CoreNetworkImsConfiguration实体类**
- 包含索引(Index)整数属性
- 包含PLMN标识(Plmn)字符串属性
- 包含核心网配置(CoreNetworkConfiguration)字符串属性
- 包含IMS服务配置(ImsServiceConfiguration)字符串属性
3. **具体实现**:
```csharp
// 领域模型
public class DeviceInfo
/// <summary>
/// 蜂窝网络配置实体
/// </summary>
public class CellularNetworkConfiguration
{
public string SerialNumber { get; set; } = string.Empty;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
/// <summary>
/// 设备代码
/// </summary>
public string DeviceCode { get; set; }
/// <summary>
/// 运行时代码
/// </summary>
public string RuntimeCode { get; set; }
/// <summary>
/// 无线接入网配置
/// </summary>
public string RadioAccessNetworkConfiguration { get; set; }
/// <summary>
/// 核心网IMS配置集合
/// </summary>
public List<CoreNetworkImsConfiguration> CoreNetworkImsConfigurations { get; set; } = new List<CoreNetworkImsConfiguration>();
}
// 领域接口
public interface IDeviceService
/// <summary>
/// 核心网IMS配置对象
/// </summary>
public class CoreNetworkImsConfiguration
{
Task<DeviceInfo> GetSerialNumberAsync();
DeviceInfo GetSerialNumber();
/// <summary>
/// 索引
/// </summary>
public int Index { get; set; }
/// <summary>
/// PLMN标识
/// </summary>
public string Plmn { get; set; }
/// <summary>
/// 核心网配置
/// </summary>
public string CoreNetworkConfiguration { get; set; }
/// <summary>
/// IMS服务配置
/// </summary>
public string ImsServiceConfiguration { get; set; }
}
```
// 基础设施实现
public class DeviceService : IDeviceService
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()
{
private readonly ISystemCommandExecutor _commandExecutor;
public async Task<DeviceInfo> GetSerialNumberAsync()
// 验证必填字段
if (string.IsNullOrWhiteSpace(DeviceCode))
{
string command = GetSerialNumberCommand();
var result = await _commandExecutor.ExecuteCommandAsync(command, new CancellationTokenSource(), 5000);
return ParseSerialNumber(result.Output);
return new ValidationResult("设备代码不能为空");
}
}
// 应用层处理器
public async Task<ApiActionResult<DeviceInfo>> Handle(GetSerialNumberCommand request, CancellationToken cancellationToken)
{
var deviceInfo = await _deviceService.GetSerialNumberAsync();
return ApiActionResult<DeviceInfo>.Ok(deviceInfo, "获取设备序列号成功");
}
// 验证无线接入网配置和核心网IMS配置至少有一个有数据
var hasRadioAccessConfig = !string.IsNullOrWhiteSpace(RadioAccessNetworkConfiguration);
var hasCoreNetworkConfigs = CoreNetworkImsConfigurations?.Any() == true;
// API控制器
[HttpGet("serial-number")]
public async Task<IActionResult> GetSerialNumber()
{
var command = new GetSerialNumberCommand();
var result = await _mediator.Send(command);
return result;
if (!hasRadioAccessConfig && !hasCoreNetworkConfigs)
{
return new ValidationResult("无线接入网配置和核心网IMS配置至少需要配置其中一项");
}
return ValidationResult.Success;
}
```
7. **设计优势**:
- **DDD架构**: 严格遵循领域驱动设计的分层架构
- **依赖倒置**: 高层模块依赖抽象,不依赖具体实现
- **单一职责**: 每个类都有明确的职责
- **跨平台支持**: 自动识别操作系统并使用对应命令
- **错误处理**: 完善的异常处理和日志记录
- **可测试性**: 接口抽象便于单元测试
- **可扩展性**: 易于添加新的设备信息获取功能
5. **设计优势**:
- **数据完整性**:确保必填字段不为空
- **业务规则校验**:验证业务逻辑的正确性
- **统一验证接口**:提供一致的验证方法
- **详细错误信息**:提供具体的错误描述
- **分层校验**:支持嵌套对象的校验
- **易于扩展**:可以轻松添加新的校验规则
**影响范围**:
- 新增系统服务模块
- 扩展API接口
- 依赖注入配置更新
- 命令处理流程扩展
- 蜂窝网络配置数据模型定义
- 核心网IMS配置管理
- 数据实体结构标准化
- 领域模型完整性
- 代码可读性和维护性提升
- 数据校验和业务规则验证
- 错误处理和用户反馈
### 创建MessageTransferProtocolLog模型解决命名冲突
@ -851,136 +907,333 @@
- 中间件的实例化策略
- 性能和内存使用优化
### DeviceInfo模型添加IsSuccess字段和ParseSerialNumber方法优化
### NetworkConfigCopier方法Bug修复和日志增强
**修改时间**: 2024年
**修改文件**:
- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
- `CoreAgent.Domain/Models/Network/NetworkConfigCopyResult.cs`
- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
**修改内容**:
1. **返回类型统一化**
- **统一返回类型**:所有方法都返回 `NetworkConfigCopyResult` 类型,不再抛出异常
- **泛型结果类型**:创建 `NetworkConfigCopyResult<T>` 泛型类,支持返回数据
- **接口更新**:更新 `INetworkConfigCopier` 接口以匹配新的返回类型
- **错误处理改进**:使用结果对象而不是异常来处理错误情况
2. **CreateCellularNetworkConfigurationFile方法修复**
- **返回类型修改**:从 `Task<NetworkConfiguration>` 改为 `Task<NetworkConfigCopyResult>`
- **路径构建Bug修复**:修复 `Path.Combine` 使用不当的问题,正确构建文件路径
- **参数验证增强**:添加完整的参数空值检查,返回失败结果而不是抛出异常
- **目录创建**:确保目标目录存在,避免文件写入失败
- **详细日志**:添加每个步骤的详细日志记录,包括开始、成功、警告和错误信息
3. **CreateRadioAccessNetworkConfigurationFile方法修复**
- **返回类型修改**:从 `Task<bool>` 改为 `Task<NetworkConfigCopyResult>`
- **参数命名规范化**:使用小写开头的参数名,符合C#命名约定
- **目录创建逻辑**:添加目录存在性检查和自动创建
- **错误处理增强**:返回失败结果而不是抛出异常
- **参数验证**:验证文件路径和配置内容不为空
4. **CreateCoreNetworkImsConfigurationFiles方法重构**
- **返回类型修改**:从 `Task<List<CoreImsConfig>>` 改为 `Task<NetworkConfigCopyResult<List<CoreImsConfig>>>`
- **方法重命名**:避免与RAN配置文件创建方法名冲突
- **参数验证完善**:验证RuntimeCode、配置列表和AppSettings
- **配置项验证**:验证每个配置项的PLMN和配置内容
- **目录创建**:为CN和IMS配置文件分别创建目录
- **错误隔离**:单个配置项失败不影响其他配置项处理
- **详细日志**:记录每个配置项的处理过程和结果
5. **DeleteCellularNetworkConfigurationFile方法增强**
- **返回类型修改**:从 `bool` 改为 `NetworkConfigCopyResult`
- **文件存在性检查**:删除前检查文件是否存在,避免异常
- **错误隔离**:单个文件删除失败不影响其他文件删除
- **删除统计**:统计成功删除的文件数量
- **详细日志**:记录每个文件的删除状态和结果
- **参数验证**:验证NetworkConfiguration参数不为空
6. **NetworkConfigCopyResult类扩展**
- **泛型支持**:添加 `NetworkConfigCopyResult<T>` 泛型类
- **数据返回**:支持返回操作结果的同时返回数据
- **继承关系**:泛型类继承自基础类,保持类型安全
- **静态工厂方法**:提供 `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<NetworkConfiguration> CreateCellularNetworkConfigurationFile(...)
// 修复后 - 返回结果类型
public async Task<NetworkConfigCopyResult> CreateCellularNetworkConfigurationFile(...)
```
8. **设计优势**:
- **统一错误处理**:所有方法都使用结果对象,避免异常传播
- **类型安全**:泛型结果类型确保类型安全
- **Bug修复**:修复路径构建、方法名冲突、文件删除等关键Bug
- **错误处理完善**:添加完整的错误处理和错误隔离机制
- **日志详细**:提供完整的操作跟踪和调试信息
- **参数验证**:确保所有输入参数的有效性
- **目录管理**:自动创建必要的目录结构
- **错误隔离**:单个操作失败不影响整体流程
- **命名规范**:遵循C#命名约定,提高代码可读性
- **方法职责清晰**:每个方法都有明确的职责和边界
9. **修复的关键问题**:
- **异常处理不一致**:统一使用结果对象而不是异常
- **路径构建错误**:`Path.Combine` 使用不当导致路径错误
- **方法名冲突**:两个不同功能的方法使用相同名称
- **文件删除异常**:删除不存在的文件导致异常
- **目录不存在**:目标目录不存在导致文件写入失败
- **错误传播**:单个错误导致整个操作失败
- **日志缺失**:缺少关键操作的日志记录
**影响范围**:
- 网络配置文件创建和删除的稳定性
- 错误处理和异常预防
- 日志记录和调试能力
- 代码可读性和维护性
- 文件系统操作的可靠性
- 配置管理的完整性
- 接口契约的一致性
- 调用方代码的错误处理方式
### NetworkConfigCopier返回类型优化和GeneralCellularNetworkService适配
**修改时间**: 2024年
**修改文件**:
- `CoreAgent.Domain/Models/System/DeviceInfo.cs`
- `CoreAgent.Domain/Models/System/DeviceInfoResponse.cs` (新建)
- `CoreAgent.Infrastructure/Services/System/DeviceService.cs`
- `CoreAgent.Application/Commands/System/GetSerialNumberCommand.cs`
- `CoreAgent.Application/Handlers/System/GetSerialNumberCommandHandler.cs`
- `CoreAgent.Infrastructure/Services/Network/NetworkConfigCopier.cs`
- `CoreAgent.Domain/Interfaces/Network/INetworkConfigCopier.cs`
- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
**修改内容**:
1. **DeviceInfo模型添加IsSuccess字段**
- 添加 `bool IsSuccess { get; set; }` 属性
- 用于标识是否成功获取到序列号
- 提供更准确的成功/失败状态判断
2. **创建DeviceInfoResponse模型**
- 创建专门用于API响应的模型
- 只包含 `SerialNumber``Timestamp` 字段
- 不包含内部状态字段 `IsSuccess`
- 确保API响应简洁明了
2. **ParseSerialNumber方法优化**
- 返回类型从 `string` 改为 `string?`(可空字符串)
- 失败时返回 `null` 而不是固定字符串
- 修复Linux系统输出格式解析:`Serial Number: xxxxxxxx`
- 添加更详细的注释说明返回值含义
3. **DeviceService方法更新**
- 修改异步和同步方法中的DeviceInfo创建逻辑
- 根据ParseSerialNumber返回值设置IsSuccess字段
- 成功时设置 `IsSuccess = true`
- 失败时设置 `IsSuccess = false`
- 添加更详细的日志记录
4. **GetSerialNumberCommand和GetSerialNumberCommandHandler优化**
- 修改命令返回类型为 `ApiActionResult<DeviceInfoResponse>`
- 在Handle方法中检查 `deviceInfo.IsSuccess` 字段
- 成功时创建 `DeviceInfoResponse` 对象返回,不包含IsSuccess字段
- 失败时返回 `ApiActionResult<DeviceInfoResponse>.Error()` 并设置错误代码 `DEVICE_SN_NOT_FOUND`
- 提供更准确的错误信息
- 确保API返回的响应模型不包含内部状态字段
1. **CreateCellularNetworkConfigurationFile返回类型优化**
- **返回类型修改**:从 `Task<NetworkConfigCopyResult>` 改为 `Task<NetworkConfigCopyResult<NetworkConfiguration>>`
- **数据返回**:成功时返回包含 `NetworkConfiguration` 对象的结果
- **接口更新**:更新接口定义以匹配新的返回类型
- **调用方适配**:修改调用方代码以正确使用返回的数据
5. **具体实现**:
2. **GeneralCellularNetworkService调用修复**
- **返回类型适配**:修改调用方式以适配新的泛型返回类型
- **数据获取**:直接从结果对象的 `Data` 属性获取 `NetworkConfiguration`
- **错误处理改进**:使用 `IsSuccess` 属性检查操作是否成功
- **代码简化**:移除手动创建 `NetworkConfiguration` 的代码
3. **接口定义更新**
- **泛型支持**:接口方法返回 `Task<NetworkConfigCopyResult<NetworkConfiguration>>`
- **文档更新**:更新XML文档注释说明返回的数据类型
4. **具体修改**:
```csharp
// NetworkConfigCopier.cs - 返回类型修改
// 修复前
public async Task<NetworkConfigCopyResult> CreateCellularNetworkConfigurationFile(...)
{
// ... 创建NetworkConfiguration对象
return NetworkConfigCopyResult.Success(); // 没有返回数据
}
// 修复后
public async Task<NetworkConfigCopyResult<NetworkConfiguration>> CreateCellularNetworkConfigurationFile(...)
{
// ... 创建NetworkConfiguration对象
return NetworkConfigCopyResult<NetworkConfiguration>.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<CoreImsConfig>());
// 修复后 - 直接使用返回的数据
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<NetworkConfigCopyResult<NetworkConfiguration>>` 改为元组 `Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)>`
- 简化返回结构,直接返回三个值:是否成功、错误信息、网络配置对象
2. **更新接口定义**
- 更新 `INetworkConfigCopier` 接口中的方法签名
- 更新XML文档注释说明新的返回类型
3. **更新调用代码**
- 修改 `GeneralCellularNetworkService.cs` 中的调用逻辑
- 使用元组解构语法 `var (isSuccess, errorMessage, config) = await ...`
- 直接使用解构后的变量
4. **具体实现**:
```csharp
// DeviceInfo模型(内部使用)
public class DeviceInfo
// 修改前
public async Task<NetworkConfigCopyResult<NetworkConfiguration>> CreateCellularNetworkConfigurationFile(...)
{
public bool IsSuccess { get; set; }
public string SerialNumber { get; set; } = string.Empty;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
return NetworkConfigCopyResult<NetworkConfiguration>.Failure("错误信息");
return NetworkConfigCopyResult<NetworkConfiguration>.Success(network);
}
// DeviceInfoResponse模型(API响应)
public class DeviceInfoResponse
// 修改后
public async Task<(bool IsSuccess, string ErrorMessage, NetworkConfiguration NetworkConfiguration)> CreateCellularNetworkConfigurationFile(...)
{
public string SerialNumber { get; set; } = string.Empty;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
return (false, "错误信息", null);
return (true, null, network);
}
```
// ParseSerialNumber方法
private string? ParseSerialNumber(string output)
5. **调用代码修改**:
```csharp
// 修改前
var createResult = await _configCopier.CreateCellularNetworkConfigurationFile(cellular, _context.GetAppSettings());
if (!createResult.IsSuccess)
{
if (string.IsNullOrWhiteSpace(output))
return null;
return CellularNetworkOperationResult.Failure($"创建网络配置文件失败: {createResult.ErrorMessage}");
}
var config = createResult.Data;
foreach (var line in lines)
{
if (OperatingSystem.IsLinux())
{
// Linux输出格式: Serial Number: xxxxxxxx
if (trimmedLine.StartsWith("Serial Number:", StringComparison.OrdinalIgnoreCase))
{
var serialNumber = trimmedLine.Substring("Serial Number:".Length).Trim();
return string.IsNullOrWhiteSpace(serialNumber) ? null : serialNumber;
}
}
}
return null;
// 修改后
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<NetworkConfigCopyResult<List<CoreImsConfig>>>` 改为元组 `Task<(bool IsSuccess, string ErrorMessage, List<CoreImsConfig> CoreImsConfigs)>`
- 简化返回结构,直接返回三个值:是否成功、错误信息、核心IMS配置列表
2. **更新调用代码**
- 修改 `CreateCellularNetworkConfigurationFile` 方法中对 `CreateCoreNetworkImsConfigurationFiles` 的调用
- 使用元组解构语法 `var (coreImsSuccess, coreImsError, coreImsConfigs) = await ...`
- 直接使用解构后的变量
// DeviceService中的使用
if (serialNumber != null)
3. **具体实现**:
```csharp
// 修改前
public async Task<NetworkConfigCopyResult<List<CoreImsConfig>>> CreateCoreNetworkImsConfigurationFiles(...)
{
return new DeviceInfo
{
IsSuccess = true,
SerialNumber = serialNumber,
Timestamp = DateTime.UtcNow
};
return NetworkConfigCopyResult<List<CoreImsConfig>>.Failure("错误信息");
return NetworkConfigCopyResult<List<CoreImsConfig>>.Success(list);
}
else
// 修改后
public async Task<(bool IsSuccess, string ErrorMessage, List<CoreImsConfig> CoreImsConfigs)> CreateCoreNetworkImsConfigurationFiles(...)
{
return new DeviceInfo
{
IsSuccess = false,
SerialNumber = "UNKNOWN",
Timestamp = DateTime.UtcNow
};
return (false, "错误信息", null);
return (true, null, list);
}
```
// GetSerialNumberCommandHandler中的使用
if (deviceInfo.IsSuccess)
4. **调用代码修改**:
```csharp
// 修改前
var coreImsResult = await CreateCoreNetworkImsConfigurationFiles(cellular.RuntimeCode, cellular.CoreNetworkImsConfigurations, appSettings);
if (!coreImsResult.IsSuccess)
{
// 创建DeviceInfoResponse对象,不包含IsSuccess字段
var resultDeviceInfo = new DeviceInfoResponse
{
SerialNumber = deviceInfo.SerialNumber,
Timestamp = deviceInfo.Timestamp
};
return ApiActionResult<DeviceInfoResponse>.Ok(resultDeviceInfo, "获取设备序列号成功");
return (false, $"创建核心网络和IMS配置文件失败: {coreImsResult.ErrorMessage}", null);
}
else
list = coreImsResult.Data;
// 修改后
var (coreImsSuccess, coreImsError, coreImsConfigs) = await CreateCoreNetworkImsConfigurationFiles(cellular.RuntimeCode, cellular.CoreNetworkImsConfigurations, appSettings);
if (!coreImsSuccess)
{
return ApiActionResult<DeviceInfoResponse>.Error("获取设备序列号失败", "DEVICE_SN_NOT_FOUND");
return (false, $"创建核心网络和IMS配置文件失败: {coreImsError}", null);
}
list = coreImsConfigs;
```
6. **设计优势**:
- **准确的状态判断**:通过IsSuccess字段明确标识操作结果
- **更好的错误处理**:区分不同类型的失败情况
- **Linux兼容性**:正确解析Linux系统的序列号输出格式
- **空值安全**:使用可空字符串避免固定错误字符串
- **详细日志**:提供更丰富的日志信息便于调试
- **API一致性**:返回统一的ApiActionResult格式
5. **设计优势**:
- **一致性**:与 `CreateCellularNetworkConfigurationFile` 方法保持相同的返回模式
- **简洁性**:元组返回比包装类更简洁
- **直观性**:直接返回三个值,语义更清晰
- **性能**:避免创建额外的包装对象
- **易用性**:使用元组解构语法,代码更简洁
6. **接口说明**:
- `CreateCoreNetworkImsConfigurationFiles` 是内部方法,不在接口中定义
- 只在 `NetworkConfigCopier` 实现类中使用
- 保持接口的简洁性
**影响范围**:
- 设备序列号获取的准确性
- API响应的错误处理
- Linux系统兼容性
- 日志记录的详细程度
- 客户端错误处理逻辑
- 核心网络和IMS配置文件创建方法的返回类型简化
- 内部方法调用代码的简化
- 代码一致性的提升
- 性能的优化
Loading…
Cancel
Save