Files
bj_power/bj_power_workstation/internal/tightening/tightening.go
T
SunYF 0ab21b1234 feat&refactor: 完成多模块功能迭代与配置优化
本次提交覆盖多个业务模块的功能完善与体验优化:
1.  **鉴权与配置调整**:
    - 统一JWT滑动续签逻辑,简化Token存储,移除RefreshToken相关冗余代码
    - 调整多项目配置文件中JWT过期时间为3600秒,统一会话闲置窗口
    - 工位配置放开1~12限制,改为仅校验大于0
2.  **术语统一替换**:全链路将"精密件"替换为"电气件",修正物料管理描述
3.  **功能新增**:
    - 新增工位类型、工艺路线与产线点位台账模块
    - 添加工艺PDF预览面板、工位终端代理转发接口
    - 新增操作日志按操作人列表筛选、工位登出日志记录
    - 新增PLC移料指令与产线点位状态管理
4.  **业务流程优化**:
    - 调整BOM物料删除校验逻辑,优化工单备料计算
    - 补充物料图号、检测单号等追溯字段
    - 完善工艺流程图与工位绑定关系说明
    - 优化前端页面文案与交互细节
5.  **代码规范与维护**:
    - 新增通用工具函数与前端静态资源
    - 整理路由权限与中间件逻辑
    - 修复部分接口与配置的不兼容问题
2026-09-10 16:59:15 +08:00

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 由 yaml 配置),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()
}