Files
zmkgC/third/ws/ws.go
2025-07-07 20:11:59 +08:00

266 lines
7.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package ws
// @Author 铁憨憨[cory] 2025/2/12 15:18:00
package ws
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/v2/os/gctx"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"github.com/tiger1103/gfast/v3/api/v1/system"
"github.com/tiger1103/gfast/v3/internal/app/system/service"
"golang.org/x/net/context"
"log"
"math/rand"
"net/http"
"strconv"
"sync"
"time"
)
// 存储所有连接的设备信息
var connectedDevices = make(map[string]*DeviceInfo)
// 存储每个 uuid 对应的通道
var responseChannels = make(map[string]chan CommonResponse)
var responseChannelsMutex sync.Mutex
// HandleWebSocket 处理WebSocket连接的函数
func HandleWebSocket(w http.ResponseWriter, r *http.Request) {
ctx := gctx.New()
// 将HTTP连接升级为WebSocket连接
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("WebSocket升级失败:", err)
return
}
defer conn.Close()
// 读取设备发送的第一条消息,从中提取 SN然后添加到设备列表中
declare, err := addDevice(ctx, conn, r)
if err != nil {
log.Println("添加设备信息时出错:", err)
return
}
// 持续读取从客户端(其他服务器)发送过来的数据
for {
// 读取消息类型和消息内容
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("读取消息时出错:", err)
// 移除设备信息
delete(connectedDevices, declare.SN)
log.Printf("设备断开连接,设备信息: %+v", declare)
err = service.BusAttendanceMachine().Change(ctx, &system.BusAttendanceMachineChangeReq{
Sn: declare.SN,
Status: "0",
})
log.Println("修改考勤设备状态时出错:", err)
break
}
// 先解析出 cmd 字段
var genericMsg GenericMessage
err = json.Unmarshal(message, &genericMsg)
if err != nil {
log.Println("解析 cmd 字段时出错:", err)
continue
}
// 根据 cmd 字段进行不同处理
switch genericMsg.CMD {
case DECLARE:
log.Println("设备在线:", declare)
case PING:
declareMessage := DeclareMessage{}
if json.Unmarshal(message, &declareMessage) != nil {
return
}
if err = handlePing(ctx, conn, r, declareMessage.SN); err != nil {
log.Println("处理心跳回复:", err)
}
case ToClient:
if err := requestResponse(message); err != nil {
log.Println("处理响应:", err)
}
default:
log.Printf("收到未知消息---> 类型: %d, 消息内容: %s", messageType, string(message))
}
}
// 清理通道资源
responseChannelsMutex.Lock()
for key, ch := range responseChannels {
if connectedDevices[declare.SN] != nil {
delete(responseChannels, key)
close(ch)
}
}
responseChannelsMutex.Unlock()
}
// addDevice 将设备信息添加到设备列表中
func addDevice(ctx context.Context, conn *websocket.Conn, r *http.Request) (declareMessage DeclareMessage, err error) {
// 读取设备发送的第一条消息,从中提取 SN
_, message, err := conn.ReadMessage()
if err != nil {
return
}
declareMessage = DeclareMessage{}
if json.Unmarshal(message, &declareMessage) != nil {
return
}
// 假设设备信息可以从请求中获取,这里简单从 RemoteAddr 解析
ip, port := parseRemoteAddr(r.RemoteAddr)
deviceInfo := &DeviceInfo{
IP: ip,
Port: port,
Conn: conn,
}
if declareMessage.SN != "" {
// 存储设备信息
connectedDevices[declareMessage.SN] = deviceInfo
log.Printf("新设备连接,设备信息: %+v", declareMessage)
// 变更状态
err = service.BusAttendanceMachine().Register(ctx, &system.BusAttendanceMachineRegisterReq{
Sn: declareMessage.SN,
})
if err != nil {
return
}
}
return declareMessage, nil
}
// handlePing 处理 ping 消息
func handlePing(ctx context.Context, conn *websocket.Conn, r *http.Request, sn string) (err error) {
pongMsg := PongMessageData{
Cmd: "pong",
}
pongJSON, _ := json.Marshal(pongMsg)
err = conn.WriteMessage(websocket.TextMessage, pongJSON)
if err != nil {
log.Println("发送 Pong 消息时出错:", err)
return err
}
// 存储设备信息、变更状态
connectedDevices[sn] = &DeviceInfo{
Conn: conn,
}
err = service.BusAttendanceMachine().Register(ctx, &system.BusAttendanceMachineRegisterReq{
Sn: sn,
})
return nil
}
// requestResponse 处理请求得到的响应
func requestResponse(message []byte) (err error) {
log.Println("请求响应:", string(message))
var common CommonResponse
err = json.Unmarshal(message, &common)
if err != nil {
log.Println("解析 cmd 字段时出错:", err)
return err
}
// 根据UUID查找对应的通道
responseChannelsMutex.Lock()
if ch, ok := responseChannels[common.To]; ok {
ch <- common
delete(responseChannels, common.To)
}
responseChannelsMutex.Unlock()
return nil
}
// 根据 SN 发送消息给对应的设备
func sendMessageToDevice(sn string, uuid string, message interface{}) error {
conn, exists := connectedDevices[sn]
if !exists {
return nil
}
msgJSON, err := json.Marshal(message)
if err != nil {
return err
}
err = conn.Conn.WriteMessage(websocket.TextMessage, msgJSON)
if err != nil {
responseChannelsMutex.Lock()
delete(responseChannels, uuid)
responseChannelsMutex.Unlock()
return err
}
return nil
}
func SendRequestAndWaitResponse(sn string, sUuid string, payload interface{}) (CommonResponse, error) {
responseChan := make(chan CommonResponse, 1)
// 存入全局映射
responseChannelsMutex.Lock()
responseChannels[sUuid] = responseChan
responseChannelsMutex.Unlock()
// 发送请求
err := sendMessageToDevice(sn, sUuid, payload)
if err != nil {
return CommonResponse{}, err
}
// 等待响应
select {
case resp := <-responseChan:
fmt.Printf("收到响应: %+v\n", resp)
return resp, nil
case <-time.After(10 * time.Second):
responseChannelsMutex.Lock()
delete(responseChannels, sUuid)
responseChannelsMutex.Unlock()
return CommonResponse{}, fmt.Errorf("等待响应超时")
}
}
/*
=========================================================业务无关=========================================================
=========================================================业务无关=========================================================
=========================================================业务无关=========================================================
*/
// 定义一个升级器用于将HTTP连接升级为WebSocket连接
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 允许跨域访问,这里设置为允许所有来源
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 解析远程地址,获取 IP 和端口
func parseRemoteAddr(addr string) (string, string) {
// 简单处理,假设地址格式为 IP:Port
for i := len(addr) - 1; i >= 0; i-- {
if addr[i] == ':' {
return addr[:i], addr[i+1:]
}
}
return "", ""
}
// GenerateUUIDWithSixRandomDigits 生成一个 UUID 并拼接 6 位随机数
func GenerateUUIDWithSixRandomDigits() string {
// 生成 UUID
uuidStr := uuid.New().String()
// 初始化随机数种子
rand.Seed(time.Now().UnixNano())
// 生成 6 位随机数
randomNum := rand.Intn(900000) + 100000
// 将随机数转换为字符串
randomNumStr := strconv.Itoa(randomNum)
// 拼接 UUID 和 6 位随机数
result := uuidStr + "-" + randomNumStr
return result
}