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,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("相机连接已关闭")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user