hlzbconcentrator.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. package main
  2. import (
  3. "context"
  4. "runtime/debug"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/sirupsen/logrus"
  10. "lc/common/models"
  11. "lc/common/mqtt"
  12. "lc/common/protocol"
  13. "lc/common/util"
  14. )
  15. //TODO 每次调光记录入库确认,灯的实际功率显示
  16. // HlZigbeeConcentrator TODO 开灯自动要关灯
  17. // 集控器类定义
  18. type HlZigbeeConcentrator struct {
  19. ctx context.Context
  20. cancel context.CancelFunc
  21. lock sync.Mutex
  22. tenant string //租户
  23. gid string //基础数据,网关ID
  24. did string //基础数据,集控器设备编码
  25. tid uint16 //基础数据,集控器物模型ID
  26. mapLamp map[string]*HlZigbeeLampController //单灯数据,编号唯一
  27. state uint8 //实时数据,0在线,1离线
  28. lastStateTime time.Time //实时数据,最新状态时间
  29. lastDataTime time.Time //最新数据时间
  30. nextHourTime time.Time //实时数据
  31. queue *util.MlQueue //数据队列
  32. errCnt uint //错误计数
  33. mapTopicHandle map[string]func(m *mqtt.Message)
  34. }
  35. // NewHlZigbeeConcentrator 新建集控器,并指定topic对应事件
  36. func NewHlZigbeeConcentrator(tenant, did string) *HlZigbeeConcentrator {
  37. ctx, cancel := context.WithCancel(context.Background())
  38. hlc := HlZigbeeConcentrator{
  39. ctx: ctx,
  40. cancel: cancel,
  41. tenant: tenant,
  42. did: did,
  43. mapLamp: make(map[string]*HlZigbeeLampController),
  44. queue: util.NewQueue(1000),
  45. mapTopicHandle: make(map[string]func(m *mqtt.Message)),
  46. }
  47. //改成只有一个
  48. hlc.mapTopicHandle[GetHLTopicUp()] = hlc.handleTopicUp
  49. return &hlc
  50. }
  51. func (o *HlZigbeeConcentrator) PutMessage(m *mqtt.Message) {
  52. o.queue.Put(m)
  53. }
  54. func (o *HlZigbeeConcentrator) Start() {
  55. go o.HandleQueue()
  56. }
  57. func (o *HlZigbeeConcentrator) Stop() {
  58. o.cancel()
  59. }
  60. // HandleQueue 启用队列循环,不同的主题调用不同的fn
  61. func (o *HlZigbeeConcentrator) HandleQueue() {
  62. defer func() {
  63. if err := recover(); err != nil {
  64. logrus.Error("HlZigbeeConcentrator.HandleQueue发生异常:", string(debug.Stack()))
  65. time.Sleep(5 * time.Second)
  66. go o.HandleQueue()
  67. }
  68. }()
  69. o.UpdateDID() //启动时更新本地灯控数组一次
  70. o.GetOnlineMsg()
  71. var obj protocol.HLWLZB_Frequency_Down
  72. if str, err := obj.EnCode(o.did, 10); err == nil { //启动时下发到设备的"上发频率"
  73. topic := GetHLTopicDown(o.tenant, protocol.DT_CONCENTRATOR, o.did, protocol.TP_CHZB_SET_ONOFFTIME)
  74. err = GetHlMqttMgr().Publish(topic, str, mqtt.AtLeastOnce)
  75. if err != nil {
  76. logrus.Error("HlZigbeeConcentrator.HandleQueue:主题发送失败:", topic, str)
  77. }
  78. }
  79. var exit = false
  80. timer := time.NewTicker(30 * time.Minute)
  81. timer5 := time.NewTicker(5 * time.Minute)
  82. //每天15点半同步日出日落时间
  83. var SyncSunset = util.New(util.MlNow()).BeginningOfDay().Add(14*time.Hour + 30*time.Minute)
  84. for {
  85. select { //if
  86. case <-o.ctx.Done():
  87. logrus.Error("HlZigbeeConcentrator.HandleQueue即将退出,原因:", o.ctx.Err())
  88. exit = true
  89. case <-timer.C: //每隔30分钟执行一次
  90. o.UpdateState() //更新集控器状态,防止无数据状态不更新
  91. o.UpdateLampControllerState() //更新灯控状态,防止无数据状态不更新
  92. case <-timer5.C: //每隔5分钟执行一次
  93. o.UpdateDID() //更新灯控设备编码 更新本地灯控数组
  94. //同步日出日落时间
  95. if util.MlNow().After(SyncSunset) {
  96. if err := o.SyncSunset(); err == nil {
  97. SyncSunset = SyncSunset.AddDate(0, 0, 1)
  98. }
  99. }
  100. default:
  101. //从队列中获取指令执行fn
  102. if m, ok, quantity := o.queue.Get(); ok {
  103. if mm, ok := m.(*mqtt.Message); ok {
  104. if f, ok := o.mapTopicHandle[mm.Topic()]; ok {
  105. f(mm)
  106. } else {
  107. logrus.Error("HlZigbeeConcentrator.HandleQueue:不支持的主题:", mm.Topic())
  108. }
  109. }
  110. } else if quantity == 0 {
  111. if exit {
  112. return
  113. }
  114. time.Sleep(100 * time.Millisecond)
  115. }
  116. }
  117. }
  118. }
  119. // SyncSunset 日出日落 平台->终端
  120. // TODO
  121. func (o *HlZigbeeConcentrator) SyncSunset() error {
  122. arr, err := models.GetZigbeeLampstrategyByconcentrator(o.did)
  123. if err != nil {
  124. logrus.Errorf("集控器[%s]从数据库读取日出日落时间错误:%s", o.did, err.Error())
  125. return err
  126. }
  127. if len(arr) == 0 {
  128. return nil
  129. }
  130. //Strategy相同的一起发送,不同的分批发送
  131. mapTime := make(map[string]*protocol.CHZB_OnOffTime) //策略时间
  132. mapNumber := make(map[string][]uint32) //策略灯控编号,注意非编码
  133. for _, v := range arr {
  134. mapNumber[v.ID] = append(mapNumber[v.ID], uint32(v.Number))
  135. if _, ok := mapTime[v.Strategy]; ok {
  136. //已计算日出日落时间的,不再重复计算
  137. continue
  138. }
  139. var oot protocol.CHZB_OnOffTime
  140. var ls []models.LampStrategy
  141. if err := json.UnmarshalFromString(v.TimeInfo, &ls); err == nil && len(ls) > 0 {
  142. oot.Brightness = uint8(ls[0].Brightness)
  143. }
  144. //计算时间
  145. if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil {
  146. onhour, _ := strconv.Atoi(strings.Split(set, ":")[0])
  147. onminite, _ := strconv.Atoi(strings.Split(set, ":")[1])
  148. offhour, _ := strconv.Atoi(strings.Split(rise, ":")[0])
  149. offminite, _ := strconv.Atoi(strings.Split(rise, ":")[1])
  150. oot.OnHour = uint8(onhour)
  151. oot.OnMinite = uint8(onminite)
  152. oot.OffHour = uint8(offhour)
  153. oot.OffMinite = uint8(offminite)
  154. }
  155. mapTime[v.Strategy] = &oot
  156. }
  157. for k, v := range mapNumber {
  158. if oot, ok := mapTime[k]; ok {
  159. var obj protocol.Pack_SetOnOffTime
  160. seq := GetNextSeq()
  161. if str, err := obj.EnCode(o.did, o.gid, seq, v, []protocol.CHZB_OnOffTime{*oot}); err == nil {
  162. topic := GetTopic(o.tenant, protocol.DT_CONCENTRATOR, o.did, protocol.TP_CHZB_SET_ONOFFTIME) //设置开关灯时间段,平台->终端
  163. err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce)
  164. if err != nil {
  165. logrus.Errorf("SyncSunset:集控器[%s]对灯控[%v]发布日出日落消息错误:%s", o.did, v, err.Error())
  166. }
  167. var msg string
  168. if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil {
  169. msg = string(msg0)
  170. } else {
  171. msg = str
  172. }
  173. odb := models.DeviceCmdRecord{
  174. ID: seq,
  175. GID: o.gid,
  176. DID: o.did,
  177. Topic: topic,
  178. Message: msg,
  179. State: 0,
  180. }
  181. if err := models.G_db.Create(&odb).Error; err != nil {
  182. logrus.Errorf("集控器[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", o.did, v, err.Error())
  183. } else {
  184. logrus.Errorf("集控器[%s]对灯控[%v]发布日出日落时间时指令入库成功", o.did, v)
  185. }
  186. }
  187. }
  188. }
  189. return nil
  190. }
  191. // UpdateDID 更新灯控的集控,更新集控的网关id
  192. func (o *HlZigbeeConcentrator) UpdateDID() {
  193. if arr, err := models.GetLampHlControllerByConcentrator(o.did); err == nil { //对集控did下所有灯控
  194. for _, v := range arr { //更新灯控设备编码
  195. if ld, ok := o.mapLamp[v.ID]; ok {
  196. ld.SetDID(v.Concentrator, v.ID) //集id,灯id
  197. if o.state == protocol.FAILED { //离线后下属灯都设置离线
  198. ld.SetOnOffLine(o.state)
  199. }
  200. } else {
  201. var ld HlZigbeeLampController //第一次则加入
  202. ld.SetDID(v.Concentrator, v.ID)
  203. o.mapLamp[v.ID] = &ld //更新本地单灯数组,需要用到
  204. }
  205. // 边缘(设备) // 设备后台 (网关后台、与java对接)
  206. o.gid = v.GID //设置集控器网关id
  207. }
  208. }
  209. }
  210. // 事件--改变状态
  211. func (o *HlZigbeeConcentrator) handleStateChange(t time.Time) {
  212. //最新状态
  213. state := uint8(0)
  214. if o.errCnt >= 10 {
  215. state = 1
  216. } else if o.errCnt == 0 {
  217. state = 0
  218. } else {
  219. return
  220. }
  221. //状态处理
  222. if o.lastStateTime.IsZero() || o.state == 0xff {
  223. t0, s0, err := getState(o.did) //取redis
  224. if err != nil {
  225. o.state = state
  226. o.lastStateTime = t
  227. return
  228. }
  229. o.state = s0
  230. o.lastStateTime = t0
  231. }
  232. if o.state == 0 && state == 1 { //在线->离线
  233. o.GetWillMsg()
  234. GetEventMgr().PushEvent(&EventObject{ID: o.did, EventType: models.ET_OFFLINE, Time: t})
  235. } else if o.state == 1 && state == 0 { //离线->在线
  236. o.GetOnlineMsg()
  237. GetEventMgr().PushEvent(&EventObject{ID: o.did, EventType: models.ET_ONLINE, Time: t})
  238. }
  239. o.state = state
  240. o.lastStateTime = t
  241. }
  242. // UpdateState 每隔一分钟,集控置为离线
  243. func (o *HlZigbeeConcentrator) UpdateState() {
  244. if o.lastStateTime.IsZero() {
  245. t0, s0, err := getState(o.did)
  246. if err == nil {
  247. o.state = s0
  248. o.lastStateTime = t0
  249. }
  250. }
  251. if o.state == protocol.FAILED || //若之前是离线状态的,则不修改状态
  252. (!o.lastDataTime.IsZero() && util.MlNow().Sub(o.lastDataTime).Minutes() < OfflineInterval) {
  253. return
  254. }
  255. //如果之前一直是在线状态的,则置为离线;若之前是离线状态的,则不修改状态
  256. if o.state == protocol.SUCCESS {
  257. o.state = protocol.FAILED
  258. o.lastStateTime = util.MlNow()
  259. GetEventMgr().PushEvent(&EventObject{ID: o.did, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
  260. cacheState(o.did, o.lastStateTime.Format("2006-01-02 15:04:05"), o.state) //改变redis
  261. o.GetWillMsg()
  262. }
  263. }
  264. // UpdateLampControllerState 每隔一分钟,灯控置为离线
  265. func (o *HlZigbeeConcentrator) UpdateLampControllerState() {
  266. for _, v := range o.mapLamp {
  267. v.UpdateState()
  268. }
  269. }
  270. // {"masterSn":"301056","msgType":"offline","snGroup":[{"serialNumber":"301056"}],
  271. // "ts":"1653445142","uuid":"40a47d85-2ebb-41ab-a6d9-6aad4b934d02"} 只有集控离线事件
  272. func (o *HlZigbeeConcentrator) onofflineProcess(s string) {
  273. var obj protocol.HLWLZB_OnOfflineStatus_Up
  274. if err := obj.DeCode(s); err != nil {
  275. logrus.Errorf("HLWLZB_OnOfflineStatus_Up====%s解析错误:%s", s, err.Error())
  276. return
  277. }
  278. i64, err1 := strconv.ParseInt(obj.Ts, 10, 62)
  279. t, err2 := util.MlParseTime(util.Unix2Time(i64))
  280. if err1 != nil || err2 != nil {
  281. logrus.Errorf("HLWLZB_OnOfflineStatus_Up====%s时间解析错误:%v %v", obj.Ts, err1, err2)
  282. return
  283. }
  284. if obj.MasterSn == o.did {
  285. if obj.MsgType == protocol.MT_ONLINE { //上线
  286. GetEventMgr().PushEvent(&EventObject{ID: o.did, EventType: models.ET_ONLINE, Time: t})
  287. o.state = 0
  288. o.lastStateTime = t
  289. o.errCnt = 0
  290. o.GetOnlineMsg()
  291. } else if obj.MsgType == protocol.MT_OFFLINE { //离线
  292. GetEventMgr().PushEvent(&EventObject{ID: o.did, EventType: models.ET_OFFLINE, Time: t})
  293. o.state = 1
  294. o.lastStateTime = t
  295. o.errCnt = 10
  296. o.GetWillMsg()
  297. }
  298. }
  299. }
  300. // 存灯数据
  301. // {"electricity":"0.0","energy":"0.64","lampStatus":"off","lux":"0.0","power":"0.0",
  302. // "powerFactor":"0.0","serialNumber":"301057","voltage":"226.0"}
  303. func (o *HlZigbeeConcentrator) reportProcess(s string) {
  304. var obj protocol.HLWLZB_QueryLampStatus_Back
  305. if err := obj.DeCode(s); err != nil {
  306. logrus.Errorf("HLWLZB_QueryLampStatus_Back====%s解析错误:%s", s, err.Error())
  307. return
  308. }
  309. i64, err := strconv.ParseInt(obj.Ts, 10, 62)
  310. t, err := util.MlParseTime(util.Unix2Time(i64))
  311. if err != nil {
  312. logrus.Errorf("时间[%s]解析错误:%s", obj.Ts, err.Error())
  313. return
  314. }
  315. //o.tid = obj.Data.TID //更新物模型TID
  316. //o.gid = obj.Gid 要通过数据库查询
  317. //处理集控器定时上报的单灯数据
  318. var errCnt_ uint = 0
  319. for _, v := range obj.SnGroup { //dev_data_XXX 更新数据 9个参数入库
  320. //未找到的等待下次更新灯控信息
  321. if ld, ok := o.mapLamp[v.SerialNumber]; ok { //对应单灯
  322. var cl protocol.CHZB_LampData
  323. var err2, err3, err4, err5, err6, err7 error
  324. cl.Data = make(map[uint16]float64)
  325. if v.LampStatus == "on" {
  326. cl.Data[1] = float64(1)
  327. } else {
  328. cl.Data[1] = float64(0)
  329. }
  330. cl.Data[0] = float64(0)
  331. cl.Data[3] = float64(0)
  332. cl.Data[2], err2 = strconv.ParseFloat(v.Electricity, 64)
  333. cl.Data[4], err4 = strconv.ParseFloat(v.Lux, 64)
  334. cl.Data[5], err5 = strconv.ParseFloat(v.Power, 64)
  335. cl.Data[6], err6 = strconv.ParseFloat(v.PowerFactor, 64)
  336. cl.Data[7], err7 = strconv.ParseFloat(v.Voltage, 64)
  337. cl.Data[8], err3 = strconv.ParseFloat(v.Energy, 64)
  338. if err2 != nil || err3 != nil || err5 != nil || err6 != nil || err7 != nil || err4 != nil {
  339. str, err := json.MarshalToString(v)
  340. logrus.Errorf("report_process数据[%s]解析错误:%v", str, err)
  341. continue
  342. }
  343. ld.HandleData(o.tenant, o.gid, v.SerialNumber /*灯*/, o.tid, t, &cl)
  344. }
  345. if v.State == protocol.FAILED {
  346. errCnt_++
  347. }
  348. }
  349. if errCnt_ == uint(len(obj.SnGroup)) {
  350. o.errCnt++
  351. } else {
  352. o.errCnt = 0
  353. }
  354. //先处理状态变化,再存入最新状态
  355. o.handleStateChange(t)
  356. cacheState(o.did, util.Unix2Time(i64), o.state) //更新dev_stat_XXX
  357. o.lastDataTime = t
  358. }
  359. func (o *HlZigbeeConcentrator) turnOnOffDimmerWholeProcess(m *mqtt.Message) {
  360. var obj protocol.HLWLZB_Switch_Whole_Ack
  361. var str string
  362. var state uint
  363. if err := obj.DeCode(m.PayloadString()); err != nil {
  364. return
  365. }
  366. ts, _ := strconv.ParseUint(obj.Ts, 10, 64)
  367. if obj.ErrorCode == 0 {
  368. str = "Success"
  369. state = 1
  370. } else {
  371. str = ""
  372. state = 0
  373. }
  374. oo := models.DeviceCmdRecord{ //前插后更新
  375. ID: ts,
  376. State: state,
  377. Resp: str,
  378. }
  379. if err := oo.Update(); err != nil {
  380. logrus.Errorf("收到网关[%s]的响应[seq:%s],主题:%s,但更新数据库失败[%s]",
  381. obj.MasterSn, obj.Ts, m.Topic(), err.Error())
  382. }
  383. }
  384. func (o *HlZigbeeConcentrator) turnOnOffDimmerMultiProcess(m *mqtt.Message) {
  385. var obj protocol.HLWLZB_Switch_Multi_Ack
  386. var str string
  387. var state uint
  388. if err := obj.DeCode(m.PayloadString()); err != nil {
  389. return
  390. }
  391. ts, _ := strconv.ParseUint(obj.Ts, 10, 64)
  392. if obj.ErrorCode == 0 {
  393. str = "Success"
  394. state = 1
  395. } else {
  396. str = ""
  397. state = 0
  398. }
  399. oo := models.DeviceCmdRecord{ //前插后更新
  400. ID: ts,
  401. State: state,
  402. Resp: str,
  403. }
  404. if err := oo.Update(); err != nil {
  405. logrus.Errorf("收到网关[%s]的响应[seq:%s],主题:%s,但更新数据库失败[%s]",
  406. obj.MasterSn, obj.Ts, m.Topic(), err.Error())
  407. }
  408. }
  409. func (o *HlZigbeeConcentrator) strategyProcess(m *mqtt.Message) {
  410. var obj protocol.Pack_HLSetOnOffTime_Ack
  411. var str string
  412. var state uint
  413. if err := obj.DeCode(m.PayloadString()); err != nil {
  414. return
  415. }
  416. ts, _ := strconv.ParseUint(obj.Ts, 10, 64)
  417. if obj.ErrorCode == 0 {
  418. str = "Success"
  419. state = 1
  420. } else {
  421. str = ""
  422. state = 0
  423. }
  424. oo := models.DeviceCmdRecord{ //前插后更新
  425. ID: ts,
  426. State: state,
  427. Resp: str,
  428. }
  429. if err := oo.Update(); err != nil {
  430. logrus.Errorf("收到网关[%s]的响应[seq:%s],主题:%s,但更新数据库失败[%s]",
  431. obj.MasterSn, obj.Ts, m.Topic(), err.Error())
  432. }
  433. }
  434. func (o *HlZigbeeConcentrator) clearStrategyProcess(m *mqtt.Message) {
  435. var obj protocol.Pack_HLClearStrategy_Ack
  436. var str string
  437. var state uint
  438. if err := obj.DeCode(m.PayloadString()); err != nil {
  439. return
  440. }
  441. ts, _ := strconv.ParseUint(obj.Ts, 10, 64)
  442. if obj.ErrorCode == 0 {
  443. str = "Success"
  444. state = 1
  445. } else {
  446. str = ""
  447. state = 0
  448. }
  449. oo := models.DeviceCmdRecord{ //前插后更新
  450. ID: ts,
  451. State: state,
  452. Resp: str,
  453. }
  454. if err := oo.Update(); err != nil {
  455. logrus.Errorf("收到网关[%s]的响应[seq:%s],主题:%s,但更新数据库失败[%s]",
  456. obj.MasterSn, obj.Ts, m.Topic(), err.Error())
  457. }
  458. }
  459. // fn topic处理入口
  460. func (o *HlZigbeeConcentrator) handleTopicUp(m *mqtt.Message) {
  461. var hp protocol.HLWLZB_Pack
  462. if err := hp.DeCode(m.PayloadString()); err != nil {
  463. logrus.Errorf("HlZigbeeConcentrator.handle_Topic_Up解析错误:%s,%s", m.PayloadString(), err.Error())
  464. }
  465. switch hp.MsgType {
  466. case protocol.MT_REPORT: //上报数据
  467. o.reportProcess(m.PayloadString())
  468. case protocol.MT_OFFLINE:
  469. fallthrough
  470. case protocol.MT_ONLINE: //上线,下线
  471. o.onofflineProcess(m.PayloadString())
  472. case protocol.MT_TURNON:
  473. fallthrough
  474. case protocol.MT_TURNOFF:
  475. fallthrough
  476. case protocol.MT_DIMMER: //开灯,关灯 dimmer ack
  477. if hp.ActionType == protocol.AT_WHOLE {
  478. o.turnOnOffDimmerWholeProcess(m)
  479. } else if hp.ActionType == protocol.AT_MULTI {
  480. o.turnOnOffDimmerMultiProcess(m)
  481. }
  482. case protocol.MT_STRATEGY: //策略 ack
  483. o.strategyProcess(m)
  484. case protocol.MT_CLEARSTRATEGY: //清除策略 ack
  485. o.clearStrategyProcess(m)
  486. case protocol.MT_ALARM: //告警
  487. //TODO
  488. default:
  489. }
  490. }
  491. func (o *HlZigbeeConcentrator) GetOnlineMsg() {
  492. //发布上线消息
  493. var obj protocol.Pack_IDObject
  494. str, err := obj.EnCode(o.gid, GetNextSeq(), 0)
  495. if err != nil {
  496. logrus.Errorf("HlZigbeeConcentrator.GetOnlineMsg:发布消息错误1:%s", err.Error())
  497. }
  498. topic := GetTopic(o.tenant, protocol.DT_GATEWAY, o.gid, protocol.TP_GW_ONLINE)
  499. if err := GetMQTTMgr().Publish(topic, str, mqtt.AtMostOnce); err != nil {
  500. logrus.Errorf("HlZigbeeConcentrator.GetOnlineMsg:发布消息错误:%s", err.Error())
  501. }
  502. }
  503. func (o *HlZigbeeConcentrator) GetWillMsg() {
  504. payload, _ := (&protocol.Pack_IDObject{}).EnCode(o.gid, GetNextSeq(), 0) //遗嘱消息
  505. topic := GetTopic(o.tenant, protocol.DT_GATEWAY, o.gid, protocol.TP_GW_WILL)
  506. if err := GetMQTTMgr().Publish(topic, payload, mqtt.AtMostOnce); err != nil {
  507. logrus.Errorf("HlZigbeeConcentrator.GetWillMsg:发布消息错误:%s", err.Error())
  508. }
  509. }