| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- package main
- import (
- "context"
- "runtime"
- "runtime/debug"
- "strconv"
- "strings"
- "sync"
- "time"
- "github.com/sirupsen/logrus"
- "lc/common/models"
- "lc/common/mqtt"
- "lc/common/protocol"
- "lc/common/util"
- )
- var _YMLampControllerMgrOnce sync.Once
- var _YMLampControllerMgrSingle *YMLampControllerMgr
- func GetYMLampControllerMgr() *YMLampControllerMgr {
- _YMLampControllerMgrOnce.Do(func() {
- ctx, cancel := context.WithCancel(context.Background())
- _YMLampControllerMgrSingle = &YMLampControllerMgr{
- queue: util.NewQueue(100),
- mapYMLampController: make(map[string]*YMLampController),
- ctx: ctx,
- cancel: cancel,
- }
- })
- return _YMLampControllerMgrSingle
- }
- type YMLampControllerMgr struct {
- queue *util.MlQueue
- mapYMLampController map[string]*YMLampController
- ctx context.Context
- cancel context.CancelFunc
- }
- func (o *YMLampControllerMgr) SubscribeTopics() {
- GetMQTTMgr().Subscribe(GetVagueTopic(protocol.DT_LAMPCONTROLLER, protocol.TP_YM_DATA), mqtt.AtMostOnce, o.HandlerData)
- GetMQTTMgr().Subscribe(GetVagueTopic(protocol.DT_LAMPCONTROLLER, protocol.TP_YM_ALARM), mqtt.AtMostOnce, o.HandlerData)
- GetMQTTMgr().Subscribe(GetVagueTopic(protocol.DT_LAMPCONTROLLER, protocol.TP_YM_SET_SWITCH_ACK), mqtt.AtMostOnce, o.HandlerData)
- GetMQTTMgr().Subscribe(GetVagueTopic(protocol.DT_LAMPCONTROLLER, protocol.TP_YM_SET_ONOFFTIME_ACK), mqtt.AtMostOnce, o.HandlerData)
- }
- func (o *YMLampControllerMgr) HandlerData(m mqtt.Message) {
- for {
- ok, cnt := o.queue.Put(&m)
- if ok {
- break
- } else {
- logrus.Errorf("YMLampControllerMgr.HandlerData:查询队列失败,队列消息数量:%d", cnt)
- runtime.Gosched()
- }
- }
- }
- func (o *YMLampControllerMgr) Stop() {
- o.cancel()
- }
- func (o *YMLampControllerMgr) Handler(args ...interface{}) interface{} {
- defer func() {
- if err := recover(); err != nil {
- gopool.Add(o.Handler, args)
- logrus.Errorf("YMLampControllerMgr.Handler发生异常:%v", err)
- logrus.Errorf("YMLampControllerMgr.Handler发生异常,堆栈信息:%s", string(debug.Stack()))
- }
- }()
- exit := false
- timer := time.NewTicker(1 * time.Minute)
- //每天15点半同步日出日落时间
- var SyncSunset = util.New(util.MlNow()).BeginningOfDay().Add(10*time.Hour + 30*time.Minute)
- for {
- select {
- case <-o.ctx.Done():
- logrus.Error("YMLampControllerMgr.HandleQueue即将退出,原因:", o.ctx.Err())
- exit = true
- case <-timer.C: //每隔1分钟执行一次
- //更新灯控状态,防止无数据状态不更新
- o.UpdateLampControllerState()
- //同步日出日落时间
- if util.MlNow().After(SyncSunset) {
- if err := o.SyncSunset(); err == nil {
- SyncSunset = SyncSunset.AddDate(0, 0, 1)
- }
- }
- default:
- if o.handleQueue() == 0 {
- if exit {
- return 0
- }
- time.Sleep(100 * time.Millisecond)
- }
- }
- }
- }
- func (o *YMLampControllerMgr) handleQueue() uint32 {
- msg, ok, quantity := o.queue.Get()
- if !ok {
- return quantity
- } else if quantity > 1000 {
- logrus.Warnf("YMLampControllerMgr.Handler:数据队列累积过多,请注意优化,当前队列条数:%d", quantity)
- }
- m, ok := msg.(*mqtt.Message)
- if !ok {
- return quantity
- }
- Tenant, _, DID, topic, err := ParseTopic(m.Topic())
- if err != nil {
- return quantity
- }
- pymlc, ok := o.mapYMLampController[DID]
- if !ok {
- pymlc = &YMLampController{}
- pymlc.Set(Tenant, DID)
- o.mapYMLampController[DID] = pymlc
- }
- switch topic {
- case protocol.TP_YM_DATA:
- o.handleDATA(pymlc, m)
- case protocol.TP_YM_ALARM:
- o.handleALARM(pymlc, m)
- case protocol.TP_YM_SET_SWITCH_ACK, protocol.TP_YM_SET_ONOFFTIME_ACK:
- o.handleACK(m)
- }
- return quantity
- }
- func (o *YMLampControllerMgr) handleDATA(lp *YMLampController, m *mqtt.Message) {
- var obj protocol.Pack_CHZB_UploadData
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- t, err := util.MlParseTime(obj.Time)
- if err != nil {
- logrus.Errorf("时间[%s]解析错误:%s", obj.Time, err.Error())
- return
- }
- for _, v := range obj.Data.Data {
- lp.HandleData(obj.Gid, obj.Data.TID, t, v)
- }
- }
- func (o *YMLampControllerMgr) handleALARM(lp *YMLampController, m *mqtt.Message) {
- var obj protocol.Pack_CHZB_LampAlarm
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- lp.HandleAlarm(obj.Data)
- }
- func (o *YMLampControllerMgr) handleACK(m *mqtt.Message) {
- var obj protocol.Pack_Ack
- if err := obj.DeCode(m.PayloadString()); err != nil {
- return
- }
- oo := models.DeviceCmdRecord{ID: obj.Seq, State: 1, Resp: obj.Data.Error}
- if err := oo.Update(); err != nil {
- logrus.Errorf("收到设备[%s]的响应[seq:%d],主题:%s,但更新数据库失败[%s]", obj.Id, obj.Seq, m.Topic(), err.Error())
- }
- }
- // SyncSunset 统一更新裕明485灯控的日出日落时间
- func (o *YMLampControllerMgr) SyncSunset() error {
- arr, err := models.GetYm485Lampstrategy(nil)
- if err != nil {
- logrus.Errorf("从数据库读取设置为日出日落时间的485灯控发生错误:%s", err.Error())
- return err
- }
- if len(arr) == 0 {
- return nil
- }
- //分别计算日出日落
- mapTime := make(map[string]*protocol.CHZB_OnOffTime) //策略时间
- for _, v := range arr {
- if _, ok := mapTime[v.Strategy]; ok {
- //已计算日出日落时间的,不再重复计算
- continue
- }
- var oot protocol.CHZB_OnOffTime
- var ls []models.LampStrategy
- if err := json.UnmarshalFromString(v.TimeInfo, &ls); err == nil && len(ls) > 0 {
- oot.Brightness = uint8(ls[0].Brightness)
- }
- //计算时间
- if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil {
- onHour, _ := strconv.Atoi(strings.Split(set, ":")[0])
- onMinute, _ := strconv.Atoi(strings.Split(set, ":")[1])
- offHour, _ := strconv.Atoi(strings.Split(rise, ":")[0])
- offMinute, _ := strconv.Atoi(strings.Split(rise, ":")[1])
- oot.OnHour = uint8(onHour)
- oot.OnMinite = uint8(onMinute)
- oot.OffHour = uint8(offHour)
- oot.OffMinite = uint8(offMinute)
- }
- mapTime[v.Strategy] = &oot
- }
- //发布mqtt消息
- for _, v := range arr {
- if oot, ok := mapTime[v.Strategy]; ok {
- var obj protocol.Pack_SetOnOffTime
- seq := GetNextSeq()
- if str, err := obj.EnCode(v.ID, v.GID, seq, nil, []protocol.CHZB_OnOffTime{*oot}); err == nil {
- topic := GetTopic(v.Tenant, protocol.DT_LAMPCONTROLLER, v.ID, protocol.TP_YM_SET_ONOFFTIME)
- err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce)
- if err != nil {
- logrus.Errorf("SyncSunset:对灯控[%s]发布日出日落消息错误:%s", v.ID, err.Error())
- }
- var msg string
- if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil {
- msg = string(msg0)
- } else {
- msg = str
- }
- odb := models.DeviceCmdRecord{
- ID: seq,
- GID: v.GID,
- DID: v.ID,
- Topic: topic,
- Message: msg,
- State: 0,
- }
- if err := models.G_db.Create(&odb).Error; err != nil {
- logrus.Errorf("对灯控[%s]发布日出日落时间时指令入库错误:%s", v.ID, err.Error())
- } else {
- logrus.Errorf("对灯控[%s]发布日出日落时间时指令入库成功", v.ID)
- }
- }
- }
- }
- return nil
- }
- func (o *YMLampControllerMgr) UpdateLampControllerState() {
- for _, v := range o.mapYMLampController {
- v.UpdateState()
- }
- }
|