star-cloud/mqtt-gateway/internal/handler/mqtt_handler.go
sky121113 9166decbb8 [FIX] 修復 MQTT 連線狀態追蹤與 EMQX 版本相容性問題
1. 優化 mqtt-gateway 的 $SYS 主題解析邏輯,改用關鍵字匹配(搜尋 clients/)以支援不同版本的 EMQX。
2. 修正啟動日誌中的 Client ID 顯示,改為讀取動態設定。
3. 配合 150 伺服器 EMQX 降級操作,同步更新通訊層容錯代碼。
2026-04-21 14:18:24 +08:00

113 lines
2.5 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 handler
import (
"encoding/json"
"log"
"star-cloud-gateway/internal/bridge"
"strings"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
type MqttHandler struct {
pusher *bridge.RedisPusher
}
func NewMqttHandler(pusher *bridge.RedisPusher) *MqttHandler {
return &MqttHandler{pusher: pusher}
}
func (h *MqttHandler) HandleMessage(client mqtt.Client, msg mqtt.Message) {
topic := msg.Topic()
payload := msg.Payload()
log.Printf("Received message on topic: %s", topic)
var serialNo, msgType string
var jsonPayload interface{}
// 檢查是否為系統主題 ($SYS)
if strings.HasPrefix(topic, "$SYS/") {
parts := strings.Split(topic, "/")
// 尋找事件類型 (connected 或 disconnected)
var event string
for _, p := range parts {
if p == "connected" || p == "disconnected" {
event = p
break
}
}
// 尋找 Client ID (通常位於 "clients" 的下一層)
for i, p := range parts {
if p == "clients" && i+1 < len(parts) {
serialNo = parts[i+1]
break
}
}
if event != "" && serialNo != "" {
// 解析系統 JSON Payload (補強:有些版本會把 ClientID 放在 Payload 裡)
var sysData map[string]interface{}
if err := json.Unmarshal(payload, &sysData); err == nil {
if cid, ok := sysData["clientid"].(string); ok {
serialNo = cid
}
}
msgType = "status"
statusValue := "online"
if event == "disconnected" {
statusValue = "offline"
}
jsonPayload = map[string]string{"status": statusValue}
log.Printf("Detected system event: Client [%s] is now [%s]", serialNo, statusValue)
} else {
return
}
} else {
// 一般業務主題 (machine/{serialNo}/{type})
parts := strings.Split(topic, "/")
if len(parts) < 3 {
return
}
serialNo = parts[1]
msgType = parts[2]
err := json.Unmarshal(payload, &jsonPayload)
if err != nil {
// 如果不是有效的 JSON則視為純文字/數值 (Raw Value)
jsonPayload = string(payload)
}
}
bridgePayload := bridge.BridgePayload{
Type: msgType,
SerialNo: cleanSerialNo(serialNo),
Payload: jsonPayload,
}
h.pusher.Push(bridgePayload)
}
// cleanSerialNo 處理 Client ID 格式,將 SC_M0408_pndk 轉為 M0408
func cleanSerialNo(clientID string) string {
if clientID == "" {
return ""
}
// 1. 移除已知前綴 SC_
cleaned := strings.TrimPrefix(clientID, "SC_")
// 2. 如果中間還有底線,通常代表隨機碼,只取前面部分
// 例如 M0408_test -> M0408
if idx := strings.Index(cleaned, "_"); idx != -1 {
return cleaned[:idx]
}
return cleaned
}