王帅锟 преди 3 месеца
ревизия
001220e5c2
променени са 19 файла, в които са добавени 1311 реда и са изтрити 0 реда
  1. 3 0
      .gitignore
  2. 5 0
      Common/Resty.go
  3. 104 0
      GPS.go
  4. 48 0
      Intercom/Intercom.go
  5. 24 0
      QuectelAT/CMD.go
  6. 18 0
      QuectelAT/ERROR.go
  7. 59 0
      QuectelAT/GeneralAT.go
  8. 17 0
      QuectelAT/NULL.go
  9. 17 0
      QuectelAT/OK.go
  10. 33 0
      QuectelAT/Sentence.go
  11. 32 0
      QuectelAT/Serial.go
  12. 17 0
      QuectelAT/Text.go
  13. 214 0
      VoiceCall/Voice.go
  14. 17 0
      cfg.toml
  15. 111 0
      go.mod
  16. 308 0
      go.sum
  17. 172 0
      main.go
  18. 96 0
      monitor.go
  19. 16 0
      nmcli/WIFI.go

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.idea
+*.exe
+*.mp3

+ 5 - 0
Common/Resty.go

@@ -0,0 +1,5 @@
+package Common
+
+import "github.com/go-resty/resty/v2"
+
+var HTTPClient3s = resty.New()

+ 104 - 0
GPS.go

@@ -0,0 +1,104 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"github.com/adrianmo/go-nmea"
+	"github.com/gin-gonic/gin"
+	"github.com/tarm/serial"
+	"io"
+	"net"
+	"sync"
+)
+
+type GPS struct {
+	ConnectionType   string       `toml:"ConnectionType"`
+	ConnectionString string       `toml:"ConnectionString"`
+	serial           interface{}  `toml:"-"`
+	latitude         float64      `toml:"-"`
+	longitude        float64      `toml:"-"`
+	mutex            sync.RWMutex `toml:"-"`
+}
+
+func (g *GPS) Open() (err error) {
+	if g.ConnectionType == "serial" {
+		g.serial, err = serial.OpenPort(&serial.Config{
+			Name: "/dev/ttyS3", Baud: 9600,
+		})
+		if err != nil {
+			return
+		}
+		g.latitude = 0
+		g.longitude = 0
+		g.mutex = sync.RWMutex{}
+		go func() {
+			for {
+				scanner := bufio.NewScanner(g.serial.(io.Reader))
+				for scanner.Scan() {
+					s, err := nmea.Parse(scanner.Text())
+					if err != nil {
+						println(err.Error())
+						continue
+					}
+					if s.DataType() == nmea.TypeRMC {
+						m := s.(nmea.RMC)
+						g.mutex.Lock()
+						g.latitude = m.Latitude
+						g.longitude = m.Longitude
+						g.mutex.Unlock()
+					}
+				}
+			}
+		}()
+	}
+	if g.ConnectionType == "TCP" {
+		// 连接到 TCP 服务器
+		g.serial, err = net.Dial("tcp", g.ConnectionString)
+		if err != nil {
+			fmt.Println("Failed to connect to TCP server:", err)
+			return
+		}
+		g.latitude = 0
+		g.longitude = 0
+		g.mutex = sync.RWMutex{}
+
+		go func() {
+			for {
+				scanner := bufio.NewScanner(g.serial.(io.Reader))
+				for scanner.Scan() {
+					s, err := nmea.Parse(scanner.Text())
+					if err != nil {
+						println(err.Error())
+						continue
+					}
+					if s.DataType() == nmea.TypeRMC {
+						m := s.(nmea.RMC)
+						g.mutex.Lock()
+						g.latitude = m.Latitude
+						g.longitude = m.Longitude
+						g.mutex.Unlock()
+					}
+				}
+			}
+		}()
+	}
+	return
+}
+
+func (g *GPS) close() (err error) {
+	if g.ConnectionType == "serial" {
+		return g.serial.(*serial.Port).Close()
+	} else if g.ConnectionType == "TCP" {
+		return g.serial.(net.Conn).Close()
+	}
+	return
+}
+
+func (g *GPS) ObtainLatitudeAndLongitude(ctx *gin.Context) {
+	g.mutex.RLock()
+	ctx.JSON(200, map[string]float64{
+		"latitude":  g.latitude,
+		"longitude": g.longitude,
+	})
+	g.mutex.RUnlock()
+}

+ 48 - 0
Intercom/Intercom.go

