// 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() }