| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- package fwh_mqtt
- import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/json"
- "fmt"
- "fwh-toolbox-server/config"
- "fwh-toolbox-server/utils/mqttClient"
- "os"
- "strconv"
- "time"
- "go.uber.org/zap"
- )
- const (
- fwhClientIdPrefix = "fwh_xlog_mqtt_client_"
- sleepTimeout = 5 * time.Minute
- sendMessageTimeout = 1 * time.Minute
- topicPrefixS2C = "fwh/s2c/"
- topicPrefixC2S = "fwh/c2s/"
- )
- type FwhMqttResult struct {
- Err error
- Data *FwhMqttMsg
- }
- type FwhMqttMsg struct {
- CmdType uint `json:"cmdType"` // 业务类型标识
- Crc string `json:"crc"` // dataArea 数据字符串的CRC
- DataArea interface{} `json:"dataArea"` // 数据域
- EquipNo string `json:"equipNo"` // 设备编号 SN
- Len int `json:"len"` // dataArea 数据长度
- Snno uint `json:"snno"` // 序号
- TimeStamp int64 `json:"timeStamp"` // 时间戳
- Type uint `json:"type"` // 设备类型 0:设备网关IBG
- }
- type MqttMsg struct {
- Topic string // 发送 Topic
- Msg FwhMqttMsg
- ResTopic *string // 响应 Topic
- ResCmdType *uint
- ResultCh chan FwhMqttResult
- Ctx context.Context
- }
- type ResultChan struct {
- ch chan FwhMqttMsg
- ctx context.Context
- }
- type FwhMqttClient struct {
- Client *mqttClient.MqttClient
- TaskCh chan MqttMsg
- }
- var fwhMqttClient = FwhMqttClient{
- Client: nil,
- TaskCh: make(chan MqttMsg, 1000),
- }
- func InitFwhMqttClient(cfg *config.FwhMqttConfig) error {
- // 证书配置
- caCert, err := os.ReadFile(cfg.FwhMqttCaUrl)
- if err != nil {
- panic(err)
- }
- caCertPool := x509.NewCertPool()
- caCertPool.AppendCertsFromPEM(caCert)
- cert, err := tls.LoadX509KeyPair(cfg.FwhMqttCertUrl, cfg.FwhMqttKeyUrl)
- if err != nil {
- panic(err)
- }
- client, err := mqttClient.CreateMqttClient(mqttClient.MqttClientOpt{
- Broker: cfg.FwhMqttBroker,
- ClientID: fwhClientIdPrefix + strconv.FormatInt(time.Now().Unix(), 10),
- TLS: &tls.Config{
- Certificates: []tls.Certificate{cert},
- RootCAs: caCertPool,
- },
- })
- if err != nil {
- zap.L().Error("Init FWH MQTT client failed.")
- return err
- }
- fwhMqttClient.Client = client
- zap.L().Info("Init FWH MQTT client success.")
- go fwhMqttClient.fwhMqttWorker()
- return nil
- }
- func (c *FwhMqttClient) fwhMqttWorker() {
- for {
- select {
- case task, ok := <-c.TaskCh:
- if !ok {
- zap.L().Error("Channel closed, worker exiting, recreate.")
- c.TaskCh = make(chan MqttMsg, 1000)
- break
- }
- if err := c.Client.Connect(); err != nil {
- zap.L().Error("FWH MQTT connect failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
- task.ResultCh <- FwhMqttResult{
- Err: fmt.Errorf("FWH MQTT connect failed."),
- Data: nil,
- }
- break
- }
- // 订阅响应消息 - 请求响应无法一一对应,不支持并发请求,改成简单的队列请求
- // if _, ok := resultChListMap[task.subTopic]; !ok {
- // resultChListMap[task.subTopic] = []*ResultChan{}
- // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
- // if err := client.SubscribeMessage(task.subTopic, func(payload []byte) {
- // var newList []*ResultChan
- // for _, ch := range resultChListMap[task.subTopic] {
- // if err := ch.ctx.Err(); err != nil {
- // switch err {
- // case context.DeadlineExceeded:
- // zap.L().Warn("Waiting for FWH MQTT message timeout.", zap.String("topic", task.topic))
- // case context.Canceled:
- // zap.L().Warn("Waiting for FWH MQTT message cancel.", zap.String("topic", task.topic))
- // }
- // } else {
- // var fwhMqttMsg FwhMqttMsg
- // err := json.Unmarshal(payload, &fwhMqttMsg)
- // if err != nil {
- // zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
- // newList = append(newList, ch)
- // continue
- // }
- // if task.msg.Snno != fwhMqttMsg.Snno {
- // newList = append(newList, ch)
- // continue
- // }
- // ch.ch <- fwhMqttMsg
- // }
- // }
- // resultChListMap[task.subTopic] = newList
- // }); err != nil {
- // zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)))
- // break
- // }
- // } else {
- // resultChListMap[task.subTopic] = append(resultChListMap[task.subTopic], &ResultChan{ch: task.resultCh, ctx: task.ctx})
- // }
- var resultCh chan FwhMqttResult
- if task.ResTopic != nil {
- resultCh = make(chan FwhMqttResult)
- if err := c.Client.SubscribeMessage(*task.ResTopic, func(payload []byte) {
- var fwhMqttMsg FwhMqttMsg
- if err := json.Unmarshal(payload, &fwhMqttMsg); err != nil {
- zap.L().Error("Result Unmarshal failed.(FwhMqttMsg)", zap.String("topic", task.Topic), zap.String("payload", string(payload)), zap.String("err", err.Error()))
- // resultCh <- FwhMqttResult{
- // Err: err,
- // Data: nil,
- // }
- return
- }
- if task.ResCmdType != nil {
- if fwhMqttMsg.CmdType == *task.ResCmdType {
- resultCh <- FwhMqttResult{
- Err: nil,
- Data: &fwhMqttMsg,
- }
- }
- } else {
- resultCh <- FwhMqttResult{
- Err: nil,
- Data: &fwhMqttMsg,
- }
- }
- }); err != nil {
- zap.L().Error("FWH MQTT subscribe message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
- task.ResultCh <- FwhMqttResult{
- Err: err,
- Data: nil,
- }
- break
- }
- }
- msgByte, err := json.Marshal(task.Msg)
- if err != nil {
- zap.L().Error("FWH MQTT message marshal failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
- task.ResultCh <- FwhMqttResult{
- Err: err,
- Data: nil,
- }
- break
- }
- if err := c.Client.SendMessageOnce(task.Topic, msgByte); err != nil {
- zap.L().Error("FWH MQTT send message failed.", zap.String("msg", fmt.Sprintf("%+v", task)), zap.String("err", err.Error()))
- task.ResultCh <- FwhMqttResult{
- Err: err,
- Data: nil,
- }
- break
- }
- if resultCh != nil {
- select {
- case res, ok := <-resultCh:
- if !ok {
- task.ResultCh <- FwhMqttResult{
- Err: fmt.Errorf("FWH MQTT received message channel closed."),
- Data: nil,
- }
- break
- }
- task.ResultCh <- res
- case <-task.Ctx.Done():
- task.ResultCh <- FwhMqttResult{
- Err: fmt.Errorf("FWH MQTT received message timeout."),
- Data: nil,
- }
- }
- } else {
- task.ResultCh <- FwhMqttResult{
- Err: nil,
- Data: nil,
- }
- }
- case <-time.After(sleepTimeout):
- if c.Client.IsConnected() {
- zap.L().Info("FWH MQTT no task received within timeout, disconnect.")
- c.Client.Disconnect()
- }
- }
- }
- }
- func (c *FwhMqttClient) sendMessage(ctx context.Context, topicName string, msg FwhMqttMsg, resTopicName *string, resCmdType *uint) (*FwhMqttMsg, error) {
- if c.Client == nil {
- return nil, fmt.Errorf("Fwh Mqtt Client Uninitialized.")
- }
- ctxSend, cancel := context.WithTimeout(ctx, sendMessageTimeout)
- defer cancel()
- topic := topicPrefixS2C + topicName
- var resTopic *string
- if resTopicName != nil {
- resTopicStr := topicPrefixC2S + *resTopicName
- resTopic = &resTopicStr
- }
- taskMsg := MqttMsg{
- Topic: topic,
- Msg: msg,
- ResTopic: resTopic,
- ResCmdType: resCmdType,
- ResultCh: make(chan FwhMqttResult, 1),
- Ctx: ctxSend,
- }
- c.TaskCh <- taskMsg
- for {
- select {
- case result, ok := <-taskMsg.ResultCh:
- if !ok {
- zap.L().Error("Result channel closed, worker exiting, recreate.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
- return nil, fmt.Errorf("Result channel closed, worker exiting, recreate.")
- }
- if result.Err != nil {
- return nil, result.Err
- }
- return result.Data, nil
- case <-ctxSend.Done():
- zap.L().Info("Result received timeout.", zap.String("topic", topic), zap.String("msg", fmt.Sprintf("%+v", msg)))
- return nil, fmt.Errorf("Result received timeout.")
- }
- }
- }
- // 仅发送不等待响应结果
- func (c *FwhMqttClient) sendMessageOnce(ctx context.Context, topicName string, msg FwhMqttMsg) error {
- _, err := c.sendMessage(ctx, topicName, msg, nil, nil)
- return err
- }
- // 发送消息后,等待响应结果
- func (c *FwhMqttClient) sendMessageWait(ctx context.Context, topicName string, msg FwhMqttMsg, resTopicName string, resCmdType uint) (*FwhMqttMsg, error) {
- res, err := c.sendMessage(ctx, topicName, msg, &resTopicName, &resCmdType)
- return res, err
- }
|