fwh_mqtt_client.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. SubCmdType *uint
  39. ResultCh chan FwhMqttResult
  40. Ctx context.Context
  41. }
  42. type ResultChan struct {
  43. ch chan FwhMqttMsg
  44. ctx context.Context
  45. }
  46. type FwhMqttClient struct {
  47. Client *mqttClient.MqttClient
  48. TaskCh chan MqttMsg
  49. }
  50. var fwhMqttClient = FwhMqttClient{
  51. Client: nil,
  52. TaskCh: make(chan MqttMsg, 1000),
  53. }
  54. func InitFwhMqttClient(cfg *config.FwhMqttConfig) error {
  55. // 证书配置
  56. caCert, err := os.ReadFile(cfg.FwhMqttCaUrl)
  57. if err != nil {
  58. panic(err)
  59. }
  60. caCertPool := x509.NewCertPool()
  61. caCertPool.AppendCertsFromPEM(caCert)
  62. cert, err := tls.LoadX509KeyPair(cfg.FwhMqttCertUrl, cfg.FwhMqttKeyUrl)
  63. if err != nil {
  64. panic(err)
  65. }
  66. client, err := mqttClient.CreateMqttClient(mqttClient.MqttClientOpt{
  67. Broker: cfg.FwhMqttBroker,
  68. ClientID: fwhClientIdPrefix + strconv.FormatInt(time.Now().Unix(), 10),
  69. TLS: &tls.Config{
  70. Certificates: []tls.Certificate{cert},
  71. RootCAs: caCertPool,
  72. },
  73. })
  74. if err != nil {
  75. zap.L().Error("Init FWH MQTT client failed.")
  76. return err
  77. }
  78. fwhMqttClient.Client = client
  79. zap.L().Info("Init FWH MQTT client success.")
  80. go fwhMqttClient.fwhMqttWorker()
  81. return nil
  82. }
  83. func (c *FwhMqttClient) fwhMqttWorker() {
  84. for {
  85. select {
  86. case task, ok := <-c.TaskCh:
  87. if !ok {
  88. zap.L().Error("Channel closed, worker exiting, recreate.")
  89. c.TaskCh = make(chan MqttMsg, 1000)
  90. break
  91. }
  92. if err := c.Client.Connect(); err != nil {
  93. zap.L().Error("FWH MQTT connect failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
  94. task.ResultCh <- FwhMqttResult{
  95. Err: fmt.Errorf("FWH MQTT connect failed."),
  96. Data: nil,
  97. }
  98. break
  99. }
  100. // 订阅响应消息 - 请求响应无法一一对应,不支持并发请求,改成简单的队列请求
  101. // if _, ok := resultChListMap[task.subTopic]; !ok {
  102. // resultChListMap[task.subTopic] = []*ResultChan{}
  103. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  104. // if err := client.SubscribeMessage(task.subTopic, func(payload []byte) {
  105. // var newList []*ResultChan
  106. // for _, ch := range resultChListMap[task.subTopic] {
  107. // if err := ch.ctx.Err(); err != nil {
  108. // switch err {
  109. // case context.DeadlineExceeded:
  110. // zap.L().Warn("Waiting for FWH MQTT message timeout.", zap.String("topic", task.topic))
  111. // case context.Canceled:
  112. // zap.L().Warn("Waiting for FWH MQTT message cancel.", zap.String("topic", task.topic))
  113. // }
  114. // } else {
  115. // var fwhMqttMsg FwhMqttMsg
  116. // err := json.Unmarshal(payload, &fwhMqttMsg)
  117. // if err != nil {
  118. // zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  119. // newList = append(newList, ch)
  120. // continue
  121. // }
  122. // if task.msg.Snno != fwhMqttMsg.Snno {
  123. // newList = append(newList, ch)
  124. // continue
  125. // }
  126. // ch.ch <- fwhMqttMsg
  127. // }
  128. // }
  129. // resultChListMap[task.subTopic] = newList
  130. // }); err != nil {
  131. // zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
  132. // break
  133. // }
  134. // } else {
  135. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  136. // }
  137. var resultCh chan FwhMqttResult
  138. if task.SubTopic != nil {
  139. resultCh = make(chan FwhMqttResult)
  140. if err := c.Client.SubscribeMessage(*task.SubTopic, func(payload []byte) {
  141. var fwhMqttMsg FwhMqttMsg
  142. if err := json.Unmarshal(payload, &fwhMqttMsg); err != nil {
  143. zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.Topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  144. // resultCh <- FwhMqttResult{
  145. // Err: err,
  146. // Data: nil,
  147. // }
  148. return
  149. }
  150. if task.SubCmdType != nil {
  151. if fwhMqttMsg.CmdType == *task.SubCmdType {
  152. resultCh <- FwhMqttResult{
  153. Err: nil,
  154. Data: &fwhMqttMsg,
  155. }
  156. }
  157. } else {
  158. resultCh <- FwhMqttResult{
  159. Err: nil,
  160. Data: &fwhMqttMsg,
  161. }
  162. }
  163. }); err != nil {
  164. zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  165. task.ResultCh <- FwhMqttResult{
  166. Err: err,
  167. Data: nil,
  168. }
  169. break
  170. }
  171. }
  172. msgByte, err := json.Marshal(task.Msg)
  173. if err != nil {
  174. zap.L().Error("FWH MQTT message marshal failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  175. task.ResultCh <- FwhMqttResult{
  176. Err: err,
  177. Data: nil,
  178. }
  179. break
  180. }
  181. if err := c.Client.SendMessageOnce(task.Topic, msgByte); err != nil {
  182. zap.L().Error("FWH MQTT send message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  183. task.ResultCh <- FwhMqttResult{
  184. Err: err,
  185. Data: nil,
  186. }
  187. break
  188. }
  189. if resultCh != nil {
  190. select {
  191. case res, ok := <-resultCh:
  192. if !ok {
  193. task.ResultCh <- FwhMqttResult{
  194. Err: fmt.Errorf("FWH MQTT received message channel closed."),
  195. Data: nil,
  196. }
  197. break
  198. }
  199. task.ResultCh <- res
  200. case <-task.Ctx.Done():
  201. task.ResultCh <- FwhMqttResult{
  202. Err: fmt.Errorf("FWH MQTT received message timeout."),
  203. Data: nil,
  204. }
  205. }
  206. } else {
  207. task.ResultCh <- FwhMqttResult{
  208. Err: nil,
  209. Data: nil,
  210. }
  211. }
  212. case <-time.After(sleepTimeout):
  213. if c.Client.IsConnected() {
  214. zap.L().Info("FWH MQTT no task received within timeout, disconnect.")
  215. c.Client.Disconnect()
  216. }
  217. }
  218. }
  219. }
  220. func (c *FwhMqttClient) sendMessage(ctx context.Context, topic string, msg FwhMqttMsg, subTopic *string, subCmdType *uint) (*FwhMqttMsg, error) {
  221. if c.Client == nil {
  222. return nil, fmt.Errorf("Fwh Mqtt Client Uninitialized.")
  223. }
  224. ctxSend, cancel := context.WithTimeout(ctx, sendMessageTimeout)
  225. defer cancel()
  226. taskMsg := MqttMsg{
  227. Topic: topic,
  228. Msg: msg,
  229. SubTopic: subTopic,
  230. SubCmdType: subCmdType,
  231. ResultCh: make(chan FwhMqttResult, 1),
  232. Ctx: ctxSend,
  233. }
  234. c.TaskCh <- taskMsg
  235. for {
  236. select {
  237. case result, ok := <-taskMsg.ResultCh:
  238. if !ok {
  239. zap.L().Error("Result channel closed, worker exiting, recreate.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  240. return nil, fmt.Errorf("Result channel closed, worker exiting, recreate.")
  241. }
  242. if result.Err != nil {
  243. return nil, result.Err
  244. }
  245. return result.Data, nil
  246. case <-ctxSend.Done():
  247. zap.L().Info("Result received timeout.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  248. return nil, fmt.Errorf("Result received timeout.")
  249. }
  250. }
  251. }
  252. // 仅发送不等待响应结果
  253. func (c *FwhMqttClient) sendMessageOnce(ctx context.Context, topic string, msg FwhMqttMsg) error {
  254. _, err := c.sendMessage(ctx, topic, msg, nil, nil)
  255. return err
  256. }
  257. // 发送消息后,等待响应结果
  258. func (c *FwhMqttClient) sendMessageWait(ctx context.Context, topic string, msg FwhMqttMsg, subTopic string, subCmdType uint) (*FwhMqttMsg, error) {
  259. res, err := c.sendMessage(ctx, topic, msg, &subTopic, &subCmdType)
  260. return res, err
  261. }