@@ -0,0 +1,48 @@
+package Intercom
+
+import (
+	"log"
+	"os/exec"
+)
+
+type Intercom struct {
+	Volume int `toml:"Volume"`
+	queue  chan []byte
+}
+
+func (i *Intercom) Init() {
+	i.queue = make(chan []byte, 10)
+}
+func (i *Intercom) Add(data []byte) {
+	i.queue <- data
+}
+
+func (i *Intercom) Start() {
+	for {
+		// 创建 ffplay 命令
+		// -i pipe:0 表示从标准输入读取数据
+		cmd := exec.Command("ffplay", "-nodisp", "-autoexit", "-i", "pipe:0")
+		// 获取命令的标准输入
+		stdin, err := cmd.StdinPipe()
+		if err != nil {
+			log.Fatalf("无法获取标准输入: %v\n", err)
+		}
+		// 启动 ffplay
+		if err := cmd.Start(); err != nil {
+			log.Fatalf("无法启动 ffplay: %v\n", err)
+		}
+		// 将音频数据写入标准输入
+		_, err = stdin.Write(<-i.queue)
+		if err != nil {
+			log.Fatalf("无法写入音频数据: %v\n", err)
+		}
+		// 关闭标准输入
+		if err := stdin.Close(); err != nil {
+			log.Fatalf("无法关闭标准输入: %v\n", err)
+		}
+		// 等待 ffplay 退出
+		if err := cmd.Wait(); err != nil {
+			log.Fatalf("播放失败: %v\n", err)
+		}
+	}
+}

+ 24 - 0
QuectelAT/CMD.go

@@ -0,0 +1,24 @@
+package QuectelAT
+
+import "strings"
+
+const (
+	CMD = "CMD"
+)
+
+type SentenceCMD struct {
+	Message []string
+	CMD     string
+}
+
+func (s SentenceCMD) DataType() string {
+	return CMD
+}
+
+func NewCMD(raw string) Sentence {
+	Index := strings.Index(raw, ":")
+	return SentenceCMD{
+		CMD:     raw[1:Index],
+		Message: strings.Split(raw[Index+2:], ","),
+	}
+}

+ 18 - 0
QuectelAT/ERROR.go

@@ -0,0 +1,18 @@
+package QuectelAT
+
+const (
+	ERROR = "ERROR"
+)
+
+type SentenceERROR struct {
+	Type string
+	err  error
+}
+
+func (s SentenceERROR) DataType() string {
+	return ERROR
+}
+
+func NewERROR() Sentence {
+	return SentenceERROR{}
+}

+ 59 - 0
QuectelAT/GeneralAT.go

@@ -0,0 +1,59 @@
+package QuectelAT
+
+import (
+	"fmt"
+	"strconv"
+)
+
+// SetEcho 设置是否开启回显
+func (q *Quectel) SetEcho(Echo bool) (err error) {
+	if Echo {
+		err = q.SendCmd("ATE1")
+	} else {
+		err = q.SendCmd("ATE0")
+	}
+	for q.ioScanner.Scan() {
+		var parse Sentence
+		parse = Parse(q.ioScanner.Text())
+		if parse.DataType() == OK {
+			return
+		} else if parse.DataType() == ERROR {
+			return parse.(SentenceERROR).err
+		}
+	}
+	return
+}
+
+type CSQ struct {
+	RSSI int `json:"RSSI"`
+	BER  int `json:"BER"`
+}
+
+func (q *Quectel) GetCSQ() (data CSQ, err error) {
+	err = q.SendCmd("AT+CSQ")
+	for q.ioScanner.Scan() {
+		var parse Sentence
+		parse = Parse(q.ioScanner.Text())
+		println(parse.DataType())
+		if parse.DataType() == CMD {
+			println(parse.(SentenceCMD).CMD)
+		}
+		if (parse.DataType() == CMD) && (parse.(SentenceCMD).CMD == "CSQ") {
+			RSSI, err := strconv.Atoi(parse.(SentenceCMD).Message[0])
+			if err != nil {
+				return CSQ{}, err
+			}
+			BER, err := strconv.Atoi(parse.(SentenceCMD).Message[1])
+			if err != nil {
+				return CSQ{}, err
+			}
+			return CSQ{
+				RSSI: RSSI,
+				BER:  BER,
+			}, nil
+		} else if parse.DataType() == ERROR {
+			return CSQ{}, parse.(SentenceERROR).err
+		}
+	}
+	return CSQ{}, fmt.Errorf("未知错误")
+}

+ 17 - 0
QuectelAT/NULL.go

@@ -0,0 +1,17 @@
+package QuectelAT
+
+const (
+	NULL = "NULL"
+)
+
+type SentenceNULL struct {
+	Type string
+}
+
+func (s SentenceNULL) DataType() string {
+	return NULL
+}
+
+func NewNULL() Sentence {
+	return SentenceNULL{}
+}

+ 17 - 0
QuectelAT/OK.go

@@ -0,0 +1,17 @@
+package QuectelAT
+
+const (
+	OK = "OK"
+)
+
+type SentenceOK struct {
+	Type string
+}
+
+func (s SentenceOK) DataType() string {
+	return OK
+}
+
+func NewOK() Sentence {
+	return SentenceOK{}
+}

