using System;
using System.Collections.Generic;
using System.Linq;
using log4net;
using Plugin.DeviceDebug.Entity;
using Aging.Infrastructure.Contracts;
namespace Plugin.DeviceDebug.Model
{
///
/// 调试会话管理器 - 负责状态机转换和权限校验
///
public class DebugSessionManager
{
private static readonly ILog log = LogManager.GetLogger(typeof(DebugSessionManager));
private readonly DeviceDebugModel _model;
private readonly IResourceManager? _resourceManager;
// 内存中的活跃会话缓存
private readonly Dictionary _activeSessions = new();
///
/// 维护租约心跳:与 LayeredResourceManager.DefaultTtlSeconds(300s) 对齐,
/// 每 120s 续期一次,防止长时间维护会话的 arb.db 租约过期进入 RecoveryRequired 后禁止 I/O。
///
private System.Threading.Timer _renewTimer;
private const int MaintenanceTtlSeconds = 300;
private const int RenewIntervalMs = 120_000;
///
/// 会话→资源令牌缓存:LayeredResourceManager 的租约按 TokenId 索引,
/// 而 ResourceOwnerToken.TokenId 每次 new 都是新 GUID,必须复用同一实例才能释放到租约
///
private readonly Dictionary _sessionOwnerTokens = new();
public DebugSessionManager(DeviceDebugModel model, IResourceManager? resourceManager = null)
{
_model = model;
_resourceManager = resourceManager;
}
///
/// 打开会话(ReadOnly模式)
///
public DebugSession OpenSession(string deviceId, string operatorId, string operatorName, string effectiveVersion)
{
try
{
// 检查是否已有活跃会话
var existingSession = GetActiveSessionByDevice(deviceId);
if (existingSession != null)
{
// 进程内存中存在 = 本会话真活跃,直接复用
if (_activeSessions.ContainsKey(existingSession.SessionId))
{
log.Warn($"设备 {deviceId} 已有活跃会话: {existingSession.SessionId}");
return existingSession;
}
// 仅存在于数据库且不在本进程内存 = 旧进程退出前的残留会话:
// 其内存上下文/维护租约(arb.db 侧靠 TTL 过期+回收)已随进程消亡,
// 必须回收为 Closed 再新建,否则 RequestMaintenance 永远“会话不存在”拒绝
_activeSessions[existingSession.SessionId] = existingSession;
existingSession.State = DebugSessionState.Closed;
existingSession.ClosedAt = DateTime.Now;
_model.UpdateSession(existingSession);
_activeSessions.Remove(existingSession.SessionId);
log.Warn($"已回收设备 {deviceId} 的残留会话 {existingSession.SessionId}(进程重启导致死会话),新建会话替代");
}
var session = new DebugSession
{
DeviceId = deviceId,
OperatorId = operatorId,
OperatorName = operatorName,
EffectiveVersion = effectiveVersion,
State = DebugSessionState.ReadOnly
};
// 持久化到数据库
_model.CreateSession(session);
// 更新内存缓存
_activeSessions[session.SessionId] = session;
log.Info($"打开会话: {session.SessionId}, 设备: {deviceId}, 操作员: {operatorName}");
return session;
}
catch (Exception ex)
{
log.Error($"打开会话失败: {ex.Message}", ex);
throw;
}
}
///
/// 请求进入维护模式(ReadOnly → AcquiringMaintenance)
/// 检查:设备是否有生产Reserved
///
public bool RequestMaintenance(string sessionId)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
// 兜底:会话可能在数据库(如 UI 持有的 sessionID 属于重启前残留)→ 恢复进缓存后继续
session = _model.GetSession(sessionId);
if (session == null || session.State == DebugSessionState.Closed)
{
log.Warn($"会话不存在: {sessionId}");
return false;
}
_activeSessions[sessionId] = session;
log.Warn($"会话 {sessionId} 从数据库恢复进内存缓存后继续维护请求");
}
// 验证状态转换
if (session.State != DebugSessionState.ReadOnly)
{
log.Warn($"会话 {sessionId} 当前状态为 {session.State},无法请求维护模式");
return false;
}
// 检查设备是否有生产Reserved(与资源预约系统集成)
if (_resourceManager != null)
{
var resourceKey = new ResourceKey(session.DeviceId, "Generic");
// 复用已缓存令牌(残留会话从 DB 恢复后首次进入时同样新建并存入)
if (!_sessionOwnerTokens.TryGetValue(sessionId, out var ownerToken))
{
ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
_sessionOwnerTokens[sessionId] = ownerToken;
}
try
{
var reservation = _resourceManager.ReserveAsync(
ownerToken, resourceKey, CancellationToken.None).GetAwaiter().GetResult();
session.ReservationRef = reservation.ReservationId;
}
catch (ResourceConflictException)
{
_sessionOwnerTokens.Remove(sessionId);
log.Warn($"设备 {session.DeviceId} 已被生产预约占用,拒绝维护请求");
return false;
}
}
else
{
// 降级模式:无 ResourceManager 时允许所有请求(向后兼容)
log.Warn("ResourceManager 未初始化,跳过资源预约检查");
}
// 状态转换
session.State = DebugSessionState.AcquiringMaintenance;
// 持久化
_model.UpdateSession(session);
log.Info($"会话 {sessionId} 请求维护模式");
return true;
}
catch (Exception ex)
{
log.Error($"请求维护模式失败: {ex.Message}", ex);
return false;
}
}
///
/// 激活维护模式(AcquiringMaintenance → MaintenanceActive)
///
public void ActivateMaintenance(string sessionId)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
log.Warn($"会话不存在: {sessionId}");
throw new InvalidOperationException($"会话不存在: {sessionId}");
}
// 验证状态转换
if (session.State != DebugSessionState.AcquiringMaintenance)
{
log.Warn($"会话 {sessionId} 当前状态为 {session.State},无法激活维护模式");
throw new InvalidOperationException($"会话 {sessionId} 当前状态为 {session.State},无法激活维护模式");
}
// 状态转换
session.State = DebugSessionState.MaintenanceActive;
// 持久化
_model.UpdateSession(session);
// 启动维护租约心跳续期(防止 TTL 过期)
EnsureRenewHeartbeat();
log.Info($"会话 {sessionId} 激活维护模式");
}
catch (Exception ex)
{
log.Error($"激活维护模式失败: {ex.Message}", ex);
throw;
}
}
/// 首次进入维护时启动周期续期定时器(幂等)。
private void EnsureRenewHeartbeat()
{
if (_renewTimer != null) return;
_renewTimer = new System.Threading.Timer(_ => RenewActiveMaintenance(),
null, RenewIntervalMs, RenewIntervalMs);
log.Info("维护租约心跳已启动");
}
/// 周期回调:对所有 MaintenanceActive 会话续期其 arb.db 租约。
private void RenewActiveMaintenance()
{
var lrm = _resourceManager as Aging.Infrastructure.Core.LayeredResourceManager;
if (lrm == null) return; // 非分层实现(无 Coordinator)时降级不续期
try
{
foreach (var kv in _activeSessions.ToArray())
{
var s = kv.Value;
if (s.State != DebugSessionState.MaintenanceActive) continue;
if (!_sessionOwnerTokens.TryGetValue(s.SessionId, out var token)) continue;
if (!lrm.TryGetGrants(token.TokenId, out var grants) || grants == null) continue;
foreach (var g in grants)
{
try { lrm.Coordinator.Renew(g, MaintenanceTtlSeconds); }
catch (Exception ex) { log.Warn($"续期设备 {s.DeviceId} 租约失败: {ex.Message}"); }
}
}
}
catch (Exception ex) { log.Error($"维护租约心跳异常: {ex.Message}", ex); }
}
/// 无维护中会话时停止心跳,避免空转。
private void StopRenewHeartbeatIfIdle()
{
if (_renewTimer == null) return;
if (_activeSessions.Values.Any(s => s.State == DebugSessionState.MaintenanceActive)) return;
try { _renewTimer.Dispose(); } catch { }
_renewTimer = null;
log.Info("无维护中会话,维护租约心跳已停止");
}
///
/// 关闭会话(MaintenanceActive → Closing → Closed)
/// 执行安全状态检查
///
public void CloseSession(string sessionId)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
log.Warn($"会话不存在: {sessionId}");
throw new InvalidOperationException($"会话不存在: {sessionId}");
}
// 验证状态转换(只有 MaintenanceActive 可以关闭)
if (session.State != DebugSessionState.MaintenanceActive)
{
log.Warn($"会话 {sessionId} 当前状态为 {session.State},无法关闭");
throw new InvalidOperationException($"会话 {sessionId} 当前状态为 {session.State},无法关闭");
}
// 状态转换: MaintenanceActive → Closing
session.State = DebugSessionState.Closing;
_model.UpdateSession(session);
log.Debug($"会话 {sessionId} 进入关闭中状态");
// 执行安全状态检查:释放资源预约
if (_resourceManager != null && !string.IsNullOrEmpty(session.ReservationRef))
{
try
{
var resourceKey = new ResourceKey(session.DeviceId, "Generic");
// 必须复用进入维护时的同一令牌实例:LayeredResourceManager 按 TokenId(GUID) 索引租约,
// 重新 new 会导致 FindGrant 落空、租约永不释放→过期后 RecoveryRequired 永久阻塞后续维护
if (!_sessionOwnerTokens.TryGetValue(sessionId, out var ownerToken))
{
ownerToken = new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId);
}
_resourceManager.ReleaseReservationAsync(
ownerToken, resourceKey, CancellationToken.None).GetAwaiter().GetResult();
_sessionOwnerTokens.Remove(sessionId);
log.Info($"已释放设备 {session.DeviceId} 的资源预约");
}
catch (Exception ex)
{
log.Error($"释放资源预约失败: {ex.Message}", ex);
// 不阻止关闭流程,但记录警告
}
}
// 状态转换: Closing → Closed
session.State = DebugSessionState.Closed;
session.ClosedAt = DateTime.Now;
_model.UpdateSession(session);
// 从缓存中移除
_activeSessions.Remove(sessionId);
// 若无维护中会话则停止心跳
StopRenewHeartbeatIfIdle();
log.Info($"会话 {sessionId} 已关闭");
}
catch (Exception ex)
{
log.Error($"关闭会话失败: {ex.Message}", ex);
throw;
}
}
///
/// 故障处理(任意 → Faulted)
///
public void FaultSession(string sessionId, string errorMessage)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
log.Warn($"会话不存在: {sessionId}");
return;
}
// 记录当前状态
var previousState = session.State;
// 状态转换
session.State = DebugSessionState.Faulted;
// 持久化
_model.UpdateSession(session);
log.Error($"会话 {sessionId} 发生故障,从 {previousState} 转为 Faulted: {errorMessage}");
}
catch (Exception ex)
{
log.Error($"标记故障状态失败: {ex.Message}", ex);
}
}
///
/// 标记需要恢复(任意 → RecoveryRequired)
///
public void MarkRecoveryRequired(string sessionId)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
log.Warn($"会话不存在: {sessionId}");
return;
}
// 记录当前状态
var previousState = session.State;
// 状态转换
session.State = DebugSessionState.RecoveryRequired;
// 持久化
_model.UpdateSession(session);
log.Warn($"会话 {sessionId} 标记为需要恢复,从 {previousState} 转为 RecoveryRequired");
}
catch (Exception ex)
{
log.Error($"标记恢复状态失败: {ex.Message}", ex);
}
}
///
/// 验证命令是否允许执行(只有 MaintenanceActive 状态允许写操作)
///
public bool ValidateCommand(string sessionId, DebugCommand command)
{
try
{
if (!_activeSessions.TryGetValue(sessionId, out var session))
{
log.Warn($"会话不存在: {sessionId}");
return false;
}
// 只有 MaintenanceActive 状态允许写操作
bool allowed = session.State == DebugSessionState.MaintenanceActive;
if (!allowed)
{
log.Warn($"会话 {sessionId} 状态为 {session.State},拒绝执行命令: {command.CommandType}");
}
// 增加资源预约验证
if (allowed && _resourceManager != null)
{
var resourceKey = new ResourceKey(session.DeviceId, "Generic");
allowed = _resourceManager.ValidateOwner(
new ResourceOwnerToken(OwnerType.Maintenance, session.SessionId),
resourceKey);
if (!allowed)
{
log.Warn($"会话 {sessionId} 资源预约验证失败,拒绝执行命令: {command.CommandType}");
}
}
return allowed;
}
catch (Exception ex)
{
log.Error($"验证命令失败: {ex.Message}", ex);
return false;
}
}
///
/// 获取会话状态
///
public DebugSessionState? GetSessionState(string sessionId)
{
try
{
if (_activeSessions.TryGetValue(sessionId, out var session))
{
return session.State;
}
// 从数据库查询
var dbSession = _model.GetSession(sessionId);
return dbSession?.State;
}
catch (Exception ex)
{
log.Error($"获取会话状态失败: {ex.Message}", ex);
return null;
}
}
///
/// 获取设备当前会话(如果有)
///
public DebugSession? GetActiveSessionByDevice(string deviceId)
{
try
{
// 先从内存缓存查找
var cachedSession = _activeSessions.Values
.FirstOrDefault(s => s.DeviceId == deviceId && s.State != DebugSessionState.Closed);
if (cachedSession != null)
{
return cachedSession;
}
// 从数据库查询
var activeSessions = _model.GetActiveSessionsByDevice(deviceId);
return activeSessions.FirstOrDefault();
}
catch (Exception ex)
{
log.Error($"获取设备活跃会话失败: {ex.Message}", ex);
return null;
}
}
}
}