初始
This commit is contained in:
265
third/ws/ws.go
Normal file
265
third/ws/ws.go
Normal file
@ -0,0 +1,265 @@
|
||||
// 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
|
||||
}
|
Reference in New Issue
Block a user