112 lines
2.8 KiB
Go
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()
|
|
}
|