187 lines
4.7 KiB
Go
187 lines
4.7 KiB
Go
// mockrun: 使用 Mock PLC + Mock Marking 完整模拟订单执行流程。
|
||
// 用法: go run cmd/mockrun/main.go -jobs 4 -docks 1,2 -product 2
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"flag"
|
||
"fmt"
|
||
"log/slog"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
xlog "bj_power_mes/common/logx"
|
||
"bj_power_mes/constants"
|
||
"bj_power_mes/ent/job"
|
||
"bj_power_mes/ent/recipestep"
|
||
"bj_power_mes/internal/config"
|
||
"bj_power_mes/internal/svc"
|
||
|
||
"github.com/zeromicro/go-zero/core/conf"
|
||
)
|
||
|
||
var (
|
||
flagJobs = flag.Int("jobs", 12, "工件数量")
|
||
flagProduct = flag.Int("product", 1, "产品类型 ID")
|
||
flagStep = flag.Int("step", 0, "初始步骤索引(0=从第一步开始,4=从清洗机上料开始)")
|
||
flagTimeout = flag.Duration("timeout", 120*time.Minute, "超时时间")
|
||
)
|
||
|
||
func main() {
|
||
flag.Parse()
|
||
|
||
slog.Info("=== Mock 订单模拟启动 ===",
|
||
"jobs", *flagJobs, "product", *flagProduct)
|
||
|
||
// 1. 加载配置
|
||
var c config.Config
|
||
conf.MustLoad("etc/bj_power_mes-api.yaml", &c)
|
||
|
||
logger := xlog.NewLogger(c.Logger)
|
||
xlog.SetDefault(logger)
|
||
|
||
//if !c.Mock.Enable {
|
||
// fmt.Println("错误: Mock.Enable 必须为 true")
|
||
// os.Exit(1)
|
||
//}
|
||
|
||
c.Mock.MockRun = true
|
||
|
||
// 2. 创建 ServiceContext(Mock 模式下已自动清空旧数据)
|
||
fmt.Println("正在初始化 ServiceContext...")
|
||
svcCtx := svc.NewServiceContext(c)
|
||
time.Sleep(2 * time.Second)
|
||
|
||
ctx := context.Background()
|
||
|
||
// 3. 加载工艺路线
|
||
recipe, err := svcCtx.RecipeLoader.Load(ctx, *flagProduct)
|
||
if err != nil {
|
||
slog.Error("加载工艺路线失败", "error", err)
|
||
os.Exit(1)
|
||
}
|
||
fmt.Printf("工艺路线: %s (%d步)\n", recipe.RecipeCode, recipe.TotalSteps)
|
||
firstStepIdx := recipe.Steps[0].StepIndex
|
||
|
||
// 4. 创建工单 + 工件
|
||
order, err := svcCtx.EntClient.WorkOrder.Create().
|
||
SetProductTypeId(*flagProduct).
|
||
SetQuantity(*flagJobs).
|
||
SetStatus(constants.WorkOrderStatus_Created).
|
||
Save(ctx)
|
||
if err != nil {
|
||
slog.Error("创建工单失败", "error", err)
|
||
os.Exit(1)
|
||
}
|
||
fmt.Printf("工单已创建: id=%d\n", order.ID)
|
||
|
||
// 确定初始步骤索引
|
||
initialStepIdx := firstStepIdx
|
||
if *flagStep > 0 {
|
||
initialStepIdx = *flagStep
|
||
}
|
||
initialStepID := ""
|
||
if step, err := svcCtx.EntClient.RecipeStep.Query().
|
||
Where(recipestep.RecipeIdEQ(recipe.RecipeID), recipestep.StepIndexEQ(initialStepIdx)).
|
||
First(ctx); err == nil {
|
||
initialStepID = step.StepId
|
||
}
|
||
|
||
for i := 1; i <= *flagJobs; i++ {
|
||
dockNo := (i-1)/9 + 1
|
||
slotIdx := (i-1)%9 + 1
|
||
|
||
jobCreate := svcCtx.EntClient.Job.Create().
|
||
SetWorkOrderId(order.ID).
|
||
SetProductTypeId(*flagProduct).
|
||
SetRecipeID(recipe.RecipeID).
|
||
SetCurrentStepId(initialStepID)
|
||
|
||
if *flagStep > 0 && i <= 8 {
|
||
// 跳过前置步骤:工件在暂存台,状态为 Processing
|
||
jobCreate = jobCreate.
|
||
SetPositionType(constants.PositionType_OnBuffer).
|
||
SetPositionRefId(fmt.Sprintf("%d", i)).
|
||
SetTempSlotNo(i).
|
||
SetStatus(constants.JobStatus_Processing)
|
||
} else {
|
||
// 正常流程:工件在接驳台
|
||
jobCreate = jobCreate.
|
||
SetPositionType(constants.PositionType_OnDock).
|
||
SetPositionRefId(fmt.Sprintf("%d:%d", dockNo, slotIdx)).
|
||
SetDockNo(dockNo).
|
||
SetDockSlotNo(slotIdx).
|
||
SetStatus(constants.JobStatus_Created)
|
||
}
|
||
|
||
_, err = jobCreate.Save(ctx)
|
||
if err != nil {
|
||
slog.Error("创建工件失败", "id", i, "error", err)
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
|
||
if *flagStep > 0 {
|
||
fmt.Printf("已创建 %d 个工件(跳过前置步骤,从 step %d 开始,暂存台位置)\n", *flagJobs, *flagStep)
|
||
} else {
|
||
fmt.Printf("已创建 %d 个工件 \n",
|
||
*flagJobs)
|
||
}
|
||
|
||
// 6. 启动工单
|
||
if err := svcCtx.OrderProcessor.StartOrder(ctx, order.ID); err != nil {
|
||
slog.Error("启动工单失败", "error", err)
|
||
os.Exit(1)
|
||
}
|
||
slog.Info("工单已启动", "orderId", order.ID)
|
||
|
||
// 7. 轮询直到全部完成
|
||
deadline := time.Now().Add(*flagTimeout)
|
||
ticker := time.NewTicker(30 * time.Second)
|
||
defer ticker.Stop()
|
||
|
||
done := false
|
||
for !done && time.Now().Before(deadline) {
|
||
<-ticker.C
|
||
|
||
jobs, err := svcCtx.EntClient.Job.Query().
|
||
Where(job.WorkOrderIdEQ(order.ID)).
|
||
All(ctx)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
allDone := true
|
||
for _, j := range jobs {
|
||
if string(j.Status) != string(constants.JobStatus_Completed) &&
|
||
string(j.Status) != string(constants.JobStatus_Scrapped) {
|
||
allDone = false
|
||
}
|
||
}
|
||
if allDone {
|
||
done = true
|
||
}
|
||
}
|
||
|
||
if !done {
|
||
fmt.Println("超时!")
|
||
} else {
|
||
fmt.Println("\n===== 全部完成 =====")
|
||
}
|
||
fmt.Printf("共运行 %d 个工件\n", *flagJobs)
|
||
}
|
||
|
||
// parseIntList 解析逗号分隔的整数列表
|
||
func parseIntList(s string) []int {
|
||
parts := strings.Split(s, ",")
|
||
var result []int
|
||
for _, p := range parts {
|
||
p = strings.TrimSpace(p)
|
||
if n, err := strconv.Atoi(p); err == nil && n > 0 {
|
||
result = append(result, n)
|
||
}
|
||
}
|
||
return result
|
||
}
|