# 自动化生产线管控系统 — 框架流程说明 > 本文档描述框架级的自动化流程,不包含具体产线设备或工艺的业务细节。 > 具体业务内容(设备清单、工艺步骤、业务规则)见 `agent业务.md` 和 `doc/data.sql`。 > 全配置化流程引擎设计见 `流程文档.md`,引擎作为纯执行器,所有业务逻辑通过数据库配置驱动。 --- ## 软件架构核心概念 ### Actor(设备代理,纯内存) 每台物理设备对应一个 **MachineActor**(统一实现),通过 `Type` 字段适配不同设备类型。Actor 是 mutex 保护的同步状态结构体,由 EventLoop 同步调用。负责: - **管理槽位状态**:当前槽位上有无工件、哪个工件 - **处理 PLC 信号**:收到完成信号后,把槽位状态改为 DONE Actor 不写数据库、不调度、不操作 PLC。 ### PlcExecutor(PLC 信号握手) `PlcExecutor` 封装所有设备类型的 PLC 信号握手时序,通过方法名区分不同操作。负责: - **Load**:将工件放入设备槽位(写 Req → 等 Done → 复位 Req) - **Unload**:从设备槽位取出工件 - **Exchange**:同时取成品+放毛坯(仅支持 exchange 的设备类型) ### Recipe(工艺配方) 每种工件型号定义一份 Recipe(加工步骤有序列表)。每个步骤包含: - 工序类型(MACHINING / INSPECTION / BUFFER_STAGE / …) - 资源类型(指定需要的设备种类) - 默认下一步步骤编号 Recipe 是静态配置,启动时加载到内存。工件实例只记录当前步骤,不复制整份配方。 ### SignalRouter(信号路由,`actor/signal_router.go`) - 每秒轮询 PLC 输入寄存器,检测上升沿变化 - 根据信号找到对应 Actor,投递 SignalEvent 到 EventLoop - 信号是系统的输入,触发状态机迁移,但信号本身不是状态源 ### EventLoop(事件循环,`eventloop/loop.go`) 产线大脑,运行在独立 goroutine 中,**串行处理**所有生产事件: - 来自 PLC 的设备信号 → 更新 Actor 槽位状态 → 投递 MACHINE_DONE → 推进步骤 - 触发调度器 → 决策下一步动作 - 下发动作指令 → PlcWorker 执行 - 处理执行结果 → 推进 Job 步骤 → 触发下一轮调度 - **唯一写数据库的入口** ### Scheduler(调度器,`scheduler/`) 三层管道,只读不写: 1. **TaskGenerator**:根据 Job 当前步骤生成候选动作 2. **ConstraintFilter**:排除不可用设备、冲突、暂停工单等 3. **PolicyEngine**:按优先级排序,选出最优方案 一次调度只派遣一个 Worker 动作,确保机器人串行执行。 ### PlcWorker(PLC 执行器,`plc_worker.go`) 按 Action 类型调用 PlcExecutor 对应方法,执行 PLC 信号握手。 ### EventBus(事件总线,`eventbus/`) 组件间解耦通信,当前实现为内存 LocalBus。订阅者: - EventLogWriter:异步写入数据库,用于审计和恢复 - SSEBridge:推送到前端看板 - AlarmService:告警通知 --- ## 自动化流程(框架级) ### 阶段一:上料 ``` 操作员创建工单 → 系统创建 N 个 Job(工件实例) → 调度器发现 Job 就绪 → 下发上料动作 → PlcWorker → PlcExecutor.DockFetch → 搬运设备从接驳台取件 → 扫码 ``` **扫码结果分支:** - **成功** → 分配暂存台空闲槽位,状态推进 - **失败** → 放回原接驳台槽位,挂起,等待人工处理 ### 阶段二:加工 ``` 暂存台有料 → 调度器选择加工设备 → PlcWorker → PlcExecutor.GrinderPlace(上料) → 设备开始加工 → 加工完成 → PLC 发送完成信号 → SignalRouter 检测上升沿 → 投递 SignalEvent → EventLoop → MachineActor.MarkSlotDone → 槽位状态 PROCESSING → DONE → 调度器得知"有工件待取走" → PlcWorker → PlcExecutor.GrinderFetch(卸料) → 工件放回暂存台 ``` **Exchange 换料优化:** 支持 exchange 的设备可一次往返同时完成"取成品 + 放毛坯"。 ### 阶段三:检测 ``` 暂存台有料 → 调度器选择检测设备 → PlcWorker → PlcExecutor.FullCheckPlace(上料) → 设备开始检测 → 检测完成 → PLC 发送结果信号(OK / NG) → SignalRouter → EventLoop → MachineActor.MarkSlotDone → 判定结果分支: ├── OK → 推进到下一步工序 └── NG → 报废处理 ``` ### 阶段四:下料 ``` 暂存台有料 → 调度器决定去向 → PlcWorker → PlcExecutor.DockPlace(下料) → 搬运设备放到接驳台 → 送走 ``` --- ## 信号触发点(框架级) | 信号类型 | 触发时机 | 处理方 | 产生结果 | |----------|---------|--------|---------| | 设备完成信号 | 加工/检测完成 | EventLoop.handleMachineSignal → Actor.MarkSlotDone | 槽位状态 OCCUPIED → DONE,投递 MACHINE_DONE | | 调度触发 | 定时器/Worker完成 | EventLoop.trySchedule | 调度决策,派遣 Worker 动作 | 具体信号名称和地址见 `doc/signals.sql`。 --- ## 关键设计原则 1. **事件驱动,不预排任务** — 每个工件只记录当前步骤,下一步由事件触发实时决策 2. **PLC 信号是输入,不是状态源** — 系统以数据库中的状态为准,PLC 信号只用来触发状态迁移 3. **Actor 只管槽位和信号** — 不做调度决策,不写数据库,由 EventLoop 同步调用 4. **PlcExecutor 封装 PLC 握手** — 所有设备类型的 PLC 操作统一通过 PlcExecutor 方法实现,信号地址校验前置 5. **调度器只读不写** — 查询状态做决策,下发任务,不直接操作硬件或数据库 6. **EventLoop 是唯一写路径** — 所有生产状态变更必须经过 EventLoop → DBState 7. **防御式编程** — 所有错误必须有日志记录或向上抛出,禁止隐性失败 --- ## 技术栈 - 后端:Go 1.23.4 + go-zero v1.3.8 - 数据库:PostgreSQL + ent ORM - 缓存/Token 存储:Redis(可选,未配置时降级内存) - PLC 通信:Snap7(西门子 S7 协议) - 前端:React 19 + Semi Design + Zustand + Vite 7 ## 代码生成 - 生成 ent:`cd schema && go generate` - 生成 go-zero API:`cd apis && go generate` ## 全局规则 - 不要在 `handler` 写业务逻辑,保持薄层 - 不要手动修改 `ent/`、`cmd/control-api/internal/handler/routes.go`、`types/types.go`、goctl 生成的 handler - 所有 API 变更先改 `apis/*.api`,再生成代码 - 所有 schema 变更先改 `schema/`,再生成 ent