+ 33 - 0
QuectelAT/Sentence.go

@@ -0,0 +1,33 @@
+package QuectelAT
+
+import (
+	"strings"
+)
+
+type Sentence interface {
+	DataType() string
+}
+
+func Parse(raw string) (Sentence Sentence) {
+	// raw 去除 /r/n 去除空格
+	raw = strings.TrimSpace(raw)
+	if raw == "" || raw == "\r\n" || raw == "\r" || raw == "\n" || raw == " " {
+		return NewNULL()
+	}
+	if raw == "OK" {
+		return NewOK()
+	}
+	if raw == "ERROR" {
+		return NewERROR()
+	}
+	if raw[0] == '+' {
+		if strings.Contains(raw, "+CME ERROR:") {
+			return NewERROR()
+		}
+		if strings.Contains(raw, "+CMS ERROR:") {
+			return NewERROR()
+		}
+		return NewCMD(raw)
+	}
+	return NewText(raw)
+}

+ 32 - 0
QuectelAT/Serial.go

@@ -0,0 +1,32 @@
+package QuectelAT
+
+import (
+	"bufio"
+	"github.com/tarm/serial"
+)
+
+type Quectel struct {
+	serial    *serial.Port
+	ioScanner *bufio.Scanner
+}
+
+func (q *Quectel) Open() (err error) {
+	q.serial, err = serial.OpenPort(&serial.Config{
+		Name: "/dev/ttyUSB2", Baud: 115200,
+	})
+	if err != nil {
+		return
+	}
+	q.ioScanner = bufio.NewScanner(q.serial)
+	err = q.SetEcho(false)
+	if err != nil {
+		return
+	}
+	return
+
+}
+
+func (q *Quectel) SendCmd(cmd string) (err error) {
+	_, err = q.serial.Write([]byte(cmd + "\r\n"))
+	return
+}

+ 17 - 0
QuectelAT/Text.go

@@ -0,0 +1,17 @@
+package QuectelAT
+
+const (
+	Text = "Text"
+)
+
+type SentenceText struct {
+	Message string
+}
+
+func (s SentenceText) DataType() string {
+	return Text
+}
+
+func NewText(raw string) Sentence {
+	return SentenceText{}
+}

+ 214 - 0
VoiceCall/Voice.go

