For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 在 HandlePutFinishedStateAsync 中实现换盘任务的特殊逻辑:当 task.RobotTaskTotalNum != 48 时,正常电芯任务完成后需补充假电芯至48个。
Architecture: 换盘任务分两阶段执行:(1) 正常电芯抓取阶段;(2) 假电芯补充阶段。假电芯从位置5抓取,指令格式 Pickbattery,5,1-3。新增 Dt_FakeBatteryPosition 表管理3×16平面点位的占用状态。
Tech Stack: C# / .NET 6, SqlSugar ORM, Redis缓存
WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/TaskInfo/
+ Dt_FakeBatteryPosition.cs # 假电芯平面点位表实体
WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoRepository/
+ IFakeBatteryPositionRepository.cs # 假电芯位置仓储接口
WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoRepository/
+ FakeBatteryPositionRepository.cs # 假电芯位置仓储实现
WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/
+ IFakeBatteryPositionService.cs # 假电芯位置服务接口
WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/
+ FakeBatteryPositionService.cs # 假电芯位置服务实现
WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/
= RobotSocketState.cs # +IsInFakeBatteryMode 标志
= RobotTaskProcessor.cs # +SendSocketRobotFakeBatteryPickAsync 方法
WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/
= RobotWorkflowOrchestrator.cs # 实现 ChangePallet 分支完整逻辑
Files:
- Create: WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/TaskInfo/Dt_FakeBatteryPosition.cs
using SqlSugar;
using WIDESEAWCS_Core.DB.Models;
namespace WIDESEAWCS_Model.Models
{
/// <summary>
/// 假电芯平面点位表
/// </summary>
/// <remarks>
/// 用于管理假电芯抓取点的平面点位信息。
/// 3行×16列布局,共48个点位(1-48),行优先排列。
/// 第1行:1-16,第2行:17-32,第3行:33-48。
/// </remarks>
[SugarTable(nameof(Dt_FakeBatteryPosition), "假电芯平面点位表")]
public class Dt_FakeBatteryPosition : BaseEntity
{
/// <summary>
/// 主键ID
/// </summary>
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "主键ID")]
public int Id { get; set; }
/// <summary>
/// 平面点位索引(1-48)
/// </summary>
/// <remarks>
/// 3×16布局,行优先(1-16为第1行,17-32为第2行,33-48为第3行)。
/// </remarks>
[SugarColumn(ColumnDescription = "平面点位索引")]
public int PositionIndex { get; set; }
/// <summary>
/// 所在行(1-3)
/// </summary>
[SugarColumn(ColumnDescription = "所在行")]
public int Row { get; set; }
/// <summary>
/// 所在列(1-16)
/// </summary>
[SugarColumn(ColumnDescription = "所在列")]
public int Col { get; set; }
/// <summary>
/// 是否已使用
/// </summary>
[SugarColumn(ColumnDescription = "是否已使用")]
public bool IsUsed { get; set; }
}
}
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/TaskInfo/Dt_FakeBatteryPosition.cs
git commit -m "feat(Robot): 添加假电芯平面点位表实体 Dt_FakeBatteryPosition"
Files:
- Create: WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoRepository/IFakeBatteryPositionRepository.cs
- Create: WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoRepository/FakeBatteryPositionRepository.cs
using WIDESEAWCS_Core.BaseRepository;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_ITaskInfoRepository
{
/// <summary>
/// 假电芯位置仓储接口
/// </summary>
public interface IFakeBatteryPositionRepository : IRepository<Dt_FakeBatteryPosition>
{
/// <summary>
/// 获取下N个可用的假电芯平面点位
/// </summary>
/// <param name="count">需要获取的点位数量</param>
/// <returns>可用点位列表,按PositionIndex升序</returns>
List<int> GetNextAvailable(int count);
/// <summary>
/// 重置所有点位为未使用
/// </summary>
/// <returns>影响的行数</returns>
int ResetAll();
/// <summary>
/// 标记指定点位为已使用
/// </summary>
/// <param name="positions">点位索引列表</param>
/// <returns>是否成功</returns>
bool MarkAsUsed(List<int> positions);
/// <summary>
/// 根据行和列获取点位索引
/// </summary>
/// <param name="row">行(1-3)</param>
/// <param name="col">列(1-16)</param>
/// <returns>点位索引(1-48),找不到返回null</returns>
int? GetPositionIndex(int row, int col);
}
}
using WIDESEAWCS_Core.BaseRepository;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_TaskInfoRepository
{
/// <summary>
/// 假电芯位置仓储实现
/// </summary>
public class FakeBatteryPositionRepository : RepositoryBase<Dt_FakeBatteryPosition>, IFakeBatteryPositionRepository
{
public FakeBatteryPositionRepository(IUnitOfWorkManage unitOfWorkManage) : base(unitOfWorkManage)
{
}
/// <inheritdoc/>
public List<int> GetNextAvailable(int count)
{
return Db.Queryable<Dt_FakeBatteryPosition>()
.Where(x => !x.IsUsed)
.OrderBy(x => x.PositionIndex)
.Take(count)
.Select(x => x.PositionIndex)
.ToList();
}
/// <inheritdoc/>
public int ResetAll()
{
return Db.Updateable<Dt_FakeBatteryPosition>()
.SetColumns(x => x.IsUsed, false)
.ExecuteCommand();
}
/// <inheritdoc/>
public bool MarkAsUsed(List<int> positions)
{
if (positions == null || positions.Count == 0)
return true;
return Db.Updateable<Dt_FakeBatteryPosition>()
.SetColumns(x => x.IsUsed, true)
.Where(x => positions.Contains(x.PositionIndex))
.ExecuteCommand() > 0;
}
/// <inheritdoc/>
public int? GetPositionIndex(int row, int col)
{
return Db.Queryable<Dt_FakeBatteryPosition>()
.Where(x => x.Row == row && x.Col == col)
.Select(x => x.PositionIndex)
.FirstOrDefault();
}
}
}
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoRepository/IFakeBatteryPositionRepository.cs
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoRepository/FakeBatteryPositionRepository.cs
git commit -m "feat(Robot): 添加假电芯位置仓储层"
Files:
- Create: WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/IFakeBatteryPositionService.cs
- Create: WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/FakeBatteryPositionService.cs
using WIDESEAWCS_Core.BaseServices;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_ITaskInfoService
{
/// <summary>
/// 假电芯位置服务接口
/// </summary>
public interface IFakeBatteryPositionService : IService<Dt_FakeBatteryPosition>
{
/// <summary>
/// 获取下N个可用的假电芯平面点位
/// </summary>
/// <param name="count">需要获取的点位数量</param>
/// <returns>可用点位列表,按PositionIndex升序</returns>
List<int> GetNextAvailable(int count);
/// <summary>
/// 重置所有点位为未使用
/// </summary>
/// <returns>影响的行数</returns>
int ResetAll();
/// <summary>
/// 标记指定点位为已使用
/// </summary>
/// <param name="positions">点位索引列表</param>
/// <returns>是否成功</returns>
bool MarkAsUsed(List<int> positions);
/// <summary>
/// 根据行和列获取点位索引
/// </summary>
/// <param name="row">行(1-3)</param>
/// <param name="col">列(1-16)</param>
/// <returns>点位索引(1-48),找不到返回null</returns>
int? GetPositionIndex(int row, int col);
/// <summary>
/// 初始化假电芯点位表(48个点位)
/// </summary>
/// <remarks>
/// 仅当表为空时插入1-48的初始数据。
/// 3行×16列,行优先排列。
/// </remarks>
/// <returns>是否成功</returns>
bool InitializeIfEmpty();
}
}
using WIDESEAWCS_Core.BaseServices;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_TaskInfoService
{
/// <summary>
/// 假电芯位置服务实现
/// </summary>
public class FakeBatteryPositionService : ServiceBase<Dt_FakeBatteryPosition, IFakeBatteryPositionRepository>, IFakeBatteryPositionService
{
public FakeBatteryPositionService(IFakeBatteryPositionRepository BaseDal) : base(BaseDal)
{
}
/// <inheritdoc/>
public List<int> GetNextAvailable(int count)
{
return BaseDal.GetNextAvailable(count);
}
/// <inheritdoc/>
public int ResetAll()
{
return BaseDal.ResetAll();
}
/// <inheritdoc/>
public bool MarkAsUsed(List<int> positions)
{
return BaseDal.MarkAsUsed(positions);
}
/// <inheritdoc/>
public int? GetPositionIndex(int row, int col)
{
return BaseDal.GetPositionIndex(row, col);
}
/// <inheritdoc/>
public bool InitializeIfEmpty()
{
var existing = BaseDal.QueryFirst(x => true);
if (existing != null)
return true;
// 生成48个点位:3行×16列,行优先
var positions = new List<Dt_FakeBatteryPosition>();
for (int row = 1; row <= 3; row++)
{
for (int col = 1; col <= 16; col++)
{
int positionIndex = (row - 1) * 16 + col;
positions.Add(new Dt_FakeBatteryPosition
{
PositionIndex = positionIndex,
Row = row,
Col = col,
IsUsed = false,
Creater = "System"
});
}
}
return BaseDal.AddData(positions) > 0;
}
}
}
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/IFakeBatteryPositionService.cs
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/FakeBatteryPositionService.cs
git commit -m "feat(Robot): 添加假电芯位置服务层"
Files:
- Modify: WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs:175 (在 RobotTaskTotalNum 属性后添加)
在 RobotSocketState.cs 的 RobotTaskTotalNum 属性后添加:
/// <summary>
/// 机器人已处理的任务总数
/// </summary>
public int RobotTaskTotalNum { get; set; }
/// <summary>
/// 是否处于假电芯补充模式
/// </summary>
/// <remarks>
/// 当正常电芯任务完成后设为 true,机器人从假电芯位置补充电芯至48个。
/// </remarks>
public bool IsInFakeBatteryMode { get; set; }
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
git commit -m "feat(Robot): RobotSocketState 添加 IsInFakeBatteryMode 标志"
Files:
- Modify: WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs (在 SendSocketRobotPickAsync 后添加新方法)
在 RobotTaskProcessor 构造函数中添加:
private readonly IFakeBatteryPositionService _fakeBatteryPositionService;
// 构造函数参数中添加:
, IFakeBatteryPositionService fakeBatteryPositionService
// 构造函数赋值:
_fakeBatteryPositionService = fakeBatteryPositionService;
在 SendSocketRobotPickAsync 方法后添加:
/// <summary>
/// 下发假电芯取货指令到机器人客户端
/// </summary>
/// <remarks>
/// 发送格式:Pickbattery,5,{startPosition}-{endPosition}
/// 例如:Pickbattery,5,1-3 表示从假电芯位置5抓取,平面点位1到3
///
/// 下发成功后:
/// 1. 标记点位为已使用
/// 2. 更新任务状态为"机器人执行中"
/// 3. 安全更新状态到 Redis
/// </remarks>
/// <param name="task">要下发的任务对象</param>
/// <param name="state">机器人当前状态</param>
/// <param name="positions">要抓取的平面点位列表</param>
public async Task SendSocketRobotFakeBatteryPickAsync(Dt_RobotTask task, RobotSocketState state, List<int> positions)
{
if (positions == null || positions.Count == 0)
{
_logger.LogWarning("SendSocketRobotFakeBatteryPickAsync:平面点位列表为空,任务号: {TaskNum}", task.RobotTaskNum);
return;
}
// 计算点位范围,格式:1-3
int startPos = positions.Min();
int endPos = positions.Max();
string taskString = $"Pickbattery,5,{startPos}-{endPos}";
// 标记点位为已使用
_fakeBatteryPositionService.MarkAsUsed(positions);
// 通过 Socket 网关发送指令到机器人客户端
bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
if (result)
{
_logger.LogInformation("下发假电芯取货指令成功,指令: {TaskString},点位: {Positions},设备: {DeviceName}",
taskString, string.Join(",", positions), state.RobotCrane?.DeviceName);
QuartzLogger.Info($"下发假电芯取货指令成功,指令: {taskString}", state.RobotCrane?.DeviceName);
// 更新任务状态为"机器人执行中"
task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
// 将任务关联到状态对象
state.CurrentTask = task;
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.UpdateRobotTaskAsync(task);
}
}
else
{
_logger.LogError("下发假电芯取货指令失败,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName);
QuartzLogger.Error($"下发假电芯取货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName);
}
}
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
git commit -m "feat(Robot): RobotTaskProcessor 添加假电芯取货指令方法"
Files:
- Modify: WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs:285-300
将现有的占位代码替换为:
else if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode())
{
// 换盘任务
// 目标:正常电芯抓取完成后,补充假电芯至48个
const int targetTotal = 48;
const int fakeBatteryPickPosition = 5; // 假电芯抓取位置
const int pickCountPerExecution = 4; // 每次抓取数量
int targetNormalCount = task.RobotTaskTotalNum; // 正常电芯目标数量
int currentCompletedCount = stateForUpdate.RobotTaskTotalNum; // 已完成数量
// 如果目标数量为48,直接下发正常任务
if (targetNormalCount == targetTotal)
{
await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
}
// 如果已完成数量小于目标数量,继续抓取正常电芯
else if (currentCompletedCount < targetNormalCount)
{
await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
}
// 正常电芯已完成,进入假电芯补充模式
else if (currentCompletedCount == targetNormalCount && !stateForUpdate.IsInFakeBatteryMode)
{
// 首次进入假电芯模式,设置标志
stateForUpdate.IsInFakeBatteryMode = true;
_logger.LogInformation("HandlePutFinishedStateAsync:正常电芯抓取完成,进入假电芯补充模式,任务号: {TaskNum}", task.RobotTaskNum);
QuartzLogger.Info($"正常电芯抓取完成,进入假电芯补充模式", stateForUpdate.RobotCrane?.DeviceName);
}
// 如果处于假电芯补充模式,计算并下发补数任务
if (stateForUpdate.IsInFakeBatteryMode)
{
int remaining = targetTotal - currentCompletedCount;
if (remaining > 0)
{
// 计算每次抓取的数量(最多4个)
int pickCount = Math.Min(pickCountPerExecution, remaining);
// 获取可用的假电芯平面点位
var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(pickCount);
if (positions.Count == 0)
{
_logger.LogError("HandlePutFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum);
QuartzLogger.Error($"无可用假电芯点位", stateForUpdate.RobotCrane?.DeviceName);
return;
}
// 下发假电芯取货指令
await _taskProcessor.SendSocketRobotFakeBatteryPickAsync(task, stateForUpdate, positions);
}
else
{
// 假电芯补充完成,重置标志
stateForUpdate.IsInFakeBatteryMode = false;
_logger.LogInformation("HandlePutFinishedStateAsync:换盘任务完成,任务号: {TaskNum}", task.RobotTaskNum);
QuartzLogger.Info($"换盘任务完成", stateForUpdate.RobotCrane?.DeviceName);
}
}
}
在 RobotTaskProcessor 中添加:
/// <summary>
/// 获取下N个可用的假电芯平面点位
/// </summary>
/// <param name="count">需要获取的点位数量</param>
/// <returns>可用点位列表</returns>
public List<int> GetNextAvailableFakeBatteryPositions(int count)
{
return _fakeBatteryPositionService.GetNextAvailable(count);
}
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
git commit -m "feat(Robot): 实现换盘任务假电芯补充逻辑"
dotnet build WCS/WIDESEAWCS_Server/WIDESEAWCS_Server.sln
-- 初始化假电芯平面点位表(48个点位:3行×16列,行优先)
-- 第1行:1-16,第2行:17-32,第3行:33-48
IF NOT EXISTS (SELECT 1 FROM Dt_FakeBatteryPosition)
BEGIN
INSERT INTO Dt_FakeBatteryPosition (PositionIndex, Row, Col, IsUsed, Creater, CreateDate)
SELECT
((row - 1) * 16 + col) AS PositionIndex,
row AS Row,
col AS Col,
0 AS IsUsed,
'System' AS Creater,
GETDATE() AS CreateDate
FROM (SELECT 1 AS row UNION SELECT 2 UNION SELECT 3) AS rows
CROSS JOIN (SELECT 1 AS col UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12
UNION SELECT 13 UNION SELECT 14 UNION SELECT 15 UNION SELECT 16) AS cols
ORDER BY row, col;
END
Plan complete and saved to docs/superpowers/plans/2026-04-15-change-pallet-fake-battery.md. Two execution options:
1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration
2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints
Which approach?