fwh_mqtt_client.go 8.5 KB

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