using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
using CellularManagement.Domain.Repositories.NetworkProfile;
using X1.Domain.Models;
using System.Collections.Concurrent;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
///
/// 启动设备运行时状态命令处理器
///
public class StartDeviceRuntimeCommandHandler : IRequestHandler>
{
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ICellularDeviceRepository _deviceRepository;
private readonly ILogger _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ICurrentUserService _currentUserService;
private readonly IInstrumentProtocolClient _protocolClient;
private readonly INetworkStackConfigRepository _networkStackConfigRepository;
private readonly ICellularDeviceRuntimeDetailRepository _detailRepository;
///
/// 初始化命令处理器
///
public StartDeviceRuntimeCommandHandler(
INetworkStackConfigRepository networkStackConfigRepository,
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ICellularDeviceRepository deviceRepository,
ILogger logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService,
IInstrumentProtocolClient protocolClient,
ICellularDeviceRuntimeDetailRepository detailRepository)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_deviceRepository = deviceRepository;
_logger = logger;
_unitOfWork = unitOfWork;
_currentUserService = currentUserService;
_protocolClient = protocolClient;
_networkStackConfigRepository = networkStackConfigRepository;
_detailRepository = detailRepository;
}
///
/// 处理启动设备运行时状态命令
///
public async Task> Handle(StartDeviceRuntimeCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始批量启动设备运行时状态,设备数量: {DeviceCount}", request?.DeviceRequests?.Count ?? 0);
// 验证命令参数
if (!request.IsValid())
{
_logger.LogWarning("命令参数验证失败");
return OperationResult.CreateFailure("命令参数验证失败");
}
// 获取当前用户
var currentUser = _currentUserService.GetCurrentUserId();
if (string.IsNullOrEmpty(currentUser))
{
_logger.LogWarning("当前用户未登录");
return OperationResult.CreateFailure("用户未登录");
}
_logger.LogDebug("开始获取网络堆栈配置,网络堆栈代码: {NetworkStackCodes}",
string.Join(", ", request.DeviceRequests.Select(r => r.NetworkStackCode)));
// 获取网络堆栈配置
var networkConfigs = await _networkStackConfigRepository.GetNetworkStackConfigsByCodesAsync(
request.DeviceRequests.Select(s => s.NetworkStackCode).ToArray(), cancellationToken);
if (networkConfigs == null || !networkConfigs.Any())
{
_logger.LogWarning("未找到指定的网络堆栈配置");
return OperationResult.CreateFailure("未找到指定的网络堆栈配置");
}
_logger.LogDebug("成功获取网络堆栈配置,配置数量: {ConfigCount}", networkConfigs.Count());
// 生成运行时编码
var runtimeCode = await GenerateRuntimeCodeAsync(cancellationToken);
if (string.IsNullOrEmpty(runtimeCode))
{
_logger.LogError("生成运行时编码失败");
return OperationResult.CreateFailure("生成运行时编码失败");
}
_logger.LogInformation("生成运行时编码: {RuntimeCode}", runtimeCode);
// 构建网络配置请求
var networkRequests = BuildNetworkConfigurationRequests(request, networkConfigs, runtimeCode);
_logger.LogInformation("成功构建网络配置请求,请求数量: {RequestCount}", networkRequests?.Count ?? 0);
// 检查是否有有效的网络配置请求
if (networkRequests == null || !networkRequests.Any())
{
_logger.LogWarning("没有有效的网络配置请求,无法启动设备运行时");
return OperationResult.CreateFailure("没有有效的网络配置请求,无法启动设备运行时");
}
// 并行启动网络并收集结果
var (successfulDevices, failedDevices) = await StartNetworksInParallelAsync(networkRequests, cancellationToken);
if (failedDevices.Any())
{
_logger.LogWarning("部分设备网络启动失败,失败设备: {FailedDevices}",
string.Join(", ", failedDevices.Select(f => f.DeviceCode)));
}
// 只为成功启动网络的设备创建运行时详情和更新状态
var runtimeDetails = new List();
var updatedRuntimes = new List();
foreach (var deviceRequest in request.DeviceRequests)
{
// 只处理网络启动成功的设备
if (!successfulDevices.Contains(deviceRequest.DeviceCode))
{
_logger.LogWarning("跳过处理网络启动失败的设备,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
continue;
}
_logger.LogDebug("处理设备运行时,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
// 创建运行时详情
var detail = CellularDeviceRuntimeDetail.Create(
deviceRequest.DeviceCode,
runtimeCode,
deviceRequest.NetworkStackCode,
currentUser, true);
runtimeDetails.Add(detail);
// 获取并更新设备运行时状态
var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(deviceRequest.DeviceCode, cancellationToken);
if (deviceRuntime == null)
{
_logger.LogWarning("设备运行时不存在,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
continue;
}
deviceRuntime.Start(deviceRequest.NetworkStackCode, runtimeCode);
_deviceRuntimeRepository.UpdateRuntime(deviceRuntime);
updatedRuntimes.Add(deviceRuntime);
_logger.LogDebug("设备运行时状态已更新,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
}
// 批量保存运行时详情
if (runtimeDetails.Any())
{
_logger.LogDebug("保存运行时详情,详情数量: {DetailCount}", runtimeDetails.Count);
await _detailRepository.AddRangeAsync(runtimeDetails);
}
// 保存所有更改
await _unitOfWork.SaveChangesAsync(cancellationToken);
_logger.LogInformation("批量启动设备运行时状态完成,运行时代码: {RuntimeCode}, 成功设备数: {SuccessCount}, 失败设备数: {FailureCount}",
runtimeCode, successfulDevices.Count, failedDevices.Count);
return OperationResult.CreateSuccess(new StartDeviceRuntimeResponse
{
Summary = new BatchOperationSummary
{
TotalCount = request.DeviceRequests.Count,
SuccessCount = successfulDevices.Count,
FailureCount = failedDevices.Count
}
});
}
catch (Exception ex)
{
_logger.LogError(ex, "批量启动设备运行时状态失败");
return OperationResult.CreateFailure($"批量启动设备运行时状态失败: {ex.Message}");
}
}
///
/// 生成唯一的运行时编码
///
/// 取消令牌
/// 生成的运行时编码
///
/// 运行时编码格式:RT-{时间戳}-{序号}
/// - 时间戳:yyyyMMddHHmmssfff 格式,精确到毫秒
/// - 序号:基于当前运行时总数自动递增,至少3位数字
///
/// 使用信号量确保在多线程环境下生成唯一的编码:
/// 1. 防止并发访问导致的序号重复
/// 2. 确保时间戳和序号的组合唯一性
/// 3. 避免数据库查询和插入之间的竞态条件
/// 4. 支持异步操作,避免死锁风险
/// 5. 控制并发访问数量,提高性能
///
private static readonly SemaphoreSlim _runtimeCodeSemaphore = new SemaphoreSlim(1, 1);
private async Task GenerateRuntimeCodeAsync(CancellationToken cancellationToken)
{
// 使用信号量确保在多线程环境下生成唯一的运行时编码
await _runtimeCodeSemaphore.WaitAsync(cancellationToken);
try
{
_logger.LogDebug("开始生成运行时编码,线程ID: {ThreadId}", Thread.CurrentThread.ManagedThreadId);
// 获取当前设备运行时总数(异步调用)
var runtimeCount = await _deviceRuntimeRepository.GetRuntimeCountAsync(cancellationToken);
_logger.LogDebug("当前运行时总数: {RuntimeCount}", runtimeCount);
// 计算下一个序号
var nextNumber = runtimeCount + 1;
// 计算需要的位数,确保至少3位数
var digitCount = CalculateRequiredDigits(nextNumber);
// 格式化序号为指定位数
var formattedNumber = nextNumber.ToString($"D{digitCount}");
// 生成时间戳,精确到毫秒
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
// 组装运行时编码
var runtimeCode = $"RT-{timestamp}-{formattedNumber}";
_logger.LogDebug("成功生成运行时编码: {RuntimeCode}, 运行时总数: {RuntimeCount}, 序号: {NextNumber}, 位数: {DigitCount}, 线程ID: {ThreadId}",
runtimeCode, runtimeCount, nextNumber, digitCount, Thread.CurrentThread.ManagedThreadId);
return runtimeCode;
}
catch (Exception ex)
{
_logger.LogError(ex, "生成运行时编码失败,线程ID: {ThreadId}", Thread.CurrentThread.ManagedThreadId);
// 重新抛出异常的原因:
// 1. 保持异常的原始堆栈跟踪信息,便于调试和问题定位
// 2. 确保上层调用者能够捕获到原始异常,进行适当的错误处理
// 3. 避免异常信息丢失,保持异常传播链的完整性
// 4. 符合异常处理的最佳实践:记录日志后重新抛出
throw;
}
finally
{
// 确保信号量被释放
_runtimeCodeSemaphore.Release();
}
}
///
/// 计算需要的位数
///
private int CalculateRequiredDigits(int number)
{
if (number <= 0) return 3; // 从000开始,至少3位数
// 计算位数:确保至少3位数,从000开始
// 1-999用3位,1000-9999用4位,10000-99999用5位,以此类推
var calculatedDigits = (int)Math.Floor(Math.Log10(number)) + 1;
return Math.Max(calculatedDigits, 3); // 确保至少3位数
}
///
/// 构建网络配置请求
///
/// 启动设备运行时命令
/// 网络堆栈配置
/// 运行时编码
/// 构建好的网络配置请求列表
private List BuildNetworkConfigurationRequests(
StartDeviceRuntimeCommand request,
IEnumerable networkConfigs,
string runtimeCode)
{
// 按NetworkStackCode分组网络配置,提高查询效率
var networkConfigsByCode = networkConfigs
.Where(nc => !string.IsNullOrEmpty(nc.NetworkStackCode))
.GroupBy(nc => nc.NetworkStackCode!)
.ToDictionary(g => g.Key, g => g.ToList());
// 使用HashSet进行去重,提高性能
var processedDeviceNetworkPairs = new HashSet();
return request.DeviceRequests
.Where(deviceRequest =>
{
// 检查是否已处理过相同的设备-网络堆栈组合
var deviceNetworkKey = $"{deviceRequest.DeviceCode}_{deviceRequest.NetworkStackCode}";
if (processedDeviceNetworkPairs.Contains(deviceNetworkKey))
{
_logger.LogWarning("设备代码 {DeviceCode} 与网络堆栈 {NetworkStackCode} 的组合已存在,跳过重复处理",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
return false;
}
// 检查是否存在对应的网络配置
if (!networkConfigsByCode.TryGetValue(deviceRequest.NetworkStackCode, out var deviceNetworkConfigs))
{
_logger.LogWarning("未找到对应的网络配置,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
return false;
}
// 验证配置有效性
var hasValidRanConfig = deviceNetworkConfigs.Any(s => !string.IsNullOrEmpty(s.RanConfigContent));
var hasValidNetworkConfigs = deviceNetworkConfigs.Any(s =>
!string.IsNullOrEmpty(s.IMSConfigContent) && !string.IsNullOrEmpty(s.CoreNetworkConfigContent));
if (!hasValidRanConfig && !hasValidNetworkConfigs)
{
_logger.LogWarning("设备 {DeviceCode} 的网络配置既缺少RAN配置内容,又缺少有效的IMS和核心网配置内容,跳过处理",
deviceRequest.DeviceCode);
return false;
}
processedDeviceNetworkPairs.Add(deviceNetworkKey);
return true;
})
.Select(deviceRequest =>
{
var deviceNetworkConfigs = networkConfigsByCode[deviceRequest.NetworkStackCode];
// 构建CoreNetworkImsConfiguration列表
var coreNetworkImsConfigurations = deviceNetworkConfigs
.Where(s => !string.IsNullOrEmpty(s.IMSConfigContent) && !string.IsNullOrEmpty(s.CoreNetworkConfigContent))
.Select(networkConfig => new CoreNetworkImsConfiguration
{
Index = networkConfig.Index ?? 0,
Plmn = ExtractPlmnFromConfig(networkConfig.CoreNetworkConfigContent),
CoreNetworkConfiguration = networkConfig.CoreNetworkConfigContent!,
ImsServiceConfiguration = networkConfig.IMSConfigContent!
})
.ToList();
// 获取RAN配置
var validRanConfig = deviceNetworkConfigs
.Where(s => !string.IsNullOrEmpty(s.RanConfigContent))
.Select(s => s.RanConfigContent)
.Distinct()
.FirstOrDefault() ?? string.Empty;
// 创建网络配置
var configuration = new CellularNetworkConfiguration
{
RuntimeCode = runtimeCode,
DeviceCode = deviceRequest.DeviceCode,
RadioAccessNetworkConfiguration = validRanConfig,
CoreNetworkImsConfigurations = coreNetworkImsConfigurations
};
_logger.LogDebug("构建网络配置请求,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}, 绑定关系数量: {BindingCount}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode, coreNetworkImsConfigurations.Count);
return configuration;
})
.ToList();
}
///
/// 并行启动网络并收集结果
///
/// 网络配置请求列表
/// 取消令牌
/// 成功和失败的设备信息元组
///
/// 该方法使用并行执行来同时启动多个网络,提高性能:
/// 1. 使用ConcurrentBag确保线程安全的结果收集
/// 2. 使用SemaphoreSlim控制并发数量,避免资源竞争(可选)
/// 3. 设置超时机制,防止任务无限等待
/// 4. 提供详细的错误处理和日志记录
/// 5. 确保资源正确释放
///
private async Task<(HashSet SuccessfulDevices, List<(string DeviceCode, string ErrorMessage)> FailedDevices)>
StartNetworksInParallelAsync(List networkRequests, CancellationToken cancellationToken)
{
var networkResults = new ConcurrentBag<(bool Success, string DeviceCode, string ErrorMessage)>();
_logger.LogInformation("开始并行启动网络,请求数量: {RequestCount}", networkRequests.Count);
// 设置超时时间,防止任务无限等待
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); // 5分钟超时
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
// 完全并行执行,不受信号量限制
var tasks = networkRequests.Select(async networkRequest =>
{
try
{
_logger.LogDebug("启动网络,设备代码: {DeviceCode}, 运行时代码: {RuntimeCode}",
networkRequest.DeviceCode, networkRequest.RuntimeCode);
// 创建包装请求对象
var request = new StartCellularNetworkRequest
{
CellularNetwork = networkRequest
};
var startResult = await _protocolClient.StartNetworkAsync(request);
_logger.LogDebug("网络启动结果,设备代码: {DeviceCode}, 启动成功: {StartResult}",
networkRequest.DeviceCode, startResult);
if (startResult)
{
_logger.LogDebug("网络启动成功,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((true, networkRequest.DeviceCode, string.Empty));
}
else
{
var errorMessage = "网络启动返回失败状态";
_logger.LogWarning("网络启动返回失败状态,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((false, networkRequest.DeviceCode, errorMessage));
}
}
catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested)
{
var errorMessage = "网络启动超时";
_logger.LogWarning("网络启动超时,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((false, networkRequest.DeviceCode, errorMessage));
}
catch (Exception ex)
{
var errorMessage = $"网络启动失败: {ex.Message}";
_logger.LogError(ex, "网络启动失败,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((false, networkRequest.DeviceCode, errorMessage));
}
});
try
{
// 等待所有任务完成
await Task.WhenAll(tasks);
}
catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested)
{
_logger.LogWarning("网络启动操作超时,部分设备可能未完成启动");
}
// 检查网络启动结果
var successfulDevices = networkResults.Where(r => r.Success).Select(r => r.DeviceCode).ToHashSet();
var failedDevices = networkResults.Where(r => !r.Success).Select(r => (r.DeviceCode, r.ErrorMessage)).ToList();
_logger.LogInformation("网络启动完成,成功设备数: {SuccessCount}, 失败设备数: {FailureCount}",
successfulDevices.Count, failedDevices.Count);
return (successfulDevices, failedDevices);
}
///
/// 从网络配置中提取PLMN值
///
/// 网络配置内容
/// 提取的PLMN值,如果未找到则返回默认值"000000"
private string ExtractPlmnFromConfig(string configContent)
{
if (string.IsNullOrEmpty(configContent))
{
return "000000";
}
try
{
// 使用正则表达式匹配PLMN值(支持双引号和单引号)
var match = System.Text.RegularExpressions.Regex.Match(
configContent,
@"""plmn""\s*:\s*[""']([^""']+)[""']",
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (match.Success && match.Groups.Count > 1)
{
var plmnValue = match.Groups[1].Value.Trim();
_logger.LogDebug("从配置中提取到PLMN值: {PlmnValue}", plmnValue);
return string.IsNullOrEmpty(plmnValue) ? "000000" : plmnValue;
}
_logger.LogDebug("未从配置中找到PLMN值,使用默认值: 000000");
return "000000";
}
catch (Exception ex)
{
_logger.LogWarning(ex, "提取PLMN值时发生异常,使用默认值: 000000");
return "000000";
}
}
}