ExistedOperationMergedProcessGraph.equipmentOps(TreeMap<Long, ExistedOperation>)。
ExistedOperation 的工厂方法 of(Operation) 需要一个 domain Operation,而审查明确说「不要伪造一个完整生产 Operation」。必须有一个不依赖 domain Operation 但能放进 equipmentOps 的类型。
ExistedOperation.of() 包装。但审查明确禁止。
FixedOccupancy 实例。
EquipmentUnavailabilityLoader 产出的是 EquipmentUnavailabilityWindow(G1' 系统的输入)。G1' 被删后必须有一个 loader 产出 FixedOccupancy。SQL 逻辑完全一样,只是输出类型不同。
unavailability 包整体被删(G1' 全删),文件自然被重构到 operation 包。
FixedOccupancy.of() 正常创建、getEquipment/getOrders/toOperation 行为、无效输入拒绝。
MergedProcessGraph 中与 ExistedOperation 共存、空列表构造、evaluate 不抛异常。
EquipmentUnavailabilityLoader.load() + UnavailabilityProblemFacts 构造FixedOccupancyLoader.load()existedOps + fixedOccupancies merge 到 solution.existedOperations
.join(),且空集合会吞掉另一边的数据(之前 G1' 用 UnavailabilityProblemFacts 解决的就是这个问题)。merge 是最简单的方案。
equipmentUnavailabilityConstraint()(G1' 硬约束)及其相关 import。
OccupancySlice 与 EquipmentUnavailabilityWindow 的冲突,但那些类已经不存在了。必须删。
unavailabilityWindows、unavailabilityFacts、fixedOccupancies 三个字段。恢复为单一 existedOperations 集合。
.toSorted { it.operation.id } 改为 .toSorted { op -> (op instanceof FixedOccupancy) ? Long.MAX_VALUE : (op.operation?.id ?: Long.MAX_VALUE) }operation 属性,访问 it.operation.id 会 NPE
changedPrepareOp 加入前检查 !(nextOp instanceof FixedOccupancy)equipmentOps 而是单独维护一个 fixedOccupancyOps 映射,可以避免这两处改动。但那样要在 tryBuildCandidateOperations 中再写一套冲突检测循环——代码更多且与既有逻辑重复。当前 2 处 instanceof + 1 行 sort 是最小侵入。
unavailability 包的 scope 限定。清空了 jacocoTestCoverageVerification 的 violationRules。
EquipmentUnavailability.class、EquipmentUnavailabilityScoring.class 等文件。不删除会导致构建失败。95% 分支覆盖率的 rule 是针对这些已删除文件的,保留无意义。
hardPenalty()、occupancyBlocked()、windowsFromEquipmentState()、IntervalsOverlap()。被 MergedProcessGraph.tryBuildCandidateOperations 既有冲突检测替代。
processHardConstraint + makespanConstraint 替代——它们已经用 MergedProcessGraph.travel() 和 evaluatePenality() 处理包含 FixedOccupancy 的全量 operations。
FixedOccupancy 替代。
existedOperations 一个集合的方式替代——当集合为空时约束本来就不需要触发(无冲突可检测)。
EquipmentAssignment 解析为 OccupancySlice。不需要替换——冲突检测已内建于 MergedProcessGraph.travel()。
OccupancyResolver 实现,跑 MergedProcessGraph.travel() 再遍历 allOperations 产出 OccupancySlice。G1' 删除后无调用方。
EquipmentUnavailabilityWindow。SQL 逻辑完全相同,只是输出类型改为 FixedOccupancy。改名的同时也移到了 operation/ 包。
EquipmentUnavailability 的 occupancyBlocked、hardPenalty、windowsFromEquipmentState 等。这些功能已被 MergedProcessGraph 既有逻辑替代。
scoreUnavailabilityHard、hardPenaltyFromOccupancies。被 MergedProcessGraphFixedOccupancyTest 替代。
"设备状态应该拆成两个字段,一个是维护状态,一个是运行状态。分别用于排产和状态显示。现在混在在一块其实是有概念重合的。"
✅ 已分离:维护占用走 FixedOccupancy → MergedProcessGraph(排产),运行状态走 OPC UA 订阅链(展示)。Equipment.state 字段保留但不再承担运行状态职责。
"设备的维护状态可以直接抽象成一个 ExistedOperation 表示被占用即可。不需要再单独维护一套额外的逻辑。"
✅ 已实施:FixedOccupancy extends ExistedOperation,放入 MergedProcessGraph.equipmentOps,复用 tryBuildCandidateOperations 冲突检测。
删 G1' 评分体系 7 文件(-550 行),零额外评分逻辑。
"关于设备运行状态我觉得概念上要有,但是实际上不需要有这个字段在数据库。因为实际上这个状态是直接从设备 opcua 那读取到的"
✅ 已覆盖:运行状态概念保留,不新增 DB 字段(OPC UA 订阅链已有:OpcUaAdapter → DigitalTwinsWebsocketService / getEquipmentStatus)。Equipment.state 未删除——它一直是管理/维护状态字段,不是新增的。
EM repair/maint order
│
▼
EquipmentUnavailabilityLoader
│
▼
EquipmentUnavailabilityWindow ─┐
│ │
▼ ▼
EquipmentUnavailability UnavailabilityProblemFacts
.hardPenalty() │
.occupancyBlocked() ▼
│ G1' hard constraint
│ (equipmentUnavailabilityConstraint)
│
▼
MergedProcessGraph ←── ExistedOperation
.tryBuildCandidateOps() (生产历史, 平行路径)
两条平行路径:
→ G1' 评分体系(7文件, 550行)
→ ExistedOperation 冲突检测(既有)
EM repair/maint order
│
▼
FixedOccupancyLoader
│
▼
FixedOccupancy extends ExistedOperation
│
▼
MergedProcessGraph.equipmentOps
TreeMap<Long, ExistedOperation>
│
▼
tryBuildCandidateOperations()
检查 equipmentOps →
发现 FixedOccupancy →
StartPostponeException →
CH 自动避开
单一链路:
→ 所有 existing ops 走同一个入口
→ 0 新增评分代码
EquipmentUnavailabilityLoader.load()
→ List<EquipmentUnavailabilityWindow>
UnavailabilityProblemFacts(windows)
EquipmentUnavailabilityScoring
.scoreUnavailabilityHard()
→ TravelOccupancyResolver.resolve()
→ MergedProcessGraph.travel()
→ graph.getAllOperations()
→ List<OccupancySlice>
→ EquipmentUnavailability.hardPenalty()
→ occupancyBlocked() × N
→ intervalsOverlap() × N×M
→ int penalty
→ penalize ONE_HARD × penalty
FixedOccupancyLoader.load()
→ List<FixedOccupancy>
merge into existedOperations
→ List<ExistedOperation>
processHardConstraint:
MergedProcessGraph.of(res, existedOps)
→ 构造 equipmentOps
(包含 ExistedOperation + FixedOccupancy)
→ travel()
→ tryBuildCandidateOperations()
检查 equipmentOps →
发现 FixedOccupancy → StartPostponeException
或通过 → return changedPrepareOp
→ 成功 → hard=0
→ InvalidSolutionException → hard=1
以下文件均被修改或删除,但 Equipment.state 相关文件全部未动:
| 范围 | 文件 | 状态 |
|---|---|---|
| 核心模型 | Equipment.groovy (state 字段) | ✅ 未改 |
| 枚举 | EquipmentState.groovy | ✅ 未改 |
| CSV schema | DomainClassField.csv (所有租户) | ✅ 未改 |
| CSV schema | DynamicFormField_001.csv (所有租户) | ✅ 未改 |
| UI 表单 | RequestMap.csv (EquipmentState 端点) | ✅ 未改 |
| MES hook | mesEquipmentProductionStateSync.groovy | ✅ 未改 |
| MES hook | mesEquipmentValidate.groovy | ✅ 未改 |
| Hook 注册 | DynamicObjectHook.csv | ✅ 未改 |
| Hook 注册 | DynamicLogic_003_equipment_integration.csv | ✅ 未改 |
| 数据脚本 | markEquipment*.groovy (12 文件, 3 租户) | ✅ 未改 |
| EM 模型 | EquipmentProfile.groovy (productionState) | ✅ 未改 |
| EM csv | DomainClassField.csv (productionState 行) | ✅ 未改 |
| EM hook | equipmentProfileSync.groovy | ✅ 未改 |
| EM hook | equipmentProfileStatusSync.groovy | ✅ 未改 |
| EM 测试 | EquipmentHookBehaviorTest.groovy | ✅ 未改 |
| 实际改动 | ||
| 新增类 | FixedOccupancy.groovy | ➕ 86 行 |
| 新增 Loader | FixedOccupancyLoader.groovy | ➕ 84 行 |
| 新增测试 | FixedOccupancyTest.groovy | ➕ 98 行 |
| 新增测试 | MergedProcessGraphFixedOccupancyTest.groovy | ➕ 81 行 |
| 排产服务 | OperationPlanningService.groovy | 🔄 ~74 行 |
| 约束提供者 | EquipmentAssignmentConstraintProvider.java | 🔄 −23 行 |
| Solution | EquipmentAssignmentSolution.groovy | 🔄 −23 行 |
| 排产图 | MergedProcessGraph.groovy | 🔄 2 处 instanceof |
| 构建 | build.gradle | 🔄 −32 行 |
| G1' 系统 | EquipmentUnavailability*.java (7 文件) | ❌ 删除 |
| G1' 测试 | EquipmentUnavailability*Test.java (2 文件) | ❌ 删除 |
| 测试文件 | 测试数 | 覆盖内容 |
|---|---|---|
| FixedOccupancyTest | 14 | 构造/边界/invalid 输入/core 方法 |
| MergedProcessGraphFixedOccupancyTest | 7 | FixedOccupancy 在 graph 中共存/travel/evaluate |
| EquipmentIntegrationContractTest | 4 | state 字段存在、序列号验证、生产状态 hook 存在、alarm 枚举 |
| MesEquipmentHookBehaviorTest | 6 | state 同步 hook、序列号验证 |
| AlarmTest | 1 | 报警渲染 |
| mes-package 总计 | 29 | 全部 PASS |
| equipment-management 总计 | 135 | 全部 PASS |
| 风险 | 说明 | 级别 |
|---|---|---|
| TreeMap 同 start 覆盖 | equipmentOps 用 TreeMap, 同一设备同一 start 时间会覆盖。5 分钟 chunk 粒度概率低, 且 ExistedOperation 原本就有此行为。 | 继承 |
| SQL 无 ORDER BY | FixedOccupancyLoader 的 UNION ALL 无 ORDER BY, 同设备同 start 的多个维护区间顺序不确定。 | 低 |
| 空历史 + 仅维护 | constraint 从 existedOperations 集合获取数据。集合为空时 forEach 不产生 tuple, 约束不触发。但 CH phase 通过 tryBuildCandidateOperations 已避开冲突, LS 起始解是可行解。 | 低 |
eada799b simplify: remove ExistingOperationsState, merge lists directly d20fac3e fix: restore Equipment.state — removal was overreach 480b55d5 fix: PR #14 review findings (fail-fast on unmapped equipment) 420e6763 refactor: PR #14 equipment state refactor (original)
分析生成:2026-07-27 · base: f6af16b2 · head: eada799b
访问:https://soft-buttons-swim.loca.lt/pr14-change-analysis.html