本提交完成了多个核心模块的功能完善与业务对齐: 1. 工位终端:固定工位配置、移除自选工位逻辑、适配配置化工位号 2. MES核心:重构工位组合逻辑、新增工单/流程状态管理、补全报工/追溯逻辑 3. WMS客户端:新增修改密码功能、优化帮助弹窗逻辑 4. 文档与权限:补充完整操作手册、统一菜单名称与权限描述 5. 修复多业务校验:排产数量校验、工单状态校验、工位绑定规则
128 lines
3.4 KiB
Go
128 lines
3.4 KiB
Go
package tightening
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"math/rand/v2"
|
|
"net/url"
|
|
"time"
|
|
|
|
"bj_power_workstation/internal/mes"
|
|
"bj_power_workstation/internal/store"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
)
|
|
|
|
// Source 拧紧枪数据源接口。
|
|
type Source interface {
|
|
Start(ctx context.Context)
|
|
}
|
|
|
|
// MockSource 内置模拟拧紧枪数据源:每 6 秒生成一条拧紧结果插入本地 torque_results(synced=0)。
|
|
// SN 从最近工单任务中获取;拿不到则用 "MOCK-"+时间戳。
|
|
type MockSource struct {
|
|
db *store.Store
|
|
mes *mes.Client
|
|
dockCode string // 模拟枪挂载的工位
|
|
}
|
|
|
|
// NewMockSource 模拟枪挂载在固定工位(stationNo 1~12),DOCK 编码为 DOCK01~DOCK12。
|
|
func NewMockSource(db *store.Store, mc *mes.Client, stationNo int) *MockSource {
|
|
dockCode := fmt.Sprintf("DOCK%02d", stationNo)
|
|
return &MockSource{db: db, mes: mc, dockCode: dockCode}
|
|
}
|
|
|
|
func (m *MockSource) Start(ctx context.Context) {
|
|
logx.Infof("模拟拧紧枪已启动:每 6 秒采集一条,工位 %s", m.dockCode)
|
|
ticker := time.NewTicker(6 * time.Second)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
logx.Info("模拟拧紧枪已停止")
|
|
return
|
|
case <-ticker.C:
|
|
m.produceOne()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *MockSource) produceOne() {
|
|
sn, orderNo := m.fetchRecentTask()
|
|
if sn == "" {
|
|
sn = "MOCK-" + time.Now().Format("20060102150405")
|
|
orderNo = "MOCK-ORDER"
|
|
}
|
|
|
|
torque := round1(40 + rand.Float64()*12) // 40 ~ 52 Nm
|
|
angle := round0(100 + rand.Float64()*40) // 100 ~ 140°
|
|
screwNo := fmt.Sprintf("S%d", rand.IntN(10)+1) // S1 ~ S10
|
|
result := "OK" // OK 概率 90%
|
|
if rand.Float64() >= 0.9 {
|
|
result = "NG"
|
|
}
|
|
|
|
_, err := m.db.InsertTorqueResult(store.TorqueResult{
|
|
WorkOrderNo: orderNo,
|
|
Sn: sn,
|
|
DockCode: m.dockCode,
|
|
ScrewNo: screwNo,
|
|
Torque: torque,
|
|
Angle: angle,
|
|
Result: result,
|
|
Operator: "auto", // 终端自动采集
|
|
})
|
|
if err != nil {
|
|
logx.Errorf("模拟拧紧结果入库失败: %v", err)
|
|
return
|
|
}
|
|
logx.Infof("[模拟拧紧] sn=%s 工位=%s 螺丝=%s 扭矩=%.1fNm 角度=%.0f° 结果=%s",
|
|
sn, m.dockCode, screwNo, torque, angle, result)
|
|
}
|
|
|
|
// fetchRecentTask 从 MES 拉取当前工位最近任务,宽松解析其中的 sn/orderNo 字段,
|
|
// 任一环节失败均静默返回空串(保证离线时继续生成 MOCK 数据)。
|
|
func (m *MockSource) fetchRecentTask() (sn string, orderNo string) {
|
|
stationNo := 0
|
|
for _, ch := range m.dockCode {
|
|
if ch < '0' || ch > '9' {
|
|
stationNo = 0
|
|
break
|
|
}
|
|
stationNo = stationNo*10 + int(ch-'0')
|
|
}
|
|
query := url.Values{}
|
|
if stationNo > 0 {
|
|
query.Set("stationNo", fmt.Sprintf("%d", stationNo))
|
|
}
|
|
body, _, err := m.mes.Get("/api/internal/station/task", query)
|
|
if err != nil {
|
|
return "", ""
|
|
}
|
|
var wrap map[string]any
|
|
if json.Unmarshal(body, &wrap) != nil {
|
|
return "", ""
|
|
}
|
|
data, ok := wrap["data"].(map[string]any)
|
|
if !ok {
|
|
data = wrap // 兼容裸 JSON 响应
|
|
}
|
|
s, _ := data["sn"].(string)
|
|
o, _ := data["orderNo"].(string)
|
|
return s, o
|
|
}
|
|
|
|
func round1(v float64) float64 { return math.Round(v*10) / 10 }
|
|
func round0(v float64) float64 { return math.Round(v) }
|
|
|
|
// NetSource 丹尼科尔拧紧枪网络数据源占位。
|
|
// TODO 正式协议暂放:待拿到丹尼科尔协议文档后在此实现 TCP/UDP 报文解析。
|
|
type NetSource struct{}
|
|
|
|
var _ Source = (*NetSource)(nil)
|
|
|
|
func (n *NetSource) Start(ctx context.Context) {
|
|
<-ctx.Done()
|
|
} |