using System;
using System.Diagnostics.CodeAnalysis;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_Tasks.StackerCraneJob;
namespace WIDESEAWCS_Tasks
{
///
/// 堆垛机命令构建器 - 封装任务到命令对象的转换与地址解析
///
///
/// 核心职责:
/// 1. 根据巷道类型选择命令格式(标准命令/成型命令)
/// 2. 构建入库、出库、移库任务的命令对象
/// 3. 解析任务地址(行-列-层格式)到命令坐标
/// 4. 查询路由信息,确定堆垛机的取货/放货站台
///
/// 地址格式约定:地址字符串格式为 "行-列-层",例如 "1-2-3" 表示第1行、第2列、第3层。
///
public class StackerCraneCommandBuilder
{
///
/// 任务服务
///
private readonly ITaskService _taskService;
///
/// 路由服务
///
private readonly IRouterService _routerService;
///
/// 堆垛机命令配置
///
private readonly StackerCraneCommandConfig _config;
///
/// 构造函数
///
/// 任务服务
/// 路由服务
/// 命令配置
public StackerCraneCommandBuilder(
ITaskService taskService,
IRouterService routerService,
StackerCraneCommandConfig config)
{
_taskService = taskService;
_routerService = routerService;
_config = config;
}
///
/// 将任务转换为堆垛机命令
///
///
/// 根据巷道类型选择不同的命令构建策略。
///
/// 任务对象
/// 堆垛机命令对象,转换失败返回 null
public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task)
{
// 根据巷道获取命令类型
string commandType = GetCommandType(task.Roadway);
// 根据命令类型调用相应的构建方法
return commandType switch
{
"Formation" => BuildCommand(task, CreateFormationCommand(task)), // 成型命令
_ => BuildCommand(task, CreateStandardCommand(task)) // 标准命令
};
}
///
/// 根据巷道获取命令类型
///
///
/// 遍历配置中的映射关系,找到匹配的命令类型。
/// 如果不匹配任何映射,返回默认命令类型。
///
/// 巷道编码
/// 命令类型(Standard 或 Formation)
private string GetCommandType(string roadway)
{
foreach (var mapping in _config.RoadwayCommandMapping)
{
if (roadway.Contains(mapping.Key))
{
return mapping.Value;
}
}
return _config.DefaultCommandType;
}
///
/// 创建标准命令
///
///
/// 用于标准堆垛机(GW、CW 开头巷道)。
///
/// 任务对象
/// 标准命令对象
private static StackerCraneTaskCommand CreateStandardCommand(Dt_Task task)
{
return new StackerCraneTaskCommand
{
TaskNum = task.TaskNum, // 任务号
WorkType = 1, // 作业类型
WorkAction = 1 // 作业指令:开始执行
};
}
///
/// 创建成型命令
///
///
/// 用于成型堆垛机(HC 开头巷道)。
/// 包含条码字段,用于电池追溯。
///
/// 任务对象
/// 成型命令对象
private static FormationStackerCraneTaskCommand CreateFormationCommand(Dt_Task task)
{
return new FormationStackerCraneTaskCommand
{
Barcode = task.PalletCode, // 托盘条码
TaskNum = task.TaskNum, // 任务号
WorkType = 1, // 作业类型
WorkAction = 1, // 作业指令:开始执行
FireAlarm = 0, // 火警:正常
HeartBeat = 0, // 心跳
FieldName = string.Empty // 保留字段
};
}
///
/// 构建命令(通用)
///
///
/// 根据任务类型(入库/出库/移库)调用相应的构建方法。
///
/// 命令类型
/// 任务对象
/// 初始命令对象
/// 填充好的命令对象
private T? BuildCommand(Dt_Task task, T command) where T : class
{
// 获取任务类型分组
TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup();
// 根据任务类型分发构建
return taskTypeGroup switch
{
TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), // 入库
TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), // 出库
TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), // 移库
_ => command // 未知类型,返回原命令
};
}
///
/// 构建入库命令
///
///
/// 入库任务需要:
/// 1. 查询堆垛机取货站台(根据当前地址和任务类型)
/// 2. 设置起始坐标(来自站台)
/// 3. 解析目标地址,设置终点坐标
///
/// 命令类型
/// 任务对象
/// 命令对象
/// 填充好的命令对象
private T? BuildInboundCommand(Dt_Task task, T command) where T : class
{
// 确定任务类型(空托盘用特殊类型 100)
int taskType = 0;
if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
{
taskType = 100;
}
else
taskType = task.TaskType;
// 查询堆垛机取货站台路由
Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, taskType);
if (router == null)
{
// 未找到站台,更新异常信息
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.CurrentAddress}】信息,无法获取对应的堆垛机取货站台信息");
return null;
}
// 设置起始坐标(来自路由配置)
SetCommandProperty(command, "StartRow", Convert.ToInt16(router.SrmRow));
SetCommandProperty(command, "StartColumn", Convert.ToInt16(router.SrmColumn));
SetCommandProperty(command, "StartLayer", Convert.ToInt16(router.SrmLayer));
// 解析目标地址(库位地址)
if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
{
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,终点:【{task.NextAddress}】");
return null;
}
// 设置终点坐标
SetCommandProperty(command, "EndRow", endRow);
SetCommandProperty(command, "EndColumn", endColumn);
SetCommandProperty(command, "EndLayer", endLayer);
return command;
}
///
/// 构建出库命令
///
///
/// 出库任务需要:
/// 1. 查询堆垛机放货站台(根据目标地址和任务类型)
/// 2. 设置终点坐标(来自站台)
/// 3. 解析起始地址,设置起点坐标
///
/// 命令类型
/// 任务对象
/// 命令对象
/// 填充好的命令对象
private T? BuildOutboundCommand(Dt_Task task, T command) where T : class
{
// 确定任务类型
int taskType = 0;
if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
{
taskType = 100;
}
else
taskType = task.TaskType;
// 查询堆垛机放货站台路由
Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, taskType);
if (router == null)
{
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.TargetAddress}】信息,无法获取对应的堆垛机放货站台信息");
return null;
}
// 设置终点坐标(来自路由配置)
SetCommandProperty(command, "EndRow", Convert.ToInt16(router.SrmRow));
SetCommandProperty(command, "EndColumn", Convert.ToInt16(router.SrmColumn));
SetCommandProperty(command, "EndLayer", Convert.ToInt16(router.SrmLayer));
// 解析起始地址(库位地址)
if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
{
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】");
return null;
}
// 设置起点坐标
SetCommandProperty(command, "StartRow", startRow);
SetCommandProperty(command, "StartColumn", startColumn);
SetCommandProperty(command, "StartLayer", startLayer);
return command;
}
///
/// 构建移库命令
///
///
/// 移库任务需要:
/// 1. 解析目标地址(新的库位)
/// 2. 解析起始地址(原来的库位)
/// 3. 设置起止坐标
///
/// 命令类型
/// 任务对象
/// 命令对象
/// 填充好的命令对象
private T? BuildRelocationCommand(Dt_Task task, T command) where T : class
{
// 解析目标地址
if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
{
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,终点:【{task.NextAddress}】");
return null;
}
// 设置终点坐标
SetCommandProperty(command, "EndRow", endRow);
SetCommandProperty(command, "EndColumn", endColumn);
SetCommandProperty(command, "EndLayer", endLayer);
// 解析起始地址
if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
{
_taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】");
return null;
}
// 设置起点坐标
SetCommandProperty(command, "StartRow", startRow);
SetCommandProperty(command, "StartColumn", startColumn);
SetCommandProperty(command, "StartLayer", startLayer);
return command;
}
///
/// 设置命令属性值(通过反射)
///
///
/// 使用反射动态设置命令对象的属性值。
///
/// 命令类型
/// 命令对象
/// 属性名称
/// 属性值
private static void SetCommandProperty(T command, string propertyName, object value) where T : class
{
var property = typeof(T).GetProperty(propertyName);
property?.SetValue(command, value);
}
///
/// 解析地址字符串
///
///
/// 地址格式:行-列-层,例如 "1-2-3" 表示第1行、第2列、第3层。
///
/// 地址字符串
/// 解析出的行坐标
/// 解析出的列坐标
/// 解析出的层坐标
/// 解析成功返回 true
private static bool TryParseAddress(string address, out short row, out short column, out short layer)
{
row = column = layer = 0;
// 按 "-" 分隔地址
string[] parts = address.Split('-');
if (parts.Length != 3)
{
return false;
}
// 解析各部分为 short 类型
return short.TryParse(parts[0], out row)
&& short.TryParse(parts[1], out column)
&& short.TryParse(parts[2], out layer);
}
}
}