# LMS 管芯托盘绑定与日计划定时调度设计 ## 1. 目标 在 LMS 中新增以“每根实体管芯”为粒度的管芯实例主表。PDA 将管芯绑定到 5×5 管芯托盘时,为每根管芯生成唯一虚拟管芯编号并登记规格、重量和托盘格位。日计划备货、套管定时器以管芯编号作为唯一业务对象,按当前顺序或权重策略逐根创建现场任务。 本期实现以下能力: - PDA 按已确认的 5×5 页面完成托盘扫描、管芯规格选择、重量录入和格位绑定。 - 管芯绑定时生成虚拟管芯编号,初始化管芯实例主表。 - 备货定时器优先从对接位抓取日计划需要的管芯;没有可抓取管芯时,才创建托盘 AGV 搬运任务。 - 套管定时器从管芯库逐根选择并预占目标规格管芯,创建管芯出库任务。 - 公共库存不提前归属日计划,轮到哪个日计划时才预占一根。 - 顺序、权重策略在下一次调度时立即生效;已经创建的现场任务不撤销。 - 备货和套管相互独立,任何一侧失败都可单独恢复。 ## 2. 核心原则 1. 一根实体管芯对应一条管芯主表记录和一个不可变的虚拟管芯编号。 2. 管芯主表保存规格、重量、生命周期、当前位置和预占关系;位置表、托盘表、任务表只关联管芯标识。 3. 每次定时器最多创建一根管芯对应的桁架抓取任务或管芯出库任务。 4. 同一轮备货调度如果只产生托盘 AGV 任务,则不再创建抓取任务。 5. 日计划进度只在现场业务动作成功后累计:AGV 托盘搬运完成不增加备货数量,桁架抓取进入管芯库后才增加已备货数量。 6. 同规格管芯可供多个日计划使用,但同一根管芯同一时刻只能被一张日计划和一个任务预占。 7. 管芯来源日计划只用于追溯,不构成永久归属;取消、结束或超量产生的可用管芯可被其他相同规格日计划复用。 ## 3. 数据模型 ### 3.1 管芯实例主表 新增 `lms_core_tube`,每根管芯一条记录: | 字段 | 含义 | |---|---| | `core_tube_id` | 内部雪花主键 | | `core_tube_code` | 虚拟管芯编号,租户内唯一且创建后不可修改 | | `material_id` | 管芯物料标识 | | `material_code` | 管芯物料编码快照 | | `material_name` | 管芯物料名称快照 | | `material_spec` | 管芯规格快照 | | `core_weight` | 管芯重量,使用 `decimal` | | `weight_unit` | 重量单位,默认 `kg` | | `stage` | 生命周期阶段 | | `status` | 可用及占用状态 | | `vehicle_code` | 当前所在管芯托盘号,可空 | | `row_num` | 当前托盘行号,可空 | | `col_num` | 当前托盘列号,可空 | | `current_location_type` | 备货缓存位、备货对接位、管芯库、设备、运输中等 | | `current_location_id` | 当前位置记录标识 | | `current_location_code` | 当前位置编码 | | `source_daily_plan_id` | 来源日计划,仅用于追溯 | | `reserved_daily_plan_id` | 当前预占日计划 | | `reserved_claim_token` | 当前日计划调度领取标识 | | `reserved_task_id` | 当前现场任务标识 | | `reserve_expire_time` | 预占过期时间 | | `version` | 乐观锁版本 | | 通用字段 | 创建人、创建时间、修改人、修改时间、逻辑删除、租户 | 重量使用 `decimal(12,3)`,必须为大于零的数值。同一租户下建立以下约束: - `core_tube_code` 唯一。 - 有效托盘格位 `vehicle_code + row_num + col_num` 唯一。 - `material_id + stage + status` 建立调度查询索引。 - `reserved_task_id` 建立任务回调索引。 ### 3.2 生命周期阶段 `stage` 使用字典 `lms_core_tube_stage`: - `1`:备货区 - `2`:备货抓取中 - `3`:管芯库 - `4`:管芯出库中 - `5`:套管中 - `6`:套管缓存位 - `7`:已消耗 - `8`:异常 `status` 使用字典 `lms_core_tube_status`: - `1`:可用 - `2`:已预占 - `3`:作业中 - `4`:已使用 - `5`:冻结 - `6`:异常 ### 3.3 托盘格位关系 `lms_base_papervehicle` 继续表示 5×5 托盘格位,但业务关联改为管芯标识: - 增加 `core_tube_id`、`core_tube_code`。 - 页面物料、规格和重量从管芯主表读取。 - 兼容旧数据字段,本期不破坏已有备货区域库存查询。 - 新绑定和后续任务不再将托盘明细中的物料字段作为管芯状态真值。 ## 4. PDA 管芯托盘绑定 ### 4.1 页面交互 复用此前已确认的 PDA 5×5 托盘绑定页面,不重新设计页面布局: 1. 扫描或输入管芯托盘号。 2. 选择管芯物料及规格。 3. 在 5×5 格位中选择待绑定位置。 4. 为每个位置录入管芯重量。 5. 提交后展示每个格位生成的虚拟管芯编号。 ### 4.2 后端绑定规则 一次提交可绑定一个或多个格位,后端在同一事务内逐项处理: 1. 校验托盘存在且位于允许人工绑定的备货缓存区域。 2. 校验格位范围为 1–5 行、1–5 列,重量大于零。 3. 校验格位当前未绑定有效管芯。 4. 使用现有编码生成能力为每根管芯生成 `core_tube_code`。 5. 插入管芯主表,阶段为“备货区”,状态为“可用”。 6. 写入 `lms_base_papervehicle` 的管芯标识、托盘号和行列。 7. 更新托盘所在备货点位为有托盘状态。 请求使用客户端请求号作为幂等标识。相同请求重复提交返回首次生成的管芯列表,不重复创建管芯。并发占用同一格位时由数据库唯一约束拒绝,整个批次回滚。 ## 5. 调度策略的单根轮转 现有日计划策略保持“备货”和“套管”两套租户级独立配置。现场任务每次只创建一根,因此调度领取数量固定为 1。 ### 5.1 顺序模式 始终从当前可执行计划中按 `sort_seq, daily_plan_id` 选择最前计划。当前计划暂时不可执行时,本轮允许跳过并继续检查后续不同规格计划;不会提前为后续计划永久分配库存。 ### 5.2 权重模式 策略记录当前轮转计划和该计划本轮已成功创建的单根任务次数。例如 A 权重 2、B 权重 3: ```text A → A → B → B → B → A ... ``` 只有成功创建管芯现场任务后才消耗一次权重。仅创建托盘 AGV 补货任务、打印缺货日志或候选跳过时不消耗该计划的权重。 切换策略、调整顺序或修改权重后,下一次调度重新读取最新配置并校验游标。模式切换时清零当前轮转次数;已经创建的任务不受影响。 ## 6. 备货定时器 新增 XXL-JOB 执行器 `dailyPlanStockingJob`。一次执行分为“直接抓取扫描”和“补充托盘”两阶段。 ### 6.1 第一阶段:跨规格寻找可直接抓取管芯 1. 查询订单进行中、备货进行中、已备货数量小于需求总量且无未完成单根备货任务的计划。 2. 按最新顺序或权重策略生成本轮候选顺序。 3. 按候选顺序逐张检查,每种物料规格在本轮只检查一次。 4. 查询备货对接位上是否存在该规格、状态可用且没有在途任务的管芯。 5. 存在时锁定具体管芯、托盘、行列和点位,创建一条桁架管芯抓取任务,本轮结束。 6. 当前规格不存在时,不立即移走对接位托盘;跳过该规格,继续检查后续不同规格日计划。 7. 同规格已有未完成备货托盘任务时,跳过该规格,避免重复补托盘。 例如对接位当前为 B 托盘,候选顺序为 A、B、C:先检查 A 并跳过,再检查 B,创建一根 B 的桁架抓取任务,不创建 B 托盘移出任务。 ### 6.2 第二阶段:所有候选均无法直接抓取时补充托盘 从候选中选择优先级最高、未处于托盘备货中的规格: 1. 对接位存在其他托盘时,选择可接收的空缓存位,只创建一条“对接位 → 缓存位”AGV 移出任务,本轮结束。 2. 对接位为空时,从缓存位查询包含目标规格管芯且没有在途任务的托盘。 3. 优先选择目标规格可用数量大于等于计划缺口且数量最接近缺口的托盘。 4. 没有单托盘可满足缺口时,选择目标规格可用数量最多的托盘。 5. 只创建一条“缓存位 → 对接位”AGV 移入任务,本轮结束。 6. 没有可用托盘时打印“备货区没有可用的{规格}管芯”,跳过该日计划。本轮可继续检查尚未处理的其他规格是否具备补货条件,但由于只有一个对接位,最多创建一条对接位相关 AGV 任务。 ### 6.3 防重复约束 - 同一规格只允许存在一条未完成的托盘备货任务。 - 同一托盘只允许存在一条未完成的 AGV 任务。 - 同一对接位只允许存在一条未完成的移入或移出任务。 - 同一管芯只允许存在一条未完成的桁架抓取任务。 - 任务创建使用确定性业务幂等标识;数据库条件更新管芯和点位状态后才能落任务,失败则事务回滚。 ### 6.4 完成与失败 - AGV 移入或移出成功:更新托盘与备货点位位置,不增加日计划备货数量。 - 桁架抓取成功:从托盘格位解绑管芯,将管芯阶段更新为“管芯库”、状态更新为“可用”,写入管芯库点位,并通过 Claim 将日计划已备货数量增加 1。 - 任务失败:释放对应管芯、托盘和点位占用,保留失败记录,下一轮允许重新调度。 - 已创建现场任务以任务状态为准,即使 Claim 到期也不能重复创建同业务任务。 ## 7. 套管定时器 新增 XXL-JOB 执行器 `dailyPlanSleevingJob`: 1. 查询订单进行中、套管进行中、已套管数量小于需求总量且无未完成单根套管任务的计划。 2. 按套管最新顺序或权重策略选择本轮计划。 3. 从管芯主表查询目标规格、阶段为“管芯库”、状态为“可用”的管芯。 4. 锁定其中一根,读取其管芯库点位,原子更新为“已预占”并关联日计划、Claim 和任务标识。 5. 创建一条管芯出库任务,本轮结束。 6. 找不到目标规格管芯时,查询该规格是否存在未完成的 AGV 补货或桁架抓取任务: - 存在:打印“{规格}管芯正在备货”。 - 不存在:打印“备货区{规格}管芯库存不足”。 7. 当前计划不可执行时释放本次无任务 Claim,并按策略规则在后续轮次继续调度。 管芯出库任务成功后更新管芯阶段为“套管中”,并将日计划已套管数量增加 1。任务失败时仅释放该管芯的套管预占,不影响备货任务。 ## 8. 任务创建和回调 现场任务使用现有 Task 模块任务能力,不新增日计划业务任务表: - 托盘 AGV 任务关联托盘号、来源点、目标点、日计划标识和目标规格。 - 桁架抓取任务关联管芯编号、托盘号、行列、来源对接位和目标管芯库点位。 - 管芯出库任务关联管芯编号、来源管芯库点位、日计划标识和套管目标。 - `bizType` 区分托盘移入、托盘移出、管芯抓取和管芯出库。 - `bizId` 使用稳定业务幂等键,任务模块中同一 `bizType + bizId` 只能存在一个未完结任务。 - LMS 回调处理器按照 `core_tube_code` 或托盘号更新业务状态;重复成功回调幂等返回,不重复累计日计划进度。 如现场具体 ACS 任务类型、桁架取放点参数尚未配置,使用配置项承载,不在代码中硬编码设备点位。 ## 9. 恢复与异常边界 1. 备货、套管定时器捕获单个候选的业务异常后记录明确日志,不能终止整次扫描。 2. 管芯、托盘、点位的预占必须先于任务创建并与任务落库保持一致;创建失败整体回滚。 3. 对已下发任务不因策略变化、日计划取消或 Claim 过期而重复下发。 4. 日计划取消后不再创建新任务;已执行中的任务允许完成业务回写,产出的未使用管芯回到公共可用库存。 5. 系统提供按业务幂等标识重新核对任务状态的恢复入口: - 任务不存在或明确失败时释放资源。 - 任务执行中时续期占用。 - 任务成功但业务未回写时补做幂等回调。 6. 备货恢复只处理托盘、抓取和备货进度;套管恢复只处理管芯出库和套管进度,两者互不修改对方状态。 ## 10. 日志与可观测性 XXL-JOB 日志至少输出: - 租户、调度类型、策略模式和候选计划。 - 跳过计划的原因,包括同规格已处理、已有在途任务、点位被占用、正在备货和库存不足。 - 选中的管芯编号、托盘号、来源点、目标点和任务标识。 - 未找到库存时的业务提示,不以系统异常结束 Job。 - 任务重复、资源竞争和回调恢复结果。 ## 11. 验收标准 1. PDA 在一个 5×5 托盘上绑定多根管芯后,每根管芯具有唯一编号、规格、重量和准确格位。 2. 相同请求重复提交或并发提交相同格位不会创建重复管芯。 3. 对接位存在某候选规格时,备货 Job 创建一根该规格的抓取任务,不创建托盘搬运任务。 4. 当前计划规格不存在时,Job 能跳过并寻找其他规格;同一轮每种规格只处理一次。 5. 所有候选均不可直接抓取时,一轮最多创建一条对接位 AGV 任务。 6. 同一规格不会出现多个未完成托盘备货任务,同一管芯不会出现多个未完成现场任务。 7. 桁架抓取成功后日计划备货数量只增加 1;AGV 完成不增加备货数量。 8. 套管 Job 每次只预占一根管芯并创建一条出库任务。 9. 套管无库存时能够准确区分“正在备货”和“备货区库存不足”。 10. 权重 2/3 的计划逐根执行结果为 A、A、B、B、B;策略修改从下一次任务选择生效。 11. 任一任务失败后可以独立释放或恢复,不影响另一类作业,也不会重复累计进度。 ## 12. 非本期范围 - 不设计新的 PDA 页面视觉方案,直接复用此前已确认页面。 - 不改变日计划订单、备货、套管人工状态机。 - 不实现拔轴机构和气胀轴优先复用流程。 - 不批量创建多根管芯现场任务。