2026-03-02 17:33:56 +08:00

112 lines
2.8 KiB
Go

// Package connection 管理单个 ESP32 硬件 WebSocket 连接的状态。
package connection
import (
"encoding/json"
"fmt"
"sync"
"github.com/gorilla/websocket"
)
// Connection 保存单个硬件连接的状态,所有方法并发安全。
type Connection struct {
WS *websocket.Conn
DeviceID string // MAC 地址,来自 URL 参数 device-id
ClientID string // 来自 URL 参数 client-id
SessionID string // 握手后分配的会话 ID
mu sync.Mutex
handshaked bool // 是否已完成 hello 握手
isPlaying bool
abortCh chan struct{} // close(abortCh) 通知流控 goroutine 中止播放
writeMu sync.Mutex // gorilla/websocket 写操作不并发安全,需独立锁
}
// New 创建新连接对象。
func New(ws *websocket.Conn, deviceID, clientID string) *Connection {
return &Connection{
WS: ws,
DeviceID: deviceID,
ClientID: clientID,
}
}
// Handshake 完成 hello 握手,存储 session_id。
func (c *Connection) Handshake(sessionID string) {
c.mu.Lock()
defer c.mu.Unlock()
c.SessionID = sessionID
c.handshaked = true
}
// IsHandshaked 返回连接是否已完成 hello 握手。
func (c *Connection) IsHandshaked() bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.handshaked
}
// SendCmd 向硬件发送控制指令,并发安全。
func (c *Connection) SendCmd(action string, params any) error {
return c.SendJSON(map[string]any{
"type": "cmd",
"action": action,
"params": params,
})
}
// StartPlayback 开始新一轮播放,返回 abortCh 供流控 goroutine 监听。
// 若已在播放,先中止上一轮再开始新的。
func (c *Connection) StartPlayback() <-chan struct{} {
c.mu.Lock()
defer c.mu.Unlock()
// 中止上一轮播放(若有)
if c.isPlaying && c.abortCh != nil {
close(c.abortCh)
}
c.abortCh = make(chan struct{})
c.isPlaying = true
return c.abortCh
}
// StopPlayback 结束播放状态。
func (c *Connection) StopPlayback() {
c.mu.Lock()
defer c.mu.Unlock()
c.isPlaying = false
}
// IsPlaying 返回当前是否正在播放。
func (c *Connection) IsPlaying() bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.isPlaying
}
// SendJSON 序列化 v 并以文本帧发送给设备,并发安全。
func (c *Connection) SendJSON(v any) error {
data, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("connection: marshal json: %w", err)
}
c.writeMu.Lock()
defer c.writeMu.Unlock()
return c.WS.WriteMessage(websocket.TextMessage, data)
}
// SendBinary 以二进制帧发送 Opus 数据,并发安全。
func (c *Connection) SendBinary(data []byte) error {
c.writeMu.Lock()
defer c.writeMu.Unlock()
return c.WS.WriteMessage(websocket.BinaryMessage, data)
}
// Close 关闭底层 WebSocket 连接。
func (c *Connection) Close() {
c.WS.Close()
}