编辑 | blame | 历史 | 原始文档

RobotState Redis → 数据库迁移设计

日期:2026-04-19
状态:已批准

1. 背景与目标

RobotSocketState 的存储从 Redis 切换到 SQL Server 数据库,利用 SqlSugar 的 RowVersion 实现乐观并发控制。

涉及范围

  • RobotJob/ 文件夹下所有使用 Redis 读写 RobotSocketState 的代码
  • 新建 Dt_RobotState 数据库实体和 IRobotStateRepository 仓储层

不涉及变更

  • RobotWorkflowOrchestratorRobotTaskProcessorRobotSimpleCommandHandler 等业务逻辑不变
  • 依赖 RobotStateManager 的调用方代码不变,只改存储后端

2. 数据库实体设计

表名Dt_RobotState

实体路径WIDESEAWCS_Model/Models/RobotState/Dt_RobotState.cs

字段名 类型 说明
Id int 主键,自增
IPAddress string(50) 设备IP,唯一索引
RowVersion byte[] SqlSugar 行版本,并发控制
IsEventSubscribed bool 是否已订阅消息
RobotRunMode int? 运行模式(1手动 2自动)
RobotControlMode int? 控制模式(1客户端控制 2其他)
RobotArmObject int? 手臂抓取状态(0无物料 1有物料)
RobotCraneJson string(max) 设备信息序列化 JSON
Homed string(50) 回零状态(Homed/Homing)
CurrentAction string(50) 当前动作(Picking/Putting/PickFinished 等)
OperStatus string(50) 运行状态(Running/Pausing/Emstoping 等)
LastPickPositionsJson string(max) 取货位置数组 JSON
LastPutPositionsJson string(max) 放货位置数组 JSON
CellBarcodeJson string(max) 电芯条码列表 JSON
CurrentTaskJson string(max) 当前任务 Dt_RobotTask 序列化 JSON
IsSplitPallet bool 是否拆盘任务
IsGroupPallet bool 是否组盘/换盘任务
RobotTaskTotalNum int 已处理任务总数
IsInFakeBatteryMode bool 是否假电芯补充模式
CurrentBatchIndex int 当前批次起始编号
ChangePalletPhase int 换盘任务阶段(0-5)
IsScanNG bool 是否扫码NG
BatteryArrived bool 电芯是否到位
CreateTime datetime 创建时间
UpdateTime datetime 最后更新时间

索引IPAddress 唯一索引,用于快速定位设备状态

并发控制:SqlSugar RowVersion,数据库自动递增,更新时 WHERE RowVersion = @expected


3. JSON 序列化字段

以下复杂对象以 JSON 字符串存储,反序列化时保持与原 RobotSocketState 属性完全兼容:

JSON 字段 对应原属性 反序列化类型
RobotCraneJson RobotCrane RobotCraneDevice
CurrentTaskJson CurrentTask Dt_RobotTask
LastPickPositionsJson LastPickPositions int[]
LastPutPositionsJson LastPutPositions int[]
CellBarcodeJson CellBarcode List<string>

序列化工具:Newtonsoft.Json(与项目现有保持一致)


4. 架构分层

调用方(RobotJob / Workflow / Processor)
        ↓ 依赖
RobotStateManager
        ↓ 依赖
IRobotStateRepository(接口)
        ↓ 实现
RobotStateRepository(SqlSugar 实现)
        ↓ 操作
SQL Server (Dt_RobotState 表)

4.1 IRobotStateRepository 接口

public interface IRobotStateRepository
{
    /// <summary>根据 IP 获取状态,不存在返回 null</summary>
    Dt_RobotState? GetByIp(string ipAddress);

    /// <summary>获取或创建状态(数据库无记录时创建)</summary>
    Dt_RobotState GetOrCreate(string ipAddress, RobotCraneDevice robotCrane);

    /// <summary>安全更新(乐观锁),返回是否成功</summary>
    bool TryUpdate(string ipAddress, Dt_RobotState newState, byte[] expectedRowVersion);
}

4.2 RobotStateRepository 实现要点

  • 注入 ISqlSugarClient
  • GetOrCreate:先查,无记录则 INSERT
  • TryUpdate:执行 UPDATE ... WHERE RowVersion = @expected,检查影响行数
  • 数组/复杂对象:在 Repository 层序列化/反序列化,对外暴露强类型属性

5. RobotStateManager 改造

文件WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs

改造内容

原来(Redis) 改造后(DB)
ICacheService _cache IRobotStateRepository _repository
GetState(ipAddress) _repository.GetByIp(ipAddress)
GetOrCreateState(ipAddress, robotCrane) _repository.GetOrCreate(ipAddress, robotCrane)
TryUpdateStateSafely(ipAddress, func) 内部调用 _repository.TryUpdate,使用 RowVersion 作为期望版本
CloneState 保留(JSON 序列化深拷贝)
GetCacheKey(ipAddress) 移除(不再需要 Redis Key)

构造函数变更

// 原来
public RobotStateManager(ICacheService cache, ILogger logger)

// 改造后
public RobotStateManager(IRobotStateRepository repository, ILogger logger)

6. RobotJob 构造函数改造

文件WIDESEAWCS_Tasks/RobotJob/RobotJob.cs

// 原来
_stateManager = new RobotStateManager(cache, _logger);

// 改造后:需要通过 DI 注入 IRobotStateRepository
_stateManager = new RobotStateManager(
    ResolvedInstances.FirstOrDefault(typeof(IRobotStateRepository)) as IRobotStateRepository,
    _logger);

备选方案:如果 DI 容器在 Job 构造时不便解析,可通过方法参数注入 IRobotStateRepository


7. 文件变更清单

操作 文件路径
新增 WIDESEAWCS_Model/Models/RobotState/Dt_RobotState.cs
新增 WIDESEAWCS_ITaskInfoRepository/IRobotStateRepository.cs
新增 WIDESEAWCS_ITaskInfoRepository/RobotStateRepository.cs
改造 WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs
改造 WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
改造 WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs(移除或保留为 DTO?)

说明RobotSocketState.cs 建议保留作为内存中的状态对象(DTO),在 Repository 层做实体转换。业务层继续使用 RobotSocketState,Repository 层负责与 Dt_RobotState 互转。


8. 依赖注入注册

AutofacModuleRegister 或对应 DI 配置中注册:

builder.RegisterType<RobotStateRepository>().As<IRobotStateRepository>().InstancePerDependency();

9. 迁移步骤(实施计划)

  1. 新建 Dt_RobotState 实体类
  2. 新建 IRobotStateRepository 接口和 RobotStateRepository 实现
  3. 改造 RobotStateManager:依赖 Repository,替换 Redis 调用
  4. 改造 RobotJob:注入 Repository 到 StateManager
  5. 更新 RobotSocketState:作为内存 DTO 保留,或与实体合并(待定)
  6. 配置 DI 注册
  7. 测试验证:确保并发更新、状态流转逻辑与原来一致