feat: 五项目业务实现并对接完成
- MES(B): 工单/BOM/备料/工序字典/PLC下发/拧紧/扫码报工/半成品/AGV/追溯逻辑,WMS与海康RCS客户端,看板Redis缓存API(概览/设备/进度/报警/趋势)+SSE - WMS(C): JWT滑动续签、Excel导入、盘点、内部API、种子数据、独立Postgres配置 - WMS客户端(E): Go网关8891反向代理+内嵌Vue3十页 - 工位终端(D): SQLite本地缓存+模拟拧紧源+内嵌Vue3页面 - Dashboard(A): 看板数据改接MES内部缓存API,SSE实时刷新+vite代理 - 清理各项目球形磨遗留代码,新增部署手册.md
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
package plcproc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"bj_power_mes/ent"
|
||||
"bj_power_mes/internal/svc"
|
||||
|
||||
goplc "bjhardman.cn/bjhardman/goplc"
|
||||
)
|
||||
|
||||
/*
|
||||
PLC 工序号下发(S7-1214)。
|
||||
业务逻辑确定:产线共 12 道工序;将收齐的工序号拼成整型下发,
|
||||
例:下发 125 → PLC 执行 1 序、2 序、5 序。
|
||||
信号地址未定义前先用默认约定(配置可改):
|
||||
工序号写入:DB100.DBW0 (uint16)
|
||||
完成位读取:M100.0 (收齐后清复位)
|
||||
Mock.Enable 或 PLC 未连接时自动进入模拟模式,SimDoneSeconds 秒后确认完成。
|
||||
*/
|
||||
|
||||
// SendProcess 下发工序号并返回日志记录
|
||||
func SendProcess(ctx *svc.ServiceContext, orderNo, codeStr string) (*ent.PlcSendLog, error) {
|
||||
if codeStr == "" {
|
||||
return nil, fmt.Errorf("codeStr 必填,如 125 表示执行1序2序5序")
|
||||
}
|
||||
// 校验每一位都是 1~12 且不重复
|
||||
for _, ch := range codeStr {
|
||||
d := int(ch - '0')
|
||||
if d < 1 || d > 12 {
|
||||
return nil, fmt.Errorf("工序号 %c 超出范围(1~12)", ch)
|
||||
}
|
||||
}
|
||||
if len(codeStr) > 12 {
|
||||
return nil, fmt.Errorf("工序号数量超过 12 道")
|
||||
}
|
||||
|
||||
mode := "real"
|
||||
result := "已下发"
|
||||
var errText string
|
||||
|
||||
simMode := ctx.Config.Mock.Enable || !ctx.PLCManager.IsConnected()
|
||||
if simMode {
|
||||
mode = "sim"
|
||||
} else {
|
||||
plc := ctx.PLCManager.GetPlc()
|
||||
if plc == nil {
|
||||
return nil, fmt.Errorf("PLC 客户端不可用")
|
||||
}
|
||||
var code uint16
|
||||
fmt.Sscanf(codeStr, "%d", &code)
|
||||
if err := plc.WriteUint16(ctx.Config.Plc.ProcessAddr, code); err != nil {
|
||||
return nil, fmt.Errorf("写 PLC 失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
rec := ctx.EntClient.PlcSendLog.Create().
|
||||
SetCodeStr(codeStr).
|
||||
SetMode(mode).
|
||||
SetResult(result)
|
||||
if orderNo != "" {
|
||||
rec.SetOrderNo(orderNo)
|
||||
}
|
||||
if errText != "" {
|
||||
rec.SetErrorText(errText)
|
||||
}
|
||||
saveRec, err := rec.Save(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 模拟模式:延时后确认完成;真实模式:轮询完成位
|
||||
go waitAck(ctx, saveRec.ID, mode, plcRefOf(simMode, ctx))
|
||||
|
||||
slog.Info("plc process sent", "order", orderNo, "code", codeStr, "mode", mode)
|
||||
return saveRec, nil
|
||||
}
|
||||
|
||||
// plcRefOf 真实模式下返回 PLC 引用供后台轮询
|
||||
type plcHolder struct{ p goplc.Client }
|
||||
|
||||
func plcRefOf(sim bool, ctx *svc.ServiceContext) any {
|
||||
if sim {
|
||||
return nil
|
||||
}
|
||||
return &plcHolder{p: ctx.PLCManager.GetPlc()}
|
||||
}
|
||||
|
||||
// waitAck 后台等待 PLC 完成位
|
||||
func waitAck(ctx *svc.ServiceContext, logID int, mode string, holder any) {
|
||||
cfg := ctx.Config.Plc
|
||||
|
||||
done := false
|
||||
switch h := holder.(type) {
|
||||
case nil:
|
||||
time.Sleep(time.Duration(cfg.SimDoneSeconds) * time.Second)
|
||||
done = true
|
||||
case *plcHolder:
|
||||
if h.p != nil && h.p.IsConnected() {
|
||||
deadline := time.Now().Add(time.Duration(cfg.SimDoneSeconds+120) * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
b, err := h.p.ReadBool(cfg.DoneBitAddr)
|
||||
if err == nil && b {
|
||||
done = true
|
||||
_ = h.p.WriteBool(cfg.DoneBitAddr, false) // 收到后清复位
|
||||
break
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if done {
|
||||
ctx.EntClient.PlcSendLog.UpdateOneID(logID).
|
||||
SetResult("已确认").
|
||||
SetAckAt(now).
|
||||
ExecX(context.Background())
|
||||
} else if mode != "sim" {
|
||||
ctx.EntClient.PlcSendLog.UpdateOneID(logID).
|
||||
SetResult("超时未确认").
|
||||
ExecX(context.Background())
|
||||
} else {
|
||||
ctx.EntClient.PlcSendLog.UpdateOneID(logID).
|
||||
SetResult("已确认").
|
||||
SetAckAt(now).
|
||||
ExecX(context.Background())
|
||||
}
|
||||
}
|
||||
|
||||
func nillable(s string) *string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &s
|
||||
}
|
||||
Reference in New Issue
Block a user