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
|
|||
|
|
}
|