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
+154
View File
@@ -0,0 +1,154 @@
package camera
import (
"bufio"
"bytes"
"context"
"fmt"
"net"
"sync"
"time"
"github.com/zeromicro/go-zero/core/logx"
)
// 命令常量
const (
CmdTakePhoto = "T1"
CmdGetDetectionResult = "GM,2,0"
)
type Client struct {
addr string
conn net.Conn
timeout time.Duration
mu sync.Mutex
}
func NewClient(addr string, timeout ...time.Duration) *Client {
t := 1 * time.Second
if len(timeout) > 0 {
t = timeout[0]
}
return &Client{
addr: addr,
timeout: t,
}
}
func (c *Client) Connect() error {
logx.Infof("正在连接相机: %s", c.addr)
conn, err := net.DialTimeout("tcp", c.addr, c.timeout)
if err != nil {
logx.Errorf("相机连接失败: %v", err)
return fmt.Errorf("相机连接失败: %w", err)
}
c.conn = conn
logx.Infof("相机连接成功: %s", c.addr)
return nil
}
func (c *Client) ConnectWithRetry(maxRetries int, retryInterval time.Duration) error {
var err error
for i := 0; i < maxRetries; i++ {
err = c.Connect()
if err == nil {
return nil
}
logx.Errorf("连接失败,%v后重试...", retryInterval)
time.Sleep(retryInterval)
}
return fmt.Errorf("连接相机失败: %w", err)
}
func (c *Client) IsConnected() bool {
return c.conn != nil
}
func (c *Client) Reconnect() error {
c.Close()
return c.Connect()
}
func (c *Client) SendCommand(cmd string) ([]byte, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.conn == nil {
return nil, fmt.Errorf("未建立连接")
}
logx.Infof("发送相机指令: %s", cmd)
_, err := c.conn.Write([]byte(cmd + "\r"))
if err != nil {
logx.Errorf("发送指令失败: %v", err)
return nil, fmt.Errorf("发送指令失败: %w", err)
}
resp, err := c.readResponse()
if err != nil {
return nil, err
}
logx.Infof("收到相机响应: %s", string(resp))
return resp, nil
}
func (c *Client) readResponse() ([]byte, error) {
reader := bufio.NewReader(c.conn)
var buffer bytes.Buffer
if err := c.conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil {
return nil, err
}
for {
b, err := reader.ReadByte()
if err != nil {
return nil, err
}
buffer.WriteByte(b)
if b == '\r' || b == '\n' {
break
}
}
return buffer.Bytes(), nil
}
func (c *Client) SendCommandWithContext(ctx context.Context, cmd string) ([]byte, error) {
type result struct {
resp []byte
err error
}
ch := make(chan result, 1)
go func() {
resp, err := c.SendCommand(cmd)
ch <- result{resp, err}
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case res := <-ch:
return res.resp, res.err
}
}
func (c *Client) TakePhoto() error {
_, err := c.SendCommand(CmdTakePhoto)
return err
}
func (c *Client) GetDetectionResult() (string, error) {
resp, err := c.SendCommand(CmdGetDetectionResult)
if err != nil {
return "", err
}
return string(resp), nil
}
func (c *Client) Close() {
if c.conn != nil {
c.conn.Close()
c.conn = nil
logx.Infof("相机连接已关闭")
}
}
+239
View File
@@ -0,0 +1,239 @@
package camera
import (
"context"
"errors"
"net"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// MockConn 模拟 net.Conn
type MockConn struct {
mock.Mock
}
func (m *MockConn) Read(b []byte) (n int, err error) {
args := m.Called(b)
return args.Int(0), args.Error(1)
}
func (m *MockConn) Write(b []byte) (n int, err error) {
args := m.Called(b)
return args.Int(0), args.Error(1)
}
func (m *MockConn) Close() error {
args := m.Called()
return args.Error(0)
}
func (m *MockConn) LocalAddr() net.Addr {
return nil
}
func (m *MockConn) RemoteAddr() net.Addr {
return nil
}
func (m *MockConn) SetDeadline(t time.Time) error {
return nil
}
func (m *MockConn) SetReadDeadline(t time.Time) error {
return nil
}
func (m *MockConn) SetWriteDeadline(t time.Time) error {
return nil
}
// MockDialer 模拟 DialTimeout
type MockDialer func(network, address string, timeout time.Duration) (net.Conn, error)
var mockDial MockDialer
func init() {
mockDial = func(network, address string, timeout time.Duration) (net.Conn, error) {
return nil, nil
}
}
var dialTimeout = net.DialTimeout
func mockDialFunc(f MockDialer) {
old := dialTimeout
dialTimeout = f
defer func() { dialTimeout = old }()
}
func TestConnect_SendCommands_ReadResponses(t *testing.T) {
// 创建客户端
client := NewClient("192.168.88.52:8600")
// 1. 测试连接
err := client.Connect()
assert.NoError(t, err)
assert.NotNil(t, client.conn)
//// 2. 发送 T1 命令
resp1, err := client.SendCommand("T1")
assert.NoError(t, err)
assert.Contains(t, string(resp1), "T1")
time.Sleep(100 * time.Millisecond)
// 3. 发送 GM,2,0 命令,验证是否收到 GM,0,... 格式回复
resp2, err := client.SendCommand("GM,2,0")
assert.NoError(t, err)
assert.Regexp(t, `^GM,0,.*`, string(resp2))
t.Logf("收到指令 GM,2,0 的回复: %s", resp2)
client.Close()
}
func TestNewClient(t *testing.T) {
client := NewClient("127.0.0.1:8080")
assert.Equal(t, "127.0.0.1:8080", client.addr)
assert.Equal(t, 5*time.Second, client.timeout)
client2 := NewClient("127.0.0.1:8080", 10*time.Second)
assert.Equal(t, 10*time.Second, client2.timeout)
}
func TestConnect_Success(t *testing.T) {
mockDialFunc(func(network, address string, timeout time.Duration) (net.Conn, error) {
return &MockConn{}, nil
})
client := NewClient("127.0.0.1:8080")
err := client.Connect()
assert.NoError(t, err)
assert.NotNil(t, client.conn)
}
func TestConnect_Failure(t *testing.T) {
mockDialFunc(func(network, address string, timeout time.Duration) (net.Conn, error) {
return nil, errors.New("connection refused")
})
client := NewClient("127.0.0.1:8080")
err := client.Connect()
assert.Error(t, err)
assert.Nil(t, client.conn)
}
func TestConnectWithRetry_SuccessOnSecondTry(t *testing.T) {
attempts := 0
mockDialFunc(func(network, address string, timeout time.Duration) (net.Conn, error) {
attempts++
if attempts == 1 {
return nil, errors.New("first fail")
}
return &MockConn{}, nil
})
client := NewClient("127.0.0.1:8080")
err := client.ConnectWithRetry(2, 100*time.Millisecond)
assert.NoError(t, err)
assert.Equal(t, 2, attempts)
}
func TestIsConnected(t *testing.T) {
client := NewClient("127.0.0.1:8080")
assert.False(t, client.IsConnected())
client.conn = &MockConn{}
assert.True(t, client.IsConnected())
}
func TestReconnect(t *testing.T) {
mockConn := new(MockConn)
mockConn.On("Close").Return(nil)
mockDialFunc(func(network, address string, timeout time.Duration) (net.Conn, error) {
return mockConn, nil
})
client := NewClient("127.0.0.1:8080")
client.conn = mockConn
err := client.Reconnect()
assert.NoError(t, err)
mockConn.AssertExpectations(t)
}
func TestSendCommand_NotConnected(t *testing.T) {
client := NewClient("127.0.0.1:8080")
_, err := client.SendCommand("CMD")
assert.Error(t, err)
assert.Equal(t, "未建立连接", err.Error())
}
func TestSendCommand_WriteError(t *testing.T) {
mockConn := new(MockConn)
mockConn.On("Write", mock.Anything).Return(0, errors.New("write error"))
client := NewClient("127.0.0.1:8080")
client.conn = mockConn
_, err := client.SendCommand("CMD")
assert.Error(t, err)
assert.Contains(t, err.Error(), "发送指令失败")
}
func TestReadResponse_Success(t *testing.T) {
conn := new(MockConn)
conn.On("SetReadDeadline", mock.Anything).Return(nil)
conn.On("Read", mock.Anything).Return(0, nil)
client := NewClient("127.0.0.1:8080")
client.conn = conn
resp, err := client.readResponse()
assert.NoError(t, err)
assert.Equal(t, []byte("RESPONSE\r\n"), resp)
}
func TestSendCommandWithContext_Cancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
client := NewClient("127.0.0.1:8080")
resp, err := client.SendCommandWithContext(ctx, "CMD")
assert.Error(t, err)
assert.Equal(t, context.Canceled, err)
assert.Nil(t, resp)
}
func TestTakePhoto(t *testing.T) {
mockConn := new(MockConn)
mockConn.On("Write", mock.Anything).Return(0, nil)
client := NewClient("127.0.0.1:8080")
client.conn = mockConn
err := client.TakePhoto()
assert.NoError(t, err)
}
func TestGetDetectionResult(t *testing.T) {
mockConn := new(MockConn)
mockConn.On("Write", mock.Anything).Return(0, nil)
mockConn.On("Read", mock.Anything).Return(0, nil)
client := NewClient("127.0.0.1:8080")
client.conn = mockConn
resp, err := client.GetDetectionResult()
assert.NoError(t, err)
assert.Empty(t, resp)
}
func TestClose(t *testing.T) {
mockConn := new(MockConn)
mockConn.On("Close").Return(nil)
client := NewClient("127.0.0.1:8080")
client.conn = mockConn
client.Close()
assert.Nil(t, client.conn)
mockConn.AssertExpectations(t)
}
+21
View File
@@ -0,0 +1,21 @@
package camera
import "math"
var (
Err_CameraTimeout = CameraError(math.Pow(2, 0)) // 相机超时
Err_CameraNotDetected = CameraError(math.Pow(2, 1)) // 未检测到目标
Err_CameraCommError = CameraError(math.Pow(2, 2)) // 通信异常
)
var CameraErrorMap = map[CameraError]string{
Err_CameraTimeout: "相机超时",
Err_CameraNotDetected: "未检测到目标",
Err_CameraCommError: "通信异常",
}
type CameraError int64
func (c CameraError) Error() string {
return CameraErrorMap[c]
}
+105
View File
@@ -0,0 +1,105 @@
package camera
import (
"fmt"
"log"
"sync"
"time"
"github.com/zeromicro/go-zero/core/logx"
)
type CameraEventHandler func(*Manager, string) []byte
type Config struct {
Addr string
Timeout time.Duration
MaxRetries int
RetryInterval time.Duration
}
type Manager struct {
client *Client
config Config
mu sync.Mutex
listeners map[string]CameraEventHandler
}
func NewManager(config Config) *Manager {
return &Manager{
config: config,
listeners: make(map[string]CameraEventHandler),
}
}
func (m *Manager) Start() error {
m.client = NewClient(m.config.Addr, m.config.Timeout)
err := m.client.ConnectWithRetry(m.config.MaxRetries, m.config.RetryInterval)
if err != nil {
return err
}
// 启动监听
go m.listenLoop()
return nil
}
func (m *Manager) listenLoop() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
m.pollEvents()
}
}
}
func (m *Manager) pollEvents() {
// 模拟检测事件,实际应由 Client 提供事件检测接口
result, err := m.client.GetDetectionResult()
if err != nil {
log.Printf("获取检测结果失败: %v", err)
return
}
if result != "" {
m.handleEvent("detection_result", result)
}
}
func (m *Manager) handleEvent(event string, data string) {
m.mu.Lock()
handler, exists := m.listeners[event]
m.mu.Unlock()
if !exists {
log.Printf("未注册事件处理器: %s", event)
return
}
logx.Infof("触发相机事件: %s, 数据: %s", event, data)
resp := handler(m, data)
if resp != nil {
// 可选:写回响应
fmt.Println("响应:", string(resp))
}
}
func (m *Manager) AddListener(event string, handler CameraEventHandler) {
m.mu.Lock()
defer m.mu.Unlock()
m.listeners[event] = handler
}
func (m *Manager) GetClient() *Client {
return m.client
}
func (m *Manager) Close() {
if m.client != nil {
m.client.Close()
}
}