fix: rebuild MES as compilable go-zero+ent backend (renamed bj_power_mes), restore 3 workstation projects from pristine original, align naming; all Go projects go build clean

This commit is contained in:
SunYF
2026-08-27 10:56:41 +08:00
parent 380715f8a9
commit 371b494c54
523 changed files with 67923 additions and 24758 deletions
@@ -0,0 +1,19 @@
package tool
// extractWorkpieceType 从参数中提取工件类型 ID
func extractWorkpieceType(params map[string]any) int {
return extractIntParam(params, "workpieceType")
}
// extractIntParam 从参数 map 中提取 int 类型值,支持 int/float64 转换
func extractIntParam(params map[string]any, key string) int {
if v, ok := params[key]; ok {
if n, ok := v.(int); ok {
return n
}
if f, ok := v.(float64); ok {
return int(f)
}
}
return 0
}
@@ -0,0 +1,20 @@
package tool
import (
"context"
)
// ToolCommand 手持工具命令
type ToolCommand struct {
Action string // 操作类型(SCAN/MARK
JobID int // 关联工件 ID
Params map[string]any // 操作参数
}
// HandheldTool 手持工具接口
type HandheldTool interface {
ID() string
Type() string
Name() string
Execute(ctx context.Context, cmd ToolCommand) error
}
@@ -0,0 +1,44 @@
package tool
import (
"context"
"testing"
"bj_power_mes/constants"
"github.com/stretchr/testify/assert"
)
func TestStationStatusValuesMatchPhase1Design(t *testing.T) {
assert.Equal(t, []string{"IDLE", "BUSY", "WAITING", "FAULT", "OFFLINE"}, constants.StationStatus("").Values())
}
func TestPositionTypeValuesIncludeBasicPositions(t *testing.T) {
values := constants.PositionType("").Values()
assert.Contains(t, values, "ON_EQUIPMENT")
assert.Contains(t, values, "ON_BUFFER")
assert.Contains(t, values, "ON_DOCK")
}
type fakeTool struct {
id string
typ string
name string
}
func (f *fakeTool) ID() string { return f.id }
func (f *fakeTool) Type() string { return f.typ }
func (f *fakeTool) Name() string { return f.name }
func (f *fakeTool) Execute(ctx context.Context, cmd ToolCommand) error { return nil }
func TestHandheldToolInterface(t *testing.T) {
tool := &fakeTool{
id: "scanner-1",
typ: "SCANNER",
name: "扫码器",
}
assert.Equal(t, "scanner-1", tool.ID())
assert.Equal(t, "SCANNER", tool.Type())
assert.Equal(t, "扫码器", tool.Name())
}
@@ -0,0 +1,139 @@
package tool
import (
"context"
"fmt"
"log/slog"
"net"
"sync"
"bj_power_mes/internal/robot"
)
// MarkerTool 激光打标机,实现 HandheldTool 接口。
// 通过 TCP 服务器接收激光打标机的文本请求,Execute 触发 PLC 信号后等待 TCP 交换完成。
type MarkerTool struct {
robotMgr *robot.Manager
mu sync.Mutex
markingText string
listener net.Listener
cancel context.CancelFunc
}
const defaultMarkerPort = 1000
// NewMarkerTool 创建激光打标机工具
func NewMarkerTool(robotMgr *robot.Manager) *MarkerTool {
ctx, cancel := context.WithCancel(context.Background())
t := &MarkerTool{
robotMgr: robotMgr,
cancel: cancel,
}
go t.startTCPServer(ctx, defaultMarkerPort)
return t
}
// ID 返回打标机唯一标识
func (t *MarkerTool) ID() string { return "laser-marker-1" }
// Type 返回工具类型编码
func (t *MarkerTool) Type() string { return "LASER" }
// Name 返回工具显示名称
func (t *MarkerTool) Name() string { return "激光打标机" }
// Execute 执行激光打标:设置标记文本 → 触发 PLC 信号 → 等待 TCP 交换完成(超时 10s)
func (t *MarkerTool) Execute(ctx context.Context, cmd ToolCommand) error {
wt := extractWorkpieceType(cmd.Params)
text, _ := cmd.Params["text"].(string)
if text == "" {
return fmt.Errorf("marking text is empty")
}
rc, exist := t.robotMgr.GetRobot()
if !exist || rc == nil {
return fmt.Errorf("robot controller not available")
}
t.mu.Lock()
t.markingText = text
t.mu.Unlock()
slog.Info("marker: start marking", "text", text)
if err := rc.StartMarking(ctx, wt); err != nil {
return fmt.Errorf("laser mark: %w", err)
}
slog.Info("marker: marking complete", "text", text)
return nil
}
// Close 关闭 TCP 服务器,释放资源
func (t *MarkerTool) Close() {
t.cancel()
if t.listener != nil {
t.listener.Close()
}
}
func (t *MarkerTool) startTCPServer(ctx context.Context, port int) {
ls, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
slog.Error("laser marker tcp listen failed", "err", err, "port", port)
return
}
t.listener = ls
slog.Info("laser marker tcp listen success", "port", port)
// 监听 ctx 取消,关闭 listener 使 Accept 返回
go func() {
<-ctx.Done()
ls.Close()
}()
for {
conn, err := ls.Accept()
if err != nil {
select {
case <-ctx.Done():
slog.Info("laser marker tcp server stopped")
return
default:
slog.Error("laser marker tcp accept failed", "err", err)
continue
}
}
slog.Info("laser marker tcp accept success", "port", port)
t.handleConn(conn)
}
}
func (t *MarkerTool) handleConn(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 100)
n, err := conn.Read(buf)
if err != nil {
slog.Error("laser marker tcp read failed", "err", err)
return
}
receiveStr := string(buf[:n])
slog.Info("marker station receive tcp request", "request", receiveStr)
if receiveStr == "TCP:Give me string" {
t.mu.Lock()
text := t.markingText
t.markingText = ""
t.mu.Unlock()
if _, err := conn.Write([]byte(text)); err != nil {
slog.Error("laser marker tcp write failed", "err", err)
} else {
slog.Info("laser marker tcp write success", "text", text)
}
}
}
@@ -0,0 +1,17 @@
package tool
import (
"testing"
)
func TestNewMarkerTool(t *testing.T) {
tool := NewMarkerTool(nil)
defer tool.Close()
if tool.ID() != "laser-marker-1" {
t.Errorf("expected laser-marker-1, got %s", tool.ID())
}
if tool.Type() != "LASER" {
t.Errorf("expected LASER, got %s", tool.Type())
}
}
@@ -0,0 +1,63 @@
package tool
import (
"context"
"fmt"
"log/slog"
"bj_power_mes/internal/robot"
)
// ScannerTool 扫码器,实现 HandheldTool 接口
// 通过 RobotManager 控制机器人执行扫码动作并读取扫码结果
type ScannerTool struct {
robotMgr *robot.Manager // 机器人管理器,用于控制扫码动作
}
// NewScannerTool 创建扫码器工具
func NewScannerTool(robotMgr *robot.Manager) *ScannerTool {
return &ScannerTool{robotMgr: robotMgr}
}
// ID 返回扫码器唯一标识
func (t *ScannerTool) ID() string { return "scanner-1" }
// Type 返回工具类型编码
func (t *ScannerTool) Type() string { return "SCANNER" }
// Name 返回工具显示名称
func (t *ScannerTool) Name() string { return "扫码器" }
// Execute 执行扫码操作:启动扫码→读取结果→清除数据
func (t *ScannerTool) Execute(ctx context.Context, cmd ToolCommand) error {
wt := extractWorkpieceType(cmd.Params)
round := 1
if v, ok := cmd.Params["round"]; ok {
if n, ok := v.(int); ok {
round = n
}
}
rc, exist := t.robotMgr.GetRobot()
if !exist || rc == nil {
return fmt.Errorf("robot controller not available")
}
slog.Info("scanner: start scan", "workpieceType", wt, "round", round)
if err := rc.StartScanCode(ctx, wt, round); err != nil {
return fmt.Errorf("start scan code: %w", err)
}
code, err := t.robotMgr.ReadScanCodeData(ctx)
if err != nil {
return fmt.Errorf("read scan code: %w", err)
}
if clearErr := t.robotMgr.ClearScanCodeData(ctx); clearErr != nil {
slog.Warn("scanner: failed to clear scan data", "error", clearErr)
}
slog.Info("scanner: scan complete", "code", code)
cmd.Params["scanCode"] = code
return nil
}