fwh_mqtt_client.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package fwh_mqtt
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "encoding/json"
  7. "fmt"
  8. "fwh-toolbox-server/config"
  9. "fwh-toolbox-server/utils/mqttClient"
  10. "os"
  11. "strconv"
  12. "time"
  13. "go.uber.org/zap"
  14. )
  15. const (
  16. fwhClientIdPrefix = "fwh_xlog_mqtt_client_"
  17. sleepTimeout = 5 * time.Minute
  18. sendMessageTimeout = 1 * time.Minute
  19. )
  20. type FwhMqttResult struct {
  21. Err error
  22. Data *FwhMqttMsg
  23. }
  24. type FwhMqttMsg struct {
  25. CmdType uint `json:"cmdType"` // 业务类型标识
  26. Crc string `json:"crc"` // dataArea 数据字符串的CRC
  27. DataArea interface{} `json:"dataArea"` // 数据域
  28. EquipNo string `json:"equipNo"` // 设备编号 SN
  29. Len int `json:"len"` // dataArea 数据长度
  30. Snno uint `json:"snno"` // 序号
  31. TimeStamp int64 `json:"timeStamp"` // 时间戳
  32. Type uint `json:"type"` // 设备类型 0:设备网关IBG
  33. }
  34. type MqttMsg struct {
  35. topic string // 发送 Topic
  36. msg FwhMqttMsg
  37. subTopic string // 响应 Topic
  38. resultCh chan FwhMqttResult
  39. ctx context.Context
  40. }
  41. type ResultChan struct {
  42. ch chan FwhMqttMsg
  43. ctx context.Context
  44. }
  45. var (
  46. taskCh = make(chan MqttMsg, 1000)
  47. // resultChListMap = map[string][]*ResultChan{}
  48. )
  49. var fwhMqttClient *mqttClient.MqttClient
  50. func InitFwhMqttClient(cfg *config.FwhMqttConfig) error {
  51. // 证书配置
  52. caCert, err := os.ReadFile(cfg.FwhMqttCaUrl)
  53. if err != nil {
  54. panic(err)
  55. }
  56. caCertPool := x509.NewCertPool()
  57. caCertPool.AppendCertsFromPEM(caCert)
  58. cert, err := tls.LoadX509KeyPair(cfg.FwhMqttCertUrl, cfg.FwhMqttKeyUrl)
  59. if err != nil {
  60. panic(err)
  61. }
  62. client, err := mqttClient.CreateMqttClient(mqttClient.MqttClientOpt{
  63. Broker: cfg.FwhMqttBroker,
  64. ClientID: fwhClientIdPrefix + strconv.FormatInt(time.Now().Unix(), 10),
  65. TLS: &tls.Config{
  66. Certificates: []tls.Certificate{cert},
  67. RootCAs: caCertPool,
  68. },
  69. })
  70. if err != nil {
  71. zap.L().Error("Init FWH MQTT client failed.")
  72. return err
  73. }
  74. fwhMqttClient = client
  75. zap.L().Info("Init FWH MQTT client success.")
  76. go fwhMqttWorker(fwhMqttClient)
  77. return nil
  78. }
  79. func fwhMqttWorker(client *mqttClient.MqttClient) {
  80. for {
  81. select {
  82. case task, ok := <-taskCh:
  83. if !ok {
  84. zap.L().Error("Channel closed, worker exiting, recreate.")
  85. taskCh = make(chan MqttMsg, 1000)
  86. break
  87. }
  88. if err := client.Connect(); err != nil {
  89. zap.L().Error("FWH MQTT connect failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
  90. task.resultCh <- FwhMqttResult{
  91. Err: fmt.Errorf("FWH MQTT connect failed."),
  92. Data: nil,
  93. }
  94. break
  95. }
  96. // 订阅响应消息 - 请求响应无法一一对应,不支持并发请求,改成简单的队列请求
  97. // if _, ok := resultChListMap[task.subTopic]; !ok {
  98. // resultChListMap[task.subTopic] = []*ResultChan{}
  99. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  100. // if err := client.SubscribeMessage(task.subTopic, func(payload []byte) {
  101. // var newList []*ResultChan
  102. // for _, ch := range resultChListMap[task.subTopic] {
  103. // if err := ch.ctx.Err(); err != nil {
  104. // switch err {
  105. // case context.DeadlineExceeded:
  106. // zap.L().Warn("Waiting for FWH MQTT message timeout.", zap.String("topic", task.topic))
  107. // case context.Canceled:
  108. // zap.L().Warn("Waiting for FWH MQTT message cancel.", zap.String("topic", task.topic))
  109. // }
  110. // } else {
  111. // var fwhMqttMsg FwhMqttMsg
  112. // err := json.Unmarshal(payload, &fwhMqttMsg)
  113. // if err != nil {
  114. // zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  115. // newList = append(newList, ch)
  116. // continue
  117. // }
  118. // if task.msg.Snno != fwhMqttMsg.Snno {
  119. // newList = append(newList, ch)
  120. // continue
  121. // }
  122. // ch.ch <- fwhMqttMsg
  123. // }
  124. // }
  125. // resultChListMap[task.subTopic] = newList
  126. // }); err != nil {
  127. // zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
  128. // break
  129. // }
  130. // } else {
  131. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  132. // }
  133. resultCh := make(chan FwhMqttResult)
  134. if err := client.SubscribeMessage(task.subTopic, func(payload []byte) {
  135. var fwhMqttMsg FwhMqttMsg
  136. if err := json.Unmarshal(payload, &fwhMqttMsg); err != nil {
  137. zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  138. resultCh <- FwhMqttResult{
  139. Err: err,
  140. Data: nil,
  141. }
  142. return
  143. }
  144. resultCh <- FwhMqttResult{
  145. Err: nil,
  146. Data: &fwhMqttMsg,
  147. }
  148. }); err != nil {
  149. zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  150. task.resultCh <- FwhMqttResult{
  151. Err: err,
  152. Data: nil,
  153. }
  154. break
  155. }
  156. msgByte, err := json.Marshal(task.msg)
  157. if err != nil {
  158. zap.L().Error("FWH MQTT message marshal failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  159. task.resultCh <- FwhMqttResult{
  160. Err: err,
  161. Data: nil,
  162. }
  163. break
  164. }
  165. if err := client.SendMessageOnce(task.topic, msgByte); err != nil {
  166. zap.L().Error("FWH MQTT send message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  167. task.resultCh <- FwhMqttResult{
  168. Err: err,
  169. Data: nil,
  170. }
  171. break
  172. }
  173. select {
  174. case res, ok := <-resultCh:
  175. if !ok {
  176. task.resultCh <- FwhMqttResult{
  177. Err: fmt.Errorf("FWH MQTT received message channel closed."),
  178. Data: nil,
  179. }
  180. break
  181. }
  182. task.resultCh <- res
  183. case <-task.ctx.Done():
  184. task.resultCh <- FwhMqttResult{
  185. Err: fmt.Errorf("FWH MQTT received message timeout."),
  186. Data: nil,
  187. }
  188. }
  189. case <-time.After(sleepTimeout):
  190. if client.IsConnected() {
  191. zap.L().Info("FWH MQTT no task received within timeout, disconnect.")
  192. client.Disconnect()
  193. }
  194. }
  195. }
  196. }
  197. func sendMessage(ctx context.Context, topic string, msg FwhMqttMsg, subTopic string) (*FwhMqttMsg, error) {
  198. if fwhMqttClient == nil {
  199. return nil, fmt.Errorf("Fwh Mqtt Client Uninitialized.")
  200. }
  201. ctxSend, cancel := context.WithTimeout(ctx, sendMessageTimeout)
  202. defer cancel()
  203. taskMsg := MqttMsg{
  204. topic: topic,
  205. msg: msg,
  206. subTopic: subTopic,
  207. resultCh: make(chan FwhMqttResult, 1),
  208. ctx: ctxSend,
  209. }
  210. taskCh <- taskMsg
  211. for {
  212. select {
  213. case result, ok := <-taskMsg.resultCh:
  214. if !ok {
  215. zap.L().Error("Result channel closed, worker exiting, recreate.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  216. return nil, fmt.Errorf("Result channel closed, worker exiting, recreate.")
  217. }
  218. if result.Err != nil {
  219. return nil, result.Err
  220. }
  221. return result.Data, nil
  222. case <-ctxSend.Done():
  223. zap.L().Info("Result received timeout.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  224. return nil, fmt.Errorf("Result received timeout.")
  225. }
  226. }
  227. }