fwh_mqtt_client.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. 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 !c.Client.IsConnected() {
  95. if err := c.Client.Connect(); err != nil {
  96. zap.L().Error("FWH MQTT connect failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  97. task.ResultCh <- FwhMqttResult{
  98. Err: fmt.Errorf("FWH MQTT connect failed."),
  99. Data: nil,
  100. }
  101. break
  102. }
  103. }
  104. // 订阅响应消息 - 请求响应无法一一对应,不支持并发请求,改成简单的队列请求
  105. // if _, ok := resultChListMap[task.subTopic]; !ok {
  106. // resultChListMap[task.subTopic] = []*ResultChan{}
  107. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  108. // if err := client.SubscribeMessage(task.subTopic, func(payload []byte) {
  109. // var newList []*ResultChan
  110. // for _, ch := range resultChListMap[task.subTopic] {
  111. // if err := ch.ctx.Err(); err != nil {
  112. // switch err {
  113. // case context.DeadlineExceeded:
  114. // zap.L().Warn("Waiting for FWH MQTT message timeout.", zap.String("topic", task.topic))
  115. // case context.Canceled:
  116. // zap.L().Warn("Waiting for FWH MQTT message cancel.", zap.String("topic", task.topic))
  117. // }
  118. // } else {
  119. // var fwhMqttMsg FwhMqttMsg
  120. // err := json.Unmarshal(payload, &fwhMqttMsg)
  121. // if err != nil {
  122. // zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  123. // newList = append(newList, ch)
  124. // continue
  125. // }
  126. // if task.msg.Snno != fwhMqttMsg.Snno {
  127. // newList = append(newList, ch)
  128. // continue
  129. // }
  130. // ch.ch <- fwhMqttMsg
  131. // }
  132. // }
  133. // resultChListMap[task.subTopic] = newList
  134. // }); err != nil {
  135. // zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
  136. // break
  137. // }
  138. // } else {
  139. // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
  140. // }
  141. var resultCh chan FwhMqttResult
  142. if task.ResTopic != nil {
  143. resultCh = make(chan FwhMqttResult)
  144. if err := c.Client.SubscribeMessage(*task.ResTopic, func(payload []byte) {
  145. var fwhMqttMsg FwhMqttMsg
  146. if err := json.Unmarshal(payload, &fwhMqttMsg); err != nil {
  147. zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.Topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
  148. // resultCh <- FwhMqttResult{
  149. // Err: err,
  150. // Data: nil,
  151. // }
  152. return
  153. }
  154. if task.ResCmdType != nil {
  155. if fwhMqttMsg.CmdType == *task.ResCmdType {
  156. resultCh <- FwhMqttResult{
  157. Err: nil,
  158. Data: &fwhMqttMsg,
  159. }
  160. }
  161. } else {
  162. resultCh <- FwhMqttResult{
  163. Err: nil,
  164. Data: &fwhMqttMsg,
  165. }
  166. }
  167. }); err != nil {
  168. zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  169. task.ResultCh <- FwhMqttResult{
  170. Err: err,
  171. Data: nil,
  172. }
  173. break
  174. }
  175. }
  176. msgByte, err := json.Marshal(task.Msg)
  177. if err != nil {
  178. zap.L().Error("FWH MQTT message marshal failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  179. task.ResultCh <- FwhMqttResult{
  180. Err: err,
  181. Data: nil,
  182. }
  183. break
  184. }
  185. if err := c.Client.SendMessageOnce(task.Topic, msgByte); err != nil {
  186. zap.L().Error("FWH MQTT send message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
  187. task.ResultCh <- FwhMqttResult{
  188. Err: err,
  189. Data: nil,
  190. }
  191. break
  192. }
  193. if resultCh != nil {
  194. select {
  195. case res, ok := <-resultCh:
  196. if !ok {
  197. task.ResultCh <- FwhMqttResult{
  198. Err: fmt.Errorf("FWH MQTT received message channel closed."),
  199. Data: nil,
  200. }
  201. break
  202. }
  203. task.ResultCh <- res
  204. case <-task.Ctx.Done():
  205. task.ResultCh <- FwhMqttResult{
  206. Err: fmt.Errorf("FWH MQTT received message timeout."),
  207. Data: nil,
  208. }
  209. }
  210. } else {
  211. task.ResultCh <- FwhMqttResult{
  212. Err: nil,
  213. Data: nil,
  214. }
  215. }
  216. case <-time.After(sleepTimeout):
  217. if c.Client.IsConnected() {
  218. zap.L().Info("FWH MQTT no task received within timeout, disconnect.")
  219. c.Client.Disconnect()
  220. }
  221. }
  222. }
  223. }
  224. func (c *FwhMqttClient) sendMessage(ctx context.Context, topicName string, msg FwhMqttMsg, resTopicName *string, resCmdType *uint) (*FwhMqttMsg, error) {
  225. if c.Client == nil {
  226. return nil, fmt.Errorf("Fwh Mqtt Client Uninitialized.")
  227. }
  228. ctxSend, cancel := context.WithTimeout(ctx, sendMessageTimeout)
  229. defer cancel()
  230. topic := topicPrefixS2C + topicName
  231. var resTopic *string
  232. if resTopicName != nil {
  233. resTopicStr := topicPrefixC2S + *resTopicName
  234. resTopic = &resTopicStr
  235. }
  236. taskMsg := MqttMsg{
  237. Topic: topic,
  238. Msg: msg,
  239. ResTopic: resTopic,
  240. ResCmdType: resCmdType,
  241. ResultCh: make(chan FwhMqttResult, 1),
  242. Ctx: ctxSend,
  243. }
  244. c.TaskCh <- taskMsg
  245. for {
  246. select {
  247. case result, ok := <-taskMsg.ResultCh:
  248. if !ok {
  249. zap.L().Error("Result channel closed, worker exiting, recreate.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  250. return nil, fmt.Errorf("Result channel closed, worker exiting, recreate.")
  251. }
  252. if result.Err != nil {
  253. return nil, result.Err
  254. }
  255. return result.Data, nil
  256. case <-ctxSend.Done():
  257. zap.L().Info("Result received timeout.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
  258. return nil, fmt.Errorf("Result received timeout.")
  259. }
  260. }
  261. }
  262. // 仅发送不等待响应结果
  263. func (c *FwhMqttClient) sendMessageOnce(ctx context.Context, topicName string, msg FwhMqttMsg) error {
  264. _, err := c.sendMessage(ctx, topicName, msg, nil, nil)
  265. return err
  266. }
  267. // 发送消息后,等待响应结果
  268. func (c *FwhMqttClient) sendMessageWait(ctx context.Context, topicName string, msg FwhMqttMsg, resTopicName string, resCmdType uint) (*FwhMqttMsg, error) {
  269. res, err := c.sendMessage(ctx, topicName, msg, &resTopicName, &resCmdType)
  270. return res, err
  271. }