日期:2026-04-19
状态:已批准
将 RobotSocketState 的存储从 Redis 切换到 SQL Server 数据库,利用 SqlSugar 的 RowVersion 实现乐观并发控制。
RobotJob/ 文件夹下所有使用 Redis 读写 RobotSocketState 的代码Dt_RobotState 数据库实体和 IRobotStateRepository 仓储层RobotWorkflowOrchestrator、RobotTaskProcessor、RobotSimpleCommandHandler 等业务逻辑不变RobotStateManager 的调用方代码不变,只改存储后端表名: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
以下复杂对象以 JSON 字符串存储,反序列化时保持与原 RobotSocketState 属性完全兼容:
| JSON 字段 | 对应原属性 | 反序列化类型 |
|---|---|---|
RobotCraneJson |
RobotCrane |
RobotCraneDevice |
CurrentTaskJson |
CurrentTask |
Dt_RobotTask |
LastPickPositionsJson |
LastPickPositions |
int[] |
LastPutPositionsJson |
LastPutPositions |
int[] |
CellBarcodeJson |
CellBarcode |
List<string> |
序列化工具:Newtonsoft.Json(与项目现有保持一致)
调用方(RobotJob / Workflow / Processor)
↓ 依赖
RobotStateManager
↓ 依赖
IRobotStateRepository(接口)
↓ 实现
RobotStateRepository(SqlSugar 实现)
↓ 操作
SQL Server (Dt_RobotState 表)
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);
}
ISqlSugarClientGetOrCreate:先查,无记录则 INSERTTryUpdate:执行 UPDATE ... WHERE RowVersion = @expected,检查影响行数文件: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)
文件: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。
| 操作 | 文件路径 |
|---|---|
| 新增 | 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 互转。
在 AutofacModuleRegister 或对应 DI 配置中注册:
builder.RegisterType<RobotStateRepository>().As<IRobotStateRepository>().InstancePerDependency();
Dt_RobotState 实体类IRobotStateRepository 接口和 RobotStateRepository 实现RobotStateManager:依赖 Repository,替换 Redis 调用RobotJob:注入 Repository 到 StateManagerRobotSocketState:作为内存 DTO 保留,或与实体合并(待定)