初始化
This commit is contained in:
@@ -1,289 +0,0 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"bj_power_mes/constants"
|
||||
"bj_power_mes/ent"
|
||||
"bj_power_mes/internal/alarm"
|
||||
"bj_power_mes/internal/config"
|
||||
"bj_power_mes/internal/db"
|
||||
"bj_power_mes/internal/eventbus"
|
||||
"bj_power_mes/internal/eventlog"
|
||||
"bj_power_mes/internal/mock"
|
||||
"bj_power_mes/internal/plc"
|
||||
"bj_power_mes/internal/preload"
|
||||
"bj_power_mes/internal/processor"
|
||||
"bj_power_mes/internal/processor/eventloop"
|
||||
"bj_power_mes/internal/processor/scheduler"
|
||||
"bj_power_mes/internal/processor/tool"
|
||||
"bj_power_mes/internal/robot"
|
||||
"bj_power_mes/internal/sse"
|
||||
"bj_power_mes/internal/wmsstock"
|
||||
|
||||
"bj_power_mes/internal/hikrcs"
|
||||
|
||||
"bjhardman.cn/bjhardman/mom"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
RedisClient *redis.Redis
|
||||
EntClient *ent.Client
|
||||
DB *sql.DB
|
||||
SSEHandler *sse.Handler
|
||||
PLCManager *plc.Manager
|
||||
RobotManager *robot.Manager
|
||||
RecipeLoader *processor.RecipeLoader
|
||||
OrderProcessor processor.OrderProcessorInterface
|
||||
EventLoop *eventloop.ProductionEventLoop
|
||||
EventBus eventbus.Bus
|
||||
EventLogWriter *eventlog.EventLogWriter
|
||||
SSEBridge *sse.EventBridge
|
||||
RecoveryCoordinator *eventloop.RecoveryCoordinator
|
||||
Mom *mom.Mom
|
||||
EquipUploader *processor.EquipUploadService
|
||||
WmsClient *wmsstock.Client
|
||||
AgvClient *hikrcs.Client
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
rc := redis.MustNewRedis(c.Redis)
|
||||
entClient, dbConn := db.MustNewDB(c.Database)
|
||||
//tools.EnsureDataInitialized(context.Background(), entClient)
|
||||
|
||||
preload.Init(entClient)
|
||||
preload.InitProductTypeCategories(entClient)
|
||||
// Q9 种子数据:12 道工序字典(表为空时才写入)
|
||||
preload.SeedProcessDict(entClient)
|
||||
|
||||
// 事件总线:始终 LocalBus(单进程模式,不再支持 RedisBus)
|
||||
bus := eventbus.NewLocalBus()
|
||||
slog.Info("svc: using LocalBus for event bus")
|
||||
|
||||
alarmSvc := alarm.NewService(entClient, bus)
|
||||
|
||||
sseHandler := sse.NewHandler()
|
||||
|
||||
var plcManager *plc.Manager
|
||||
if c.Mock.Enable {
|
||||
plcManager = plc.NewManager(mock.NewMockPLC())
|
||||
} else {
|
||||
plcManager = plc.Build(c.Plc, bus)
|
||||
}
|
||||
|
||||
// Mock 模式:清空旧数据,避免恢复逻辑干扰
|
||||
if c.Mock.MockRun {
|
||||
ctx := context.Background()
|
||||
entClient.Job.Delete().ExecX(ctx)
|
||||
entClient.WorkOrder.Delete().ExecX(ctx)
|
||||
slots, _ := entClient.EquipmentSlot.Query().All(ctx)
|
||||
for _, s := range slots {
|
||||
entClient.EquipmentSlot.UpdateOneID(s.ID).
|
||||
SetStatus(constants.SlotStatus_Empty).
|
||||
ClearCurrentJobId().
|
||||
ClearOccupiedAt().
|
||||
ExecX(ctx)
|
||||
}
|
||||
dockSlots := entClient.DockSlot.Query().AllX(ctx)
|
||||
for _, d := range dockSlots {
|
||||
entClient.DockSlot.UpdateOneID(d.ID).
|
||||
SetStatus(0).
|
||||
ClearCurrentJobId().
|
||||
ClearWorkOrderId().
|
||||
ClearWorkpieceTypeId().
|
||||
ExecX(ctx)
|
||||
}
|
||||
slog.Info("svc: mock mode, cleared old data")
|
||||
}
|
||||
|
||||
robotManager := robot.MustNewManager(entClient, plcManager, sseHandler, alarmSvc)
|
||||
|
||||
robotCtrl, robotExist := robotManager.GetRobot()
|
||||
if !robotExist {
|
||||
slog.Warn("svc: robot not available, stations will run without robot actions")
|
||||
}
|
||||
|
||||
dbState := eventloop.NewDBState(entClient)
|
||||
scanner := tool.NewScannerTool(robotManager)
|
||||
marker := tool.NewMarkerTool(robotManager)
|
||||
recipeLoader := processor.NewRecipeLoader(entClient)
|
||||
sched := scheduler.DefaultScheduler()
|
||||
|
||||
// 提前声明供 Actor 的 OnDone/OnInspect 闭包捕获(EventLoop 随后创建)
|
||||
var eventLoop *eventloop.ProductionEventLoop
|
||||
|
||||
line, err := processor.BuildProductionLine(context.Background(), entClient, dbState, plcManager.GetPlc(), robotCtrl,
|
||||
func(machineID, slotNo, jobID int) {
|
||||
if eventLoop != nil {
|
||||
eventLoop.Send(eventloop.EventLoopMessage{
|
||||
ID: fmt.Sprintf("actor-done-%d-%d", machineID, time.Now().UnixNano()),
|
||||
Type: eventloop.EvtMachineDone,
|
||||
Payload: map[string]any{
|
||||
"machineId": machineID,
|
||||
"slotNo": slotNo,
|
||||
"jobId": jobID,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
func(machineID int, pass bool) {
|
||||
if eventLoop != nil {
|
||||
eventLoop.Send(eventloop.EventLoopMessage{
|
||||
ID: fmt.Sprintf("actor-insp-%d-%d", machineID, time.Now().UnixNano()),
|
||||
Type: eventloop.EvtMachineDone,
|
||||
Payload: map[string]any{
|
||||
"machineId": machineID,
|
||||
"inspectionPass": pass,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
func(machineID int) {
|
||||
if eventLoop != nil {
|
||||
eventLoop.SendScheduleTick()
|
||||
}
|
||||
},
|
||||
sseHandler,
|
||||
)
|
||||
if err != nil {
|
||||
slog.Error("svc: build production line failed", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
orderProcessor := processor.NewJobProcessor(
|
||||
entClient, line.Actors, bus, scanner, marker, robotCtrl, robotManager, line.TempStore,
|
||||
)
|
||||
worker := processor.NewRobotWorker(entClient, robotCtrl, line.Actors, line.Stations, scanner, marker)
|
||||
|
||||
// MOM 客户端初始化(必须在 EventLoop 构造之前)
|
||||
var momClient *mom.Mom
|
||||
if c.Mom.Enable {
|
||||
var err error
|
||||
momClient, err = mom.New(c.Mom.MomConf)
|
||||
if err != nil {
|
||||
slog.Error("svc: mom init failed", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DockStateReader adapter
|
||||
var dockReader eventloop.DockStateReader
|
||||
if robotCtrl != nil {
|
||||
dockReader = dockStateAdapter{ctrl: robotCtrl}
|
||||
}
|
||||
|
||||
momBiz := eventloop.MomBizConfig{
|
||||
InstoreType: c.Mom.InstoreType,
|
||||
}
|
||||
|
||||
eventLoop = eventloop.NewProductionEventLoop(
|
||||
entClient, worker, line.Actors, line.TempStore, sched,
|
||||
bus, orderProcessor, orderProcessor.GetReplenisher(),
|
||||
plcManager.GetPlc(),
|
||||
momClient, c.Mom.MomConf, momBiz, dockReader,
|
||||
)
|
||||
eventLoop.SetOnDockChange(func() {
|
||||
sseHandler.Emit(sse.EventDockDataUpdate, "{}")
|
||||
})
|
||||
orderProcessor.SetEventLoop(eventLoop)
|
||||
|
||||
go line.SignalRouter.Run(context.Background(), 1*time.Second)
|
||||
recoveryCoordinator := eventloop.NewRecoveryCoordinator(entClient, bus, orderProcessor)
|
||||
|
||||
if err := bus.Start(context.Background()); err != nil {
|
||||
slog.Error("svc: bus start failed", "error", err)
|
||||
}
|
||||
|
||||
// 事件日志写入器
|
||||
eventLogWriter := eventlog.NewEventLogWriter(entClient, 20, 1000)
|
||||
eventLogWriter.Start(context.Background())
|
||||
|
||||
bus.SubscribeAll(func(ctx context.Context, evt eventbus.Event) error {
|
||||
eventLogWriter.Write(evt)
|
||||
return nil
|
||||
})
|
||||
|
||||
// SSE 桥接器
|
||||
sseBridge := sse.NewEventBridge(bus, sseHandler)
|
||||
|
||||
// 启动恢复
|
||||
recovered, pendingOrderIDs, manualActions, err := eventloop.NewDBState(entClient).RecoverOnStartup(context.Background())
|
||||
if err != nil {
|
||||
slog.Error("svc: startup recovery failed", "error", err)
|
||||
} else {
|
||||
slog.Info("svc: startup recovery complete", "recoveredJobs", recovered, "pendingRecovery", len(pendingOrderIDs), "manualActions", len(manualActions))
|
||||
eventLoop.SetRecoveryPending(pendingOrderIDs)
|
||||
// 通知前端:启动时发现的不一致项需要人工确认
|
||||
for _, ma := range manualActions {
|
||||
orderID := 0
|
||||
if ma.OrderId != nil {
|
||||
orderID = *ma.OrderId
|
||||
}
|
||||
bus.Publish(context.Background(), eventbus.NewManualActionCreatedEvent(
|
||||
ma.ID, string(ma.ActionType), orderID,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// 从 DB 恢复所有 Actor 槽位状态(设备 + 暂存台)
|
||||
eventLoop.InitActorsFromDB(context.Background())
|
||||
|
||||
// 启动事件循环
|
||||
go eventLoop.Run(context.Background())
|
||||
|
||||
// 设备数据每日上传任务(仅 MOM 启用时)
|
||||
var equipUploader *processor.EquipUploadService
|
||||
if momClient != nil {
|
||||
equipUploader = processor.NewEquipUploadService(
|
||||
dbConn, momClient, c.Mom.EquipUploadHour, c.Mom.EquipUploadDeviceTypes,
|
||||
)
|
||||
equipUploader.Start(context.Background())
|
||||
}
|
||||
|
||||
slog.Info("service context: initialized",
|
||||
"mock", c.Mock.Enable,
|
||||
"stations", len(line.Actors),
|
||||
)
|
||||
|
||||
svcCtx := &ServiceContext{
|
||||
Config: c,
|
||||
SSEHandler: sseHandler,
|
||||
RedisClient: rc,
|
||||
EntClient: entClient,
|
||||
DB: dbConn,
|
||||
PLCManager: plcManager,
|
||||
RobotManager: robotManager,
|
||||
RecipeLoader: recipeLoader,
|
||||
OrderProcessor: orderProcessor,
|
||||
EventBus: bus,
|
||||
EventLogWriter: eventLogWriter,
|
||||
SSEBridge: sseBridge,
|
||||
EventLoop: eventLoop,
|
||||
RecoveryCoordinator: recoveryCoordinator,
|
||||
Mom: momClient,
|
||||
EquipUploader: equipUploader,
|
||||
WmsClient: wmsstock.New(c.Wms.BaseURL, c.Wms.Token),
|
||||
}
|
||||
if !c.AgvRcs.Sim && c.AgvRcs.BaseURL != "" {
|
||||
svcCtx.AgvClient = hikrcs.New(c.AgvRcs.BaseURL, c.AgvRcs.AppKey, c.AgvRcs.AppSecret, c.AgvRcs.Version)
|
||||
}
|
||||
|
||||
return svcCtx
|
||||
}
|
||||
|
||||
// dockStateAdapter 将 robot.Controller 适配为 DockStateReader
|
||||
type dockStateAdapter struct {
|
||||
ctrl *robot.Controller
|
||||
}
|
||||
|
||||
func (a dockStateAdapter) ReadDockStates(ctx context.Context) ([]bool, error) {
|
||||
if a.ctrl == nil {
|
||||
return nil, fmt.Errorf("controller not available")
|
||||
}
|
||||
return a.ctrl.DockStates()
|
||||
}
|
||||
Reference in New Issue
Block a user