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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user