using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using WIDESEAWCS_Common;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_Tasks
{
///
/// 机械手状态管理器 - 负责 RobotSocketState 的线程安全更新和克隆
///
///
/// 核心功能是通过 IRobotStateRepository 管理数据库中的机械手状态。
/// 提供乐观并发控制,通过 RowVersion 防止并发更新时的数据覆盖问题。
///
public class RobotStateManager
{
///
/// 仓储服务实例,用于读写数据库中的状态数据
///
private readonly IRobotStateRepository _repository;
///
/// 日志记录器
///
private readonly ILogger _logger;
///
/// 构造函数
///
/// 仓储服务实例
/// 日志记录器
public RobotStateManager(IRobotStateRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
///
/// 安全更新 RobotSocketState 缓存,防止并发覆盖
///
///
/// 使用乐观并发模式:先读取当前 RowVersion,执行更新时检查版本是否一致。
/// 如果 RowVersion 不匹配(说明有其他线程已更新),则更新失败返回 false。
///
/// 设备 IP 地址
/// 更新状态的委托函数,传入当前状态副本,返回修改后的新状态
/// 是否更新成功;false 表示版本冲突或状态不存在
public bool TryUpdateStateSafely(string ipAddress, Func updateAction)
{
// 从数据库获取当前存储的状态
var currentEntity = _repository.GetByIp(ipAddress);
if (currentEntity == null)
{
return false;
}
// 记录当前存储的 RowVersion,作为更新时的期望版本
var expectedRowVersion = currentEntity.RowVersion;
// 创建状态的深拷贝副本(使用 JSON 序列化实现)
var stateCopy = CloneState(_repository.ToSocketState(currentEntity));
// 执行调用者提供的更新逻辑,传入副本状态,获取新的状态对象
var newState = updateAction(stateCopy);
// 将新状态转换为数据库实体
var newEntity = _repository.ToEntity(newState);
newEntity.RowVersion = Array.Empty(); // SqlSugar 会自动管理
newEntity.Id = currentEntity.Id;
// 调用仓储的安全更新方法,传入期望 RowVersion
// 如果 RowVersion 不一致(已被其他线程更新),则更新失败
return _repository.TryUpdate(ipAddress, newEntity, expectedRowVersion);
}
///
/// 安全更新 RobotSocketState 的重载版本(直接传入新状态)
///
///
/// 与上一个重载的区别:此方法直接接收完整的新状态对象,而不是更新委托。
/// 如果数据库中不存在该设备的状态,则创建新记录。
///
/// 设备 IP 地址
/// 新状态对象
/// 是否更新成功;新建设置为 true
public bool TryUpdateStateSafely(string ipAddress, RobotSocketState newState)
{
// 从数据库获取当前存储的状态
var currentEntity = _repository.GetByIp(ipAddress);
// 如果当前不存在该设备的状态,创建新记录
if (currentEntity == null)
{
var entity = _repository.ToEntity(newState);
entity.CreateTime = DateTime.Now;
entity.UpdateTime = DateTime.Now;
_repository.GetOrCreate(newState.IPAddress, newState.RobotCrane ?? new RobotCraneDevice());
_logger.LogDebug("TryUpdateStateSafely:创建新状态,IP: {IpAddress}", ipAddress);
QuartzLogger.Debug($"创建新状态,IP: {ipAddress}", ipAddress);
return true;
}
// 当前存在状态,记录期望 RowVersion 用于乐观锁检查
var expectedRowVersion = currentEntity.RowVersion;
// 将新状态转换为数据库实体
var newEntity = _repository.ToEntity(newState);
newEntity.Id = currentEntity.Id;
newEntity.RowVersion = Array.Empty();
// 尝试安全更新,如果版本冲突则返回 false
bool success = _repository.TryUpdate(ipAddress, newEntity, expectedRowVersion);
if (!success)
{
_logger.LogWarning("TryUpdateStateSafely:版本冲突,更新失败,IP: {IpAddress},期望版本字节长度: {ExpectedLength}", ipAddress, expectedRowVersion.Length);
QuartzLogger.Warn($"版本冲突,更新失败,IP: {ipAddress}", ipAddress);
}
return success;
}
///
/// 克隆 RobotSocketState 对象(深拷贝)
///
///
/// 使用 JSON 序列化/反序列化实现深拷贝。
/// 这样可以确保新对象与原对象完全独立,修改新对象不会影响原对象。
///
/// 源状态对象
/// 新的状态对象,是源对象的深拷贝
public RobotSocketState CloneState(RobotSocketState source)
{
var json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject(json) ?? new RobotSocketState { IPAddress = source.IPAddress };
}
///
/// 从数据库获取机械手状态
///
/// 设备 IP 地址
/// 如果存在则返回状态对象,否则返回 null
public RobotSocketState? GetState(string ipAddress)
{
var entity = _repository.GetByIp(ipAddress);
return entity != null ? _repository.ToSocketState(entity) : null;
}
///
/// 获取或创建机械手状态
///
///
/// 如果数据库中已存在该设备的状态,直接返回。
/// 如果不存在,则创建新的状态记录并返回。
///
/// 设备 IP 地址
/// 机器人设备信息,用于初始化新状态
/// 该设备的状态对象
public RobotSocketState GetOrCreateState(string ipAddress, RobotCraneDevice robotCrane)
{
var entity = _repository.GetOrCreate(ipAddress, robotCrane);
return _repository.ToSocketState(entity);
}
}
}