package main import ( "runtime/debug" "strconv" "strings" "sync" "time" "github.com/go-redis/redis/v7" "github.com/sirupsen/logrus" "lc/common/models" "lc/common/mqtt" "lc/common/protocol" "lc/common/util" ) var _LampStrategyMgrOnce sync.Once var _LampStrategyMgrSingle *LampStrategyMgr func GetLampStrategyMgr() *LampStrategyMgr { _LampStrategyMgrOnce.Do(func() { _LampStrategyMgrSingle = &LampStrategyMgr{ mapLampOOT: make(map[string][]protocol.CHZB_OnOffTime), mapLampHlOOT: make(map[string][]protocol.HLWLZB_OnOffTime), } }) return _LampStrategyMgrSingle } // LampStrategyQueue redis缓存 var LampStrategyQueue = "lamp_strategy_queue" //已修改的灯控编码列表,队列方式,先进先出 // LampStrategyMgr 灯控时间策略管理 type LampStrategyMgr struct { mapLampOOT map[string][]protocol.CHZB_OnOffTime //灯控策略时间,开灯时间段 mapLampHlOOT map[string][]protocol.HLWLZB_OnOffTime //灯控策略时间,开灯时间段 } func (o *LampStrategyMgr) Handler(args ...interface{}) interface{} { defer func() { if err := recover(); err != nil { gopool.Add(o.Handler, args) logrus.Errorf("LampStrategyMgr.Handler发生异常:%s", string(debug.Stack())) } }() IDList := make([]string, 0, 100) for { strID, err := redisCltRawData.RPop(LampStrategyQueue).Result() //灯控id if err != nil { if err != redis.Nil { //排除掉无数据的情况 logrus.Errorf("读取redis队列lamp_strategy_queue发生错误:%s", err.Error()) } } else { id := strings.Trim(strID, " ") if len(id) > 0 { IDList = append(IDList, id) } } //队列无数据并且IDList有数据,或IDList数据达到封顶值100,则进行处理 if (err == redis.Nil && len(IDList) > 0) || len(IDList) == 100 { idlist := RemoveReplicaString(IDList) o.HandleZigbeeLampStrategy(idlist) //处理zigbee灯控 o.HandleHLZigbeeLampStrategy(idlist) //处理海蓝zigbee灯控 o.HandleYm485LampStrategy(idlist) //处理485灯控 IDList = make([]string, 0, 100) } time.Sleep(1 * time.Second) } } type ConcentratorInfo struct { DID string Tenant string GID string LampIDs []uint32 } // HandleZigbeeLampStrategy 处理Zigbee灯控策略 func (o *LampStrategyMgr) HandleZigbeeLampStrategy(IDs []string) { zlsArr, err := models.GetZigbeeLampStrategyByIDs(IDs) if err != nil { oList := make([]interface{}, 0, len(IDs)) for _, v := range IDs { oList = append(oList, v) } redisCltRawData.LPush(LampStrategyQueue, oList...) } if len(zlsArr) == 0 { return } //处理照明策略时间 mapTime := make(map[string][]protocol.CHZB_OnOffTime) //策略编号->时间和亮度信息 for _, v := range zlsArr { if _, ok := mapTime[v.Strategy]; ok { o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) continue } var ls []models.LampStrategy //time_info字段 if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil { logrus.Errorf("HandleZigbeeLampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error()) continue } if len(ls) == 0 { continue } //日出日落 if v.Sunset == 1 { if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil { onOffTimes := make([]protocol.CHZB_OnOffTime, 1) onOffTimes[0].Brightness = uint8(ls[0].Brightness) 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]) onOffTimes[0].OnHour = uint8(onHour) onOffTimes[0].OnMinite = uint8(onMinute) onOffTimes[0].OffHour = uint8(offHour) onOffTimes[0].OffMinite = uint8(offMinute) mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...) o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) } } else { onOffTimes := make([]protocol.CHZB_OnOffTime, len(ls), len(ls)) for i, v := range ls { onOffTimes[i].Brightness = uint8(v.Brightness) onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0]) onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1]) offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0]) offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1]) onOffTimes[i].OnHour = uint8(onHour) onOffTimes[i].OnMinite = uint8(onMinute) onOffTimes[i].OffHour = uint8(offHour) onOffTimes[i].OffMinite = uint8(offMinute) } mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...) o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) } } //处理照明策略,集控器及末端编号 mapStrategyToMapConcentratorToNumbers := make(map[string]map[string]*ConcentratorInfo) for _, v := range zlsArr { if _, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy]; !ok { mapStrategyToMapConcentratorToNumbers[v.Strategy] = make(map[string]*ConcentratorInfo) } if pConcentratorInfo, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator]; !ok { c := ConcentratorInfo{DID: v.Concentrator, Tenant: v.Tenant, GID: v.GID} c.LampIDs = append(c.LampIDs, uint32(v.Number)) mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator] = &c } else { pConcentratorInfo.LampIDs = append(pConcentratorInfo.LampIDs, uint32(v.Number)) } } for k0, v0 := range mapStrategyToMapConcentratorToNumbers { oots, ok := mapTime[k0] if !ok { continue } for k1, v1 := range v0 { var obj protocol.Pack_SetOnOffTime seq := GetNextSeq() str, err := obj.EnCode(k1, v1.GID, seq, v1.LampIDs, oots) if err != nil { continue } topic := GetTopic(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME) var msg string if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil { msg = string(msg0) } else { msg = str } odb := models.DeviceCmdRecord{ ID: seq, GID: v1.GID, DID: k1, Topic: topic, Message: msg, State: 0, } if err := models.G_db.Create(&odb).Error; err != nil { logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error()) } else { logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库成功", k1, v1.LampIDs) } err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce) if err != nil { logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落消息错误:%s", k1, v1.LampIDs, err.Error()) } } } } // HandleHLZigbeeLampStrategy 处理海蓝Zigbee灯控策略 func (o *LampStrategyMgr) HandleHLZigbeeLampStrategy(IDs []string) { zlsArr, err := models.GetHLZigbeeLampStrategyByIDs(IDs) ////海蓝根据灯控编码列表,获取所有hlzigbee灯控 if err != nil { oList := make([]interface{}, 0, len(IDs)) for _, v := range IDs { oList = append(oList, v) } redisCltRawData.LPush(LampStrategyQueue, oList...) //灯控ID入队列中,待处理的ids } if len(zlsArr) == 0 { return } //处理照明策略时间-从存入到数据库中的策略处理 mapTime := make(map[string][]protocol.HLWLZB_OnOffTime) //策略编号->时间和亮度信息 for _, v := range zlsArr { if _, ok := mapTime[v.Strategy]; ok { o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...) continue } var ls []models.LampStrategy //time_info字段 if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil { logrus.Errorf("HandleZigbeeLampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error()) continue } if len(ls) == 0 { continue } //日出日落 if v.Sunset == 1 { if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil { oots := make([]protocol.HLWLZB_OnOffTime, 1) oots[0].Brightness = uint8(ls[0].Brightness) onhour, _ := strconv.Atoi(strings.Split(set, ":")[0]) onminite, _ := strconv.Atoi(strings.Split(set, ":")[1]) offhour, _ := strconv.Atoi(strings.Split(rise, ":")[0]) offminite, _ := strconv.Atoi(strings.Split(rise, ":")[1]) oots[0].OnHour = uint8(onhour) oots[0].OnMinite = uint8(onminite) oots[0].OffHour = uint8(offhour) oots[0].OffMinite = uint8(offminite) mapTime[v.Strategy] = append(mapTime[v.Strategy], oots...) o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...) } } else { onOffTimes := make([]protocol.HLWLZB_OnOffTime, len(ls), len(ls)) for i, v := range ls { onOffTimes[i].Brightness = uint8(v.Brightness) onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0]) onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1]) offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0]) offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1]) onOffTimes[i].OnHour = uint8(onHour) onOffTimes[i].OnMinite = uint8(onMinute) onOffTimes[i].OffHour = uint8(offHour) onOffTimes[i].OffMinite = uint8(offMinute) } mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...) o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...) } } //处理照明策略,集控器及末端编号 mapStrategyToMapConcentratorToNumbers := make(map[string]map[string]*ConcentratorInfo) for _, v := range zlsArr { if _, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy]; !ok { mapStrategyToMapConcentratorToNumbers[v.Strategy] = make(map[string]*ConcentratorInfo) //key为Strategy 没有就创建 } if pConcentratorInfo, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator]; !ok { //没有就创建 c := ConcentratorInfo{DID: v.Concentrator, Tenant: v.Tenant, GID: v.GID} c.LampIDs = append(c.LampIDs, uint32(v.Number)) //灯加入进去 mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator] = &c } else { pConcentratorInfo.LampIDs = append(pConcentratorInfo.LampIDs, uint32(v.Number)) //加灯 } } for k0, v0 := range mapStrategyToMapConcentratorToNumbers { oots, ok := mapTime[k0] if !ok { continue } for k1, v1 := range v0 { if len(oots) == 1 && (oots[0].OffHour == 0 && oots[0].OffMinite == 0 && oots[0].OnHour == 0 && oots[0].OnMinite == 0) { //清除策略 var obj protocol.Pack_HLClearStrategy seq := GetNextSeq() str, err := obj.EnCode(k1) logrus.Debugf("%s", str) s, err := json.MarshalToString(oots) logrus.Debugf("%s", s) if err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器---[%s]对灯控[%v]发布清除策略指令入库错误:%s", k1, v1.LampIDs, err.Error()) continue } topic := GetHLTopicDown(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME) var msg string if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil { msg = string(msg0) } else { msg = str } odb := models.DeviceCmdRecord{ //记录入库 ID: seq, GID: v1.GID, DID: k1, Topic: topic, Message: msg, State: 0, } if err := models.G_db.Create(&odb).Error; err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略时指令入库错误:%s", k1, v1.LampIDs, err.Error()) } else { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略时指令入库成功", k1, v1.LampIDs) } err = GetHlMqttMgr().Publish(topic, str, mqtt.AtLeastOnce) if err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略消息错误:%s", k1, v1.LampIDs, err.Error()) } return } var obj protocol.Pack_HLSetOnOffTime seq := GetNextSeq() str, err := obj.EnCode(k1, v1.GID, seq, v1.LampIDs, oots) logrus.Debugf("%s", str) s, err := json.MarshalToString(oots) logrus.Debugf("%s", s) if err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器---[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error()) continue } topic := GetHLTopicDown(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME) var msg string if msg0, errMsg := json.MarshalIndent(obj, "", " "); errMsg == nil { msg = string(msg0) } else { msg = str } odb := models.DeviceCmdRecord{ //记录入库 ID: seq, GID: v1.GID, DID: k1, Topic: topic, Message: msg, State: 0, } if err := models.G_db.Create(&odb).Error; err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error()) } else { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库成功", k1, v1.LampIDs) } err = GetHlMqttMgr().Publish(topic, str, mqtt.AtLeastOnce) if err != nil { logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落消息错误:%s", k1, v1.LampIDs, err.Error()) } } } } // HandleYm485LampStrategy 处理裕明485灯控 func (o *LampStrategyMgr) HandleYm485LampStrategy(IDs []string) { lsArr, err := models.GetYm485Lampstrategy(IDs) if err != nil { oList := make([]interface{}, 0, len(IDs)) for _, v := range IDs { oList = append(oList, v) } redisCltRawData.LPush(LampStrategyQueue, oList...) } if len(lsArr) == 0 { return } //处理照明策略时间 mapTime := make(map[string][]protocol.CHZB_OnOffTime) //策略编号->时间和亮度信息 for _, v := range lsArr { if _, ok := mapTime[v.Strategy]; ok { o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) continue } var ls []models.LampStrategy //time_info字段 if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil { logrus.Errorf("HandleYm485LampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error()) continue } if len(ls) == 0 { continue } //日出日落 if v.Sunset == 1 { if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil { onOffTimes := make([]protocol.CHZB_OnOffTime, 1) onOffTimes[0].Brightness = uint8(ls[0].Brightness) 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]) onOffTimes[0].OnHour = uint8(onHour) onOffTimes[0].OnMinite = uint8(onMinute) onOffTimes[0].OffHour = uint8(offHour) onOffTimes[0].OffMinite = uint8(offMinute) mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...) o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) } } else { onOffTimes := make([]protocol.CHZB_OnOffTime, len(ls), len(ls)) for i, v := range ls { onOffTimes[i].Brightness = uint8(v.Brightness) onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0]) onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1]) offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0]) offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1]) onOffTimes[i].OnHour = uint8(onHour) onOffTimes[i].OnMinite = uint8(onMinute) onOffTimes[i].OffHour = uint8(offHour) onOffTimes[i].OffMinite = uint8(offMinute) } mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...) o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...) } } //处理照明策略,集控器及末端编号 for _, v := range lsArr { oots, ok := mapTime[v.Strategy] if !ok { continue } var obj protocol.Pack_SetOnOffTime seq := GetNextSeq() str, err := obj.EnCode(v.ID, v.GID, seq, nil, oots) if err != nil { continue } topic := GetTopic(v.Tenant, protocol.DT_LAMPCONTROLLER, v.ID, protocol.TP_YM_SET_ONOFFTIME) 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("HandleYm485LampStrategy:对灯控[%s]发布日出日落时间时指令入库错误:%s", v.ID, err.Error()) } else { logrus.Errorf("HandleYm485LampStrategy:对灯控[%s]发布日出日落时间时指令入库成功", v.ID) } err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce) if err != nil { logrus.Errorf("HandleYm485LampStrategy:对灯控[%s]发布日出日落消息错误:%s", v.ID, err.Error()) } } } // RemoveReplicaString 字符串去重 func RemoveReplicaString(slc []string) []string { result := make([]string, 0) tempMap := make(map[string]bool, len(slc)) for _, e := range slc { if _, ok := tempMap[e]; !ok { tempMap[e] = true result = append(result, e) } } return result }