@@ -0,0 +1,214 @@
+package VoiceCall
+
+import (
+	"fmt"
+	"github.com/go-resty/resty/v2"
+	lksdk "github.com/livekit/server-sdk-go/v2"
+	"github.com/pion/webrtc/v4"
+	"github.com/pion/webrtc/v4/pkg/media/oggwriter"
+	"io"
+	"os/exec"
+	"runtime"
+	"strings"
+	"time"
+)
+
+type Voice struct {
+	VoiceServiceAddress string `toml:"VoiceServiceAddress"`
+	RoomID              string `toml:"RoomID"`
+	ParticipantName     string `toml:"ParticipantName"`
+	MakeTokenAddress    string `toml:"MakeTokenAddress"`
+	Volume              int    `toml:"Volume"`
+	RecordingEquipment  string `toml:"RecordingEquipment"`
+
+	player  map[string]*TrackWriter
+	roomObj *lksdk.Room
+
+	refresh func()
+}
+
+var HTTPClient3s = resty.New()
+
+func (receiver *Voice) getToken() (string, error) {
+	get, err := HTTPClient3s.R().Get(fmt.Sprintf(receiver.MakeTokenAddress, receiver.RoomID, receiver.ParticipantName))
+	if err != nil || get.StatusCode() != 200 {
+		return "", err
+	}
+	return string(get.Body()), nil
+}
+func (receiver *Voice) SetOnRefresh(refresh func()) {
+	receiver.refresh = refresh
+}
+
+func (receiver *Voice) ConnectRoom() error {
+	var err error
+	var token string
+	token, err = receiver.getToken()
+	if err != nil {
+		return err
+	}
+	receiver.player = make(map[string]*TrackWriter)
+	receiver.roomObj, err = lksdk.ConnectToRoomWithToken(receiver.VoiceServiceAddress, token, &lksdk.RoomCallback{
+		ParticipantCallback: lksdk.ParticipantCallback{
+			OnTrackSubscribed:   receiver.onTrackSubscribed,
+			OnTrackUnsubscribed: receiver.onTrackUnsubscribed,
+		},
+	})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (receiver *Voice) PushMicrophone() error {
+	// 判断当前是windows还是linux
+	var cmd *exec.Cmd
+	if strings.Contains(runtime.GOOS, "windows") {
+		cmd = exec.Command(
+			"ffmpeg",
+			"-f", "dshow",
+			"-i", receiver.RecordingEquipment,
+			"-ac", "2", // 强制为单声道
+			"-ar", "48000", // 设置采样率为 48kHz
+			"-c:a", "libopus",
+			"-b:a", "32k", // 设置比特率为 32 kbps
+			"-frame_duration", "10", // 每帧持续 20ms
+			"-application", "lowdelay", // 设置低延迟编码
+			"-f", "ogg",
+			"-page_duration", "10000",
+			"pipe:1",
+		)
+	} else {
+		cmd = exec.Command(
+			"ffmpeg",
+			"-f", "alsa", // 使用 ALSA 捕获音频
+			"-i", receiver.RecordingEquipment, // 音频设备,例如 "hw:0,0"
+			"-ac", "2", // 强制为单声道
+			"-ar", "48000", // 设置采样率为 48kHz
+			"-c:a", "libopus", // 使用 Opus 编解码器
+			"-b:a", "32k", // 设置比特率为 32 kbps
+			"-frame_duration", "10", // 每帧持续 20ms
+			"-application", "lowdelay", // 设置低延迟编码
+			"-f", "ogg", // 输出格式为 OGG
+			"-page_duration", "10000", // 设置页持续时间
+			"pipe:1", // 输出到管道
+		)
+	}
+	// Get the stdout pipe
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		fmt.Println("Error creating StdoutPipe:", err)
+		return err
+	}
+	// Start the ffmpeg command
+	if err = cmd.Start(); err != nil {
+		fmt.Println("Error starting ffmpeg command:", err)
+		return err
+	}
+
+	// Create a new local reader track
+	track, err := lksdk.NewLocalReaderTrack(stdout, webrtc.MimeTypeOpus,
+		lksdk.ReaderTrackWithFrameDuration(10*time.Millisecond),
+		lksdk.ReaderTrackWithOnWriteComplete(func() { fmt.Println("track finished") }),
+	)
+	if err != nil {
+		fmt.Println("Error creating local reader track:", err)
+		return err
+	}
+
+	// Publish the track to the room
+	if _, err = receiver.roomObj.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{Name: "live_audio"}); err != nil {
+		fmt.Println("Error publishing track:", err)
+		return err
+	}
+	err = cmd.Wait()
+	if err != nil {
+		if err == io.EOF {
+			fmt.Println("ffmpeg process exited successfully")
+		} else {
+			fmt.Println("Error waiting for ffmpeg process:", err)
+		}
+		return err
+	}
+	// 与房间断开连接
+	receiver.roomObj.Disconnect()
+	return nil
+}
+
+func (receiver *Voice) onTrackUnsubscribed(track *webrtc.TrackRemote, publication *lksdk.RemoteTrackPublication, rp *lksdk.RemoteParticipant) {
+	fileName := fmt.Sprintf("%s-%s", rp.Identity(), track.ID())
+	v, ok := receiver.player[fileName]
+	if ok {
+		v.stop()
+		delete(receiver.player, fileName)
+		println(receiver.player)
+	}
+}
+
+func (receiver *Voice) onTrackSubscribed(track *webrtc.TrackRemote, publication *lksdk.RemoteTrackPublication, rp *lksdk.RemoteParticipant) {
+	switch {
+	case strings.EqualFold(track.Codec().MimeType, "audio/opus"):
+	default:
+		return
+	}
+	fileName := fmt.Sprintf("%s-%s", rp.Identity(), track.ID())
+	receiver.player[fileName] = NewTrackWriter(track)
+	println(receiver.player)
+}
+
+type TrackWriter struct {
+	track *webrtc.TrackRemote
+	pr    *io.PipeReader
+	pw    *io.PipeWriter
+	cmd   *exec.Cmd
+}
+
+func NewTrackWriter(track *webrtc.TrackRemote) *TrackWriter {
+	pr, pw := io.Pipe()
+	t := &TrackWriter{
+		pr:    pr,
+		pw:    pw,
+		track: track,
+	}
+	go t.start()
+	return t
+}
+
+func (t *TrackWriter) start() {
+	t.cmd = exec.Command("ffplay", "-nodisp", "-f", "ogg", "-")
+	//t.cmd = exec.Command("ffplay", "-f", "ogg", "-")
+	t.cmd.Stdin = t.pr
+	err := t.cmd.Start()
+	if err != nil {
+		fmt.Printf("Error starting ffplay: %v\n", err)
+		return
+	}
+	writer, err := oggwriter.NewWith(t.pw, 48000, 2)
+	if err != nil {
+		println(err)
+		return
+	}
+	defer writer.Close()
+	for {
+		rtpPacket, _, err := t.track.ReadRTP()
+		if err != nil {
+			break
+		}
+		err = writer.WriteRTP(rtpPacket)
+		if err != nil {
+			return
+		}
+	}
+}
+func (t *TrackWriter) stop() {
+	t.pr.Close()
+	t.pw.Close()
+	if t.cmd.Process != nil {
+		err := t.cmd.Process.Kill()
+		if err != nil {
+			fmt.Printf("Error sending SIGTERM to ffplay process: %v\n", err)
+			return
+		}
+	}
+	t.cmd.Wait()
+}

+ 17 - 0
cfg.toml

@@ -0,0 +1,17 @@
+#[GPS]
+#  ConnectionType = "TCP"
+#  ConnectionString = "192.168.211.5:5100"
+
+[VoiceCall]
+  VoiceServiceAddress = "ws://36.133.244.231:7880"
+  RoomID = "aikun"
+  ParticipantName = "11111Embedded"
+  MakeTokenAddress = "http://36.133.244.231:1688/getToken?Room=%s&Identity=%s"
+  RecordingEquipment = "audio=Microphone in (Realtek(R) Audio)"
+  Volume = 50
+
+[Intercom]
+  Volume = 50
+
+[Alarm]
+  Volume = 50

+ 111 - 0
go.mod

@@ -0,0 +1,111 @@
+module AudioPlayer
+
+go 1.23.2
+
+require (
+	github.com/BurntSushi/toml v1.4.0
+	github.com/adrianmo/go-nmea v1.10.0
+	github.com/gin-gonic/gin v1.10.0
+	github.com/go-resty/resty/v2 v2.16.2
+	github.com/livekit/server-sdk-go/v2 v2.4.0
+	github.com/pion/mediadevices v0.7.0
+	github.com/shirou/gopsutil/v4 v4.24.8
+	github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
+)
+
+require (
+	buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 // indirect
+	buf.build/go/protoyaml v0.2.0 // indirect
+	github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+	github.com/benbjohnson/clock v1.3.5 // indirect
+	github.com/bep/debounce v1.2.1 // indirect
+	github.com/bufbuild/protovalidate-go v0.6.3 // indirect
+	github.com/bytedance/sonic v1.11.6 // indirect
+	github.com/bytedance/sonic/loader v0.1.1 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/cloudwego/base64x v0.1.4 // indirect
+	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/frostbyte73/core v0.0.13 // indirect
+	github.com/fsnotify/fsnotify v1.8.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/gammazero/deque v0.2.1 // indirect
+	github.com/gen2brain/malgo v0.11.23 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-jose/go-jose/v3 v3.0.3 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-ole/go-ole v1.2.6 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.20.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/google/cel-go v0.21.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/gorilla/websocket v1.5.3 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/jxskiss/base62 v1.1.0 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
+	github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 // indirect
+	github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98 // indirect
+	github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841 // indirect
+	github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8 // indirect
+	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
+	github.com/magefile/mage v1.15.0 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/nats-io/nats.go v1.36.0 // indirect
+	github.com/nats-io/nkeys v0.4.7 // indirect
+	github.com/nats-io/nuid v1.0.1 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/pion/datachannel v1.5.9 // indirect
+	github.com/pion/dtls/v3 v3.0.4 // indirect
+	github.com/pion/ice/v4 v4.0.3 // indirect
+	github.com/pion/interceptor v0.1.37 // indirect
+	github.com/pion/logging v0.2.2 // indirect
+	github.com/pion/mdns/v2 v2.0.7 // indirect
+	github.com/pion/randutil v0.1.0 // indirect
+	github.com/pion/rtcp v1.2.14 // indirect
+	github.com/pion/rtp v1.8.9 // indirect
+	github.com/pion/sctp v1.8.34 // indirect
+	github.com/pion/sdp/v3 v3.0.9 // indirect
+	github.com/pion/srtp/v3 v3.0.4 // indirect
+	github.com/pion/stun/v3 v3.0.0 // indirect
+	github.com/pion/transport/v3 v3.0.7 // indirect
+	github.com/pion/turn/v4 v4.0.0 // indirect
+	github.com/pion/webrtc/v4 v4.0.5 // indirect
+	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+	github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
+	github.com/redis/go-redis/v9 v9.7.0 // indirect
+	github.com/shoenig/go-m1cpu v0.1.6 // indirect
+	github.com/stoewer/go-strcase v1.3.0 // indirect
+	github.com/tklauser/go-sysconf v0.3.12 // indirect
+	github.com/tklauser/numcpus v0.6.1 // indirect
+	github.com/twitchtv/twirp v8.1.3+incompatible // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	github.com/wlynxg/anet v0.0.5 // indirect
+	github.com/yusufpapurcu/wmi v1.2.4 // indirect
+	github.com/zeebo/xxh3 v1.0.2 // indirect
+	go.uber.org/atomic v1.11.0 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	go.uber.org/zap v1.27.0 // indirect
+	go.uber.org/zap/exp v0.3.0 // indirect
+	golang.org/x/arch v0.8.0 // indirect
+	golang.org/x/crypto v0.29.0 // indirect
+	golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
+	golang.org/x/image v0.23.0 // indirect
+	golang.org/x/net v0.31.0 // indirect
+	golang.org/x/sync v0.10.0 // indirect
+	golang.org/x/sys v0.27.0 // indirect
+	golang.org/x/text v0.21.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
+	google.golang.org/grpc v1.68.0 // indirect
+	google.golang.org/protobuf v1.35.2 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 308 - 0
go.sum

@@ -0,0 +1,308 @@
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 h1:SZRVx928rbYZ6hEKUIN+vtGDkl7uotABRWGY4OAg5gM=
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2/go.mod h1:ylS4c28ACSI59oJrOdW4pHS4n0Hw4TgSPHn8rpHl4Yw=
+buf.build/go/protoyaml v0.2.0 h1:2g3OHjtLDqXBREIOjpZGHmQ+U/4mkN1YiQjxNB68Ip8=
+buf.build/go/protoyaml v0.2.0/go.mod h1:L/9QvTDkTWcDTzAL6HMfN+mYC6CmZRm2KnsUA054iL0=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/adrianmo/go-nmea v1.10.0 h1:L1aYaebZ4cXFCoXNSeDeQa0tApvSKvIbqMsK+iaRiCo=
+github.com/adrianmo/go-nmea v1.10.0/go.mod h1:u8bPnpKt/D/5rll/5l9f6iDfeq5WZW0+/SXdkwix6Tg=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
+github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
+github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
+github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
+github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/bufbuild/protovalidate-go v0.6.3 h1:wxQyzW035zM16Binbaz/nWAzS12dRIXhZdSUWRY7Fv0=
+github.com/bufbuild/protovalidate-go v0.6.3/go.mod h1:J4PtwP9Z2YAGgB0+o+tTWEDtLtXvz/gfhFZD8pbzM/U=
+github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
+github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
+github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
+github.com/frostbyte73/core v0.0.13 h1:W/NFPNiCkGTRzMWnCVptn6vX6Tr4a7LvN0RFc0xsC2k=
+github.com/frostbyte73/core v0.0.13/go.mod h1:XsOGqrqe/VEV7+8vJ+3a8qnCIXNbKsoEiu/czs7nrcU=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
+github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
+github.com/gen2brain/malgo v0.11.23 h1:3/VAI8DP9/Wyx1CUDNlUQJVdWUvGErhjHDqYcHVk9ME=
+github.com/gen2brain/malgo v0.11.23/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
+github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
+github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
+github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
+github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
+github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
+github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58=
+github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
+github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98 h1:QA7DqIC/ZSsMj8HC0+zNfMMwssHbA0alZALK68r30LQ=
+github.com/livekit/mediatransportutil v0.0.0-20241128072814-c363618d4c98/go.mod h1:WIVFAGzVZ7VMjPC5+nbSfwdFjWcbuLgx97KeNSUDTEo=
+github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841 h1:69dSvfL6H6odFhL9q4s+RjDRDdfLY+WUUQ/Lz0av2Bs=
+github.com/livekit/protocol v1.28.2-0.20241128072830-b738aedbd841/go.mod h1:mqXSWNHbENjxM0/HG25wZ7wgja/K9fA0PeQxi+MPmWw=
+github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8 h1:Ibh0LoFl5NW5a1KFJEE0eLxxz7dqqKmYTj/BfCb0PbY=
+github.com/livekit/psrpc v0.6.1-0.20241018124827-1efff3d113a8/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0=
+github.com/livekit/server-sdk-go/v2 v2.4.0 h1:ide41hppBf7btHLz/nj6rLIQSkaIOxP5tVSki74ZDhg=
+github.com/livekit/server-sdk-go/v2 v2.4.0/go.mod h1:0hzAkh/FegPZmXDp8Ai92ndP/mWVpBxeR5VnR3muQp4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
+github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
+github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
+github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
+github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
+github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
+github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
+github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
+github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
+github.com/pion/ice/v4 v4.0.3 h1:9s5rI1WKzF5DRqhJ+Id8bls/8PzM7mau0mj1WZb4IXE=
+github.com/pion/ice/v4 v4.0.3/go.mod h1:VfHy0beAZ5loDT7BmJ2LtMtC4dbawIkkkejHPRZNB3Y=
+github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
+github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
+github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
+github.com/pion/mediadevices v0.7.0 h1:OdSusoLDY3Igz5IlbpROrj0nAHS2Jwn4XsdUQQ08zyk=
+github.com/pion/mediadevices v0.7.0/go.mod h1:DE13cQik3WWSGJa4Rc5Y9Lf0PV5cbCaVuKvH8VE97bo=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
+github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
+github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
+github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
+github.com/pion/sctp v1.8.34 h1:rCuD3m53i0oGxCSp7FLQKvqVx0Nf5AUAHhMRXTTQjBc=
+github.com/pion/sctp v1.8.34/go.mod h1:yWkCClkXlzVW7BXfI2PjrUGBwUI0CjXJBkhLt+sdo4U=
+github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
+github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
+github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
+github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
+github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
+github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
+github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
+github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
+github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
+github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
+github.com/pion/webrtc/v4 v4.0.5 h1:8cVPojcv3cQTwVga2vF1rzCNvkiEimnYdCCG7yF317I=
+github.com/pion/webrtc/v4 v4.0.5/go.mod h1:LvP8Np5b/sM0uyJIcUPvJcCvhtjHxJwzh2H2PYzE6cQ=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
+github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
+github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
+github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/shirou/gopsutil/v4 v4.24.8 h1:pVQjIenQkIhqO81mwTaXjTzOMT7d3TZkf43PlVFHENI=
+github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg=
+github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
+github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
+github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
+github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
+github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
+github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
+github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
+github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
+go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
+go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
+go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
+golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
+golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
+golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
+google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
+google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
+google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
+google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
+google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 172 - 0
main.go

@@ -0,0 +1,172 @@
+package main
+
+import (
+	"AudioPlayer/Intercom"
+	"AudioPlayer/QuectelAT"
+	"AudioPlayer/VoiceCall"
+	"bytes"
+	"github.com/gin-gonic/gin"
+	"os"
+	"os/exec"
+	"sync"
+
+	"github.com/BurntSushi/toml"
+)
+
+func Command(exe string, args []string, ctx *gin.Context) {
+	cmd := exec.Command(exe, args...)
+	var in bytes.Buffer
+	cmd.Stdin = &in
+	var out bytes.Buffer
+	cmd.Stdout = &out
+
+	err := cmd.Run()
+	if err != nil {
+		println(err.Error())
+		return
+	}
+	ctx.Data(200, "text/plain", out.Bytes())
+}
+
+var InExecMutex sync.RWMutex
+var InExec bool
+
+func MutexCommandGin(ctx *gin.Context) {
+
+	InExecMutex.RLock()
+	if InExec {
+		ctx.AbortWithStatusJSON(200, gin.H{"error": "Command is already in execution. Request discarded."})
+		InExecMutex.RUnlock()
+		return
+	}
+	InExecMutex.RUnlock()
+
+	var v []string
+	err := ctx.ShouldBindJSON(&v)
+	if err != nil {
+		println(err.Error())
+		return
+	}
+
+	InExecMutex.Lock()
+	InExec = true
+	InExecMutex.Unlock()
+
+	Command(ctx.Query("exec"), v, ctx)
+
+	InExecMutex.Lock()
+	InExec = false
+	InExecMutex.Unlock()
+}
+
+func CommandGin(ctx *gin.Context) {
+	var v []string
+	err := ctx.ShouldBindJSON(&v)
+	if err != nil {
+		println(err.Error())
+		return
+	}
+	Command(ctx.Query("exec"), v, ctx)
+}
+
+type ServerInfo struct {
+	GlobalPositioningSystem *GPS               `toml:"GPS"`
+	VoiceService            *VoiceCall.Voice   `toml:"VoiceCall"`
+	Intercom                *Intercom.Intercom `toml:"Intercom"`
+	Alarm                   *Alarm             `toml:"Alarm"`
+}
+
+type Alarm struct {
+	Volume int `toml:"Volume"`
+}
+
+func (receiver *ServerInfo) Refresh() {
+	marshal, err := toml.Marshal(receiver)
+	if err != nil {
+		return
+	}
+	err = os.WriteFile("./cfg.toml", marshal, 0666)
+	if err != nil {
+		return
+	}
+}
+
+func main() {
+	var cfg ServerInfo
+	var err error
+	_, err = toml.DecodeFile("./cfg.toml", &cfg)
+	if err != nil {
+		panic(err)
+		return
+	}
+	if cfg.VoiceService != nil {
+		cfg.VoiceService.SetOnRefresh(cfg.Refresh)
+		err = cfg.VoiceService.ConnectRoom()
+		if err == nil {
+			go func() {
+				err := cfg.VoiceService.PushMicrophone()
+				if err != nil {
+					println(err)
+					return
+				}
+			}()
+		} else {
+			println(err)
+		}
+	}
+
+	err = os.Setenv("ALSA_PCM_CARD", "1")
+	if err != nil {
+		return
+	}
+	err = os.Setenv("ALSA_PCM_DEVICE", "0")
+	if err != nil {
+		return
+	}
+	gin.SetMode(gin.ReleaseMode)
+	r := gin.New()
+	r.PUT("/MutexCommand", MutexCommandGin)
+	r.PUT("/Command", CommandGin)
+	r.GET("/Host", Host)
+	r.GET("/NetPort", NetPort)
+	r.GET("/Memory", Memory)
+	r.GET("/Cpu", Cpu)
+	r.GET("/Bandwidth", Bandwidth)
+	r.GET("/Temperature", Temperature)
+
+	cfg.Intercom.Init()
+	r.PUT("/Mailbox", func(ctx *gin.Context) {
+		data, err := ctx.GetRawData()
+		if err != nil {
+			return
+		}
+		cfg.Intercom.Add(data)
+	})
+	go cfg.Intercom.Start()
+
+	var Q QuectelAT.Quectel
+	err = Q.Open()
+	if err == nil {
+		r.GET("/MobileSignal", func(context *gin.Context) {
+			if data, err := Q.GetCSQ(); err != nil {
+				context.JSON(400, gin.H{"error": err.Error()})
+			} else {
+				context.JSON(200, data)
+			}
+		})
+	}
+
+	if cfg.GlobalPositioningSystem != nil {
+		err = cfg.GlobalPositioningSystem.Open()
+		if err != nil {
+			return
+		}
+		r.GET("/GPS", cfg.GlobalPositioningSystem.ObtainLatitudeAndLongitude)
+	}
+
+	err = r.Run(":4000")
+	if err != nil {
+		println(err)
+		return
+	}
+}

+ 96 - 0
monitor.go

@@ -0,0 +1,96 @@
+package main
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/shirou/gopsutil/v4/cpu"
+	"github.com/shirou/gopsutil/v4/host"
+	"github.com/shirou/gopsutil/v4/mem"
+	"github.com/shirou/gopsutil/v4/net"
+	"github.com/shirou/gopsutil/v4/sensors"
+	"regexp"
+	"time"
+)
+
+type NetPortJson struct {
+	Name         string                `json:"name"`
+	HardwareAddr string                `json:"hardwareAddr"`
+	Addrs        net.InterfaceAddrList `json:"addrs"`
+}
+
+type IOCountersStat struct {
+	Name      string `json:"name"`      // interface name
+	BytesSent uint64 `json:"bytesSent"` // number of bytes sent
+	BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
+}
+
+func shouldSkipInterface(name string) bool {
+	// 使用正则表达式匹配需要跳过的接口名称
+	skipPattern := regexp.MustCompile(`^lo|docker|br|蓝牙|Loopback|veth`)
+	return skipPattern.MatchString(name)
+}
+
+// 获取主机信息
+func Host(ctx *gin.Context) {
+	h, _ := host.Info()
+	ctx.JSON(200, h)
+}
+
+// 获取网卡信息
+func NetPort(ctx *gin.Context) {
+	interfaces, _ := net.Interfaces()
+	var NetPortJsonRes []NetPortJson
+	for _, i := range interfaces {
+		if shouldSkipInterface(i.Name) {
+			continue
+		}
+		if i.HardwareAddr == "" {
+			continue
+		}
+		NetPortJsonRes = append(NetPortJsonRes, NetPortJson{
+			Name:         i.Name,
+			HardwareAddr: i.HardwareAddr,
+			Addrs:        i.Addrs,
+		})
+	}
+
+	ctx.JSON(200, NetPortJsonRes)
+}
+
+// 获取温度信息
+func Temperature(ctx *gin.Context) {
+	temperatures, err := sensors.SensorsTemperatures()
+	if err != nil {
+		return
+	}
+	ctx.JSON(200, temperatures)
+}
+
+// 带宽信息
+func Bandwidth(ctx *gin.Context) {
+	netIO, _ := net.IOCounters(true)
+	var NetBandwidth []IOCountersStat
+	for _, io := range netIO {
+		if shouldSkipInterface(io.Name) {
+			continue
+		}
+		NetBandwidth = append(NetBandwidth, IOCountersStat{
+			Name:      io.Name,
+			BytesSent: io.BytesSent,
+			BytesRecv: io.BytesRecv,
+		})
+	}
+	ctx.JSON(200, NetBandwidth)
+}
+
+func Memory(ctx *gin.Context) {
+	v, _ := mem.VirtualMemory()
+	ctx.Header("Content-Type", "application/json")
+	ctx.String(200, fmt.Sprintf("%v", v.UsedPercent))
+}
+
+func Cpu(ctx *gin.Context) {
+	percent, _ := cpu.Percent(time.Second, false)
+	ctx.Header("Content-Type", "application/json")
+	ctx.String(200, fmt.Sprintf("%v", percent))
+}

+ 16 - 0
nmcli/WIFI.go

@@ -0,0 +1,16 @@
+package nmcli
+
+import "github.com/gin-gonic/gin"
+
+func WifiList(c *gin.Context) {
+
+}
+
+func WifiConnect(c *gin.Context) {
+	//SSID string, password string
+}
+
+func Routing(c *gin.RouterGroup) {
+	c.GET("/WifiList", WifiList)
+	c.PUT("/WifiConnect", WifiConnect)
+}