1. 優化 mqtt-gateway 的 $SYS 主題解析邏輯,改用關鍵字匹配(搜尋 clients/)以支援不同版本的 EMQX。 2. 修正啟動日誌中的 Client ID 顯示,改為讀取動態設定。 3. 配合 150 伺服器 EMQX 降級操作,同步更新通訊層容錯代碼。
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
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
|
||
}
|