fix: rebuild MES as compilable go-zero+ent backend (renamed bj_power_mes), restore 3 workstation projects from pristine original, align naming; all Go projects go build clean
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package eventloop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"bj_power_mes/constants"
|
||||
"bj_power_mes/ent"
|
||||
"bj_power_mes/ent/equipmentslot"
|
||||
"bj_power_mes/ent/job"
|
||||
"bj_power_mes/ent/pallet"
|
||||
"bj_power_mes/ent/workorder"
|
||||
)
|
||||
|
||||
// RecoverOnStartup 启动时执行保守人工确认恢复。
|
||||
// 先暂停所有工单再校验一致性——避免启动时未经验证的状态被调度器消费。
|
||||
// 1. 将所有 IN_PROGRESS 的 work_order 改为 PAUSED
|
||||
// 2. 校验 temp_slot_no 一致性(重复/越界)
|
||||
// 3. 校验 equipment_slot 与 job 位置一致性
|
||||
// 4. 不一致项创建 manual_action 等待人工确认
|
||||
func (d *DBState) RecoverOnStartup(ctx context.Context) (recoveredJobs int, pendingRecoveryOrderIDs []int, manualActions []*ent.ManualAction, err error) {
|
||||
// 逐个检查 IN_PROGRESS/PAUSING 订单:有 ON_EQUIPMENT 活跃工件的保持原状等待人工确认
|
||||
activeOrders, _ := d.client.WorkOrder.Query().
|
||||
Where(workorder.StatusIn(constants.WorkOrderStatus_InProgress, constants.WorkOrderStatus_Pausing)).
|
||||
All(ctx)
|
||||
var pendingOrderIDs []int
|
||||
for _, o := range activeOrders {
|
||||
onEquipCount, _ := d.client.Job.Query().
|
||||
Where(
|
||||
job.WorkOrderIdEQ(o.ID),
|
||||
job.StatusNotIn(constants.JobStatus_Completed, constants.JobStatus_Scrapped),
|
||||
job.PositionTypeEQ(constants.PositionType_OnEquipment),
|
||||
).
|
||||
Count(ctx)
|
||||
if onEquipCount > 0 {
|
||||
// 在设备中的工件需要人工确认,保持 IN_PROGRESS
|
||||
pendingOrderIDs = append(pendingOrderIDs, o.ID)
|
||||
slog.Info("recovery: order kept IN_PROGRESS for position confirmation", "orderId", o.ID)
|
||||
} else {
|
||||
// 没有设备中工件,安全切换为 PAUSED
|
||||
_, _ = d.client.WorkOrder.UpdateOneID(o.ID).
|
||||
SetStatus(constants.WorkOrderStatus_Paused).
|
||||
Save(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
jobs, err := d.GetActiveJobs(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
||||
// 构建 jobID→orderID 映射,用于 ManualAction 关联工单
|
||||
jobOrder := make(map[int]int, len(jobs))
|
||||
for _, j := range jobs {
|
||||
jobOrder[j.ID] = j.WorkOrderId
|
||||
}
|
||||
|
||||
createMA := func(jobID int, desc string) {
|
||||
ma, _ := d.CreateManualAction(ctx, constants.ManualActionType_RecoveryRequired, jobID, jobOrder[jobID], desc)
|
||||
if ma != nil {
|
||||
manualActions = append(manualActions, ma)
|
||||
}
|
||||
}
|
||||
|
||||
capacity := d.TempSlotCapacity(ctx)
|
||||
slotUsers := make(map[int][]int)
|
||||
for _, j := range jobs {
|
||||
if j.TempSlotNo != nil {
|
||||
slot := *j.TempSlotNo
|
||||
if slot < 1 || slot > capacity {
|
||||
createMA(j.ID, fmt.Sprintf("temp_slot_no %d out of range [1,%d]", slot, capacity))
|
||||
continue
|
||||
}
|
||||
slotUsers[slot] = append(slotUsers[slot], j.ID)
|
||||
}
|
||||
}
|
||||
|
||||
for slot, users := range slotUsers {
|
||||
if len(users) > 1 {
|
||||
slog.Warn("recovery: duplicate temp slot", "slot", slot, "jobs", users)
|
||||
for _, jID := range users {
|
||||
createMA(jID, fmt.Sprintf("duplicate temp_slot_no %d", slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, j := range jobs {
|
||||
if j.PositionType == constants.PositionType_OnEquipment {
|
||||
equipID, slotNo := parsePositionRef(j.PositionRefId)
|
||||
if equipID == 0 {
|
||||
continue
|
||||
}
|
||||
slot, slotErr := d.client.EquipmentSlot.Query().
|
||||
Where(equipmentslot.EquipmentIdEQ(equipID), equipmentslot.SlotNoEQ(slotNo)).
|
||||
First(ctx)
|
||||
if slotErr != nil {
|
||||
slog.Warn("recovery: slot not found", "equip", equipID, "slot", slotNo)
|
||||
createMA(j.ID, fmt.Sprintf("equipment slot %d:%d not found", equipID, slotNo))
|
||||
continue
|
||||
}
|
||||
cjid := 0
|
||||
if slot.CurrentJobId != nil {
|
||||
cjid = *slot.CurrentJobId
|
||||
}
|
||||
if cjid != j.ID {
|
||||
createMA(j.ID, fmt.Sprintf("equipment_slot %d:%d has job %d, expected %d", equipID, slotNo, cjid, j.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pallet 状态推导
|
||||
pallets, pErr := d.client.Pallet.Query().
|
||||
Where(pallet.StatusIn(constants.PalletStatus_PendingProcess, constants.PalletStatus_Processing)).
|
||||
All(ctx)
|
||||
if pErr != nil {
|
||||
slog.Warn("recovery: query pallets failed", "error", pErr)
|
||||
} else {
|
||||
for _, p := range pallets {
|
||||
palletPred := job.HasPalletWith(pallet.IDEQ(p.ID))
|
||||
activeCount, cErr := d.client.Job.Query().
|
||||
Where(
|
||||
palletPred,
|
||||
job.StatusNotIn(constants.JobStatus_Completed, constants.JobStatus_Scrapped),
|
||||
).
|
||||
Count(ctx)
|
||||
if cErr != nil {
|
||||
continue
|
||||
}
|
||||
if activeCount == 0 {
|
||||
// 全部终态 → COMPLETED
|
||||
totalJobs, _ := d.client.Job.Query().Where(job.HasPalletWith(pallet.IDEQ(p.ID))).Count(ctx)
|
||||
scrappedCount, _ := d.client.Job.Query().
|
||||
Where(job.HasPalletWith(pallet.IDEQ(p.ID)), job.StatusEQ(constants.JobStatus_Scrapped)).
|
||||
Count(ctx)
|
||||
now := time.Now()
|
||||
_, err := d.client.Pallet.UpdateOneID(p.ID).
|
||||
SetStatus(constants.PalletStatus_Completed).
|
||||
SetPassQty(totalJobs - scrappedCount).
|
||||
SetNgQty(scrappedCount).
|
||||
SetCompletedAt(now).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
slog.Warn("recovery: complete pallet failed", "palletId", p.ID, "error", err)
|
||||
} else {
|
||||
slog.Info("recovery: pallet completed", "palletId", p.ID)
|
||||
}
|
||||
}
|
||||
// 存在非终态 Job → 保持不变,跟随 Job 恢复流程
|
||||
}
|
||||
}
|
||||
return len(jobs), pendingOrderIDs, manualActions, nil
|
||||
}
|
||||
Reference in New Issue
Block a user