| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package main
- import (
- "strconv"
- "time"
- "github.com/go-redis/redis/v7"
- "github.com/sirupsen/logrus"
- "lc/common/models"
- "lc/common/protocol"
- "lc/common/util"
- )
- // var LAMP_EVENT_ID string = "lamp_event_id"
- // var LAMP_ALARM_ID string = "lamp_alarm_id"
- type HlZigbeeLampController struct {
- tenant string //租户
- gid string //基础数据,网关ID
- did string //基础数据,集控器设备编码
- tid uint16 //基础数据,集控器物模型ID
- lampDid string //灯编码
- errCnt uint //连续错误计数
- lampOn uint8 //开灯状态,1开灯,0关灯,0xFF未知
- state uint8 //实时数据,0在线,1离线
- lastStateTime time.Time //实时数据,最新状态时间
- mapData map[uint16]float64 //实时数据,最新数据
- lastDataTime time.Time //实时数据,最新数据时间
- nextHourTime time.Time //实时数据
- mapAlarm map[string]int64 //告警开始时间->数据库记录ID
- // lampNumber uint32 //灯编号,长和单灯末端编号,海蓝通信编码
- }
- func (o *HlZigbeeLampController) SetDID(did string, pdid string) {
- if o.lampDid == "" { //0xFF未知
- o.state = 0xFF
- o.lampOn = 0xFF
- }
- if o.mapAlarm == nil {
- o.mapAlarm = make(map[string]int64)
- }
- o.did = did //集id
- o.lampDid = pdid //灯id
- }
- // HandleData 更新灯控数据
- func (o *HlZigbeeLampController) HandleData(tenant, gid, pdid string, tid uint16, t time.Time, mapData *protocol.CHZB_LampData) {
- if o.lampDid == "" {
- logrus.Debugf("HlZigbeeLampController.HandleData:设备编码为空:lampDid=%s", pdid)
- return
- }
- //更新基础数据
- o.tenant = tenant
- o.gid = gid
- o.lampDid = pdid //灯
- o.tid = tid
- if o.nextHourTime.IsZero() { //下一个小时
- o.nextHourTime = protocol.ToBJTime(util.BeginningOfHour().Add(1 * time.Hour))
- }
- if o.mapData == nil {
- o.mapData = make(map[uint16]float64)
- }
- if len(mapData.Data) > 0 { //按什么顺序?
- if v, ok := mapData.Data[1]; ok {
- o.handleLampEvent(t, v)
- }
- for k, v := range mapData.Data {
- o.mapData[k] = v
- }
- //需要告警,则推入告警管理器
- //TODO:
- bv := BizValue{ID: pdid, Time: t, Tid: tid, Data: o.mapData} //sid,值
- GetBizAlarmMgr().PushData(&bv)
- o.lastDataTime = t
- cacheData(o.lampDid, t, mapData.Data)
- o.errCnt = 0
- } else {
- o.errCnt++
- }
- o.handleStateChange(t) //灯控在离线事件
- cacheState(o.lampDid, protocol.ToBJTime(t).Format("2006-01-02 15:04:05"), o.state)
- o.checkSaveData() //1 hour
- }
- // UpdateState 上层每隔一分钟调用,置状态为离线
- func (o *HlZigbeeLampController) UpdateState() {
- if o.lampDid == "" {
- logrus.Debug("HlZigbeeLampController.handleStateChange:设备编码为空")
- return
- }
- if o.lastStateTime.IsZero() { //时间为空
- t0, s0, err := getState(o.lampDid)
- if err == nil {
- o.state = s0
- o.lastStateTime = t0
- } else {
- err := redisCltRawData.HGet(DeviceAlarmId, o.lampDid).Err()
- if err == redis.Nil {
- o.state = protocol.FAILED
- o.lastStateTime = util.MlNow()
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
- }
- }
- }
- if o.state == protocol.FAILED || //若之前是离线状态的,则不修改状态
- (!o.lastDataTime.IsZero() && util.MlNow().Sub(o.lastDataTime).Minutes() < OfflineInterval) {
- return
- }
- //如果之前一直是在线状态的,则置为离线;若之前是离线状态的,则不修改状态
- if o.state == protocol.SUCCESS {
- o.state = protocol.FAILED
- o.lastStateTime = util.MlNow()
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
- cacheState(o.lampDid, o.lastStateTime.Format("2006-01-02 15:04:05"), o.state)
- //检查是否要缓存数据
- o.checkSaveData() //1hour
- }
- }
- func (o *HlZigbeeLampController) SetOnOffLine(onoffline uint8) {
- if onoffline == protocol.FAILED { //使离线
- if o.state == protocol.FAILED || //若之前是离线状态的,则不修改状态
- (!o.lastDataTime.IsZero() && util.MlNow().Sub(o.lastDataTime).Minutes() < OfflineInterval) {
- return
- }
- //如果之前一直是在线状态的,则置为离线;若之前是离线状态的,则不修改状态
- if o.state == protocol.SUCCESS {
- o.state = protocol.FAILED
- o.lastStateTime = util.MlNow()
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
- cacheState(o.lampDid, o.lastStateTime.Format("2006-01-02 15:04:05"), o.state)
- //检查是否要缓存数据
- o.checkSaveData() //1hour
- }
- } else if onoffline == protocol.SUCCESS { //使在线
- //单灯收到不online
- }
- }
- func (o *HlZigbeeLampController) handleLampEvent(t time.Time, lampOn float64) {
- //开关灯状态
- if o.lampOn == 0xFF {
- if lampOn > 0 {
- o.lampOn = 1
- } else {
- o.lampOn = 0
- }
- return
- }
- if lampOn > 0 && o.lampOn == 0 { //熄灯状态到开灯状态
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_ONLAMP, Time: t, Value: lampOn})
- } else if lampOn == 0 && o.lampOn == 1 { //开灯状态到熄灯状态
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLAMP, Time: t, Value: lampOn})
- }
- //更新状态
- if lampOn > 0 {
- o.lampOn = 1
- } else {
- o.lampOn = 0
- }
- }
- //灯控状态改变事件
- func (o *HlZigbeeLampController) handleStateChange(t time.Time) {
- if o.lampDid == "" {
- logrus.Debug("HlZigbeeLampController.handleStateChange:设备编码为空")
- return
- }
- //最新状态
- state := uint8(0)
- if o.errCnt >= 10 {
- state = 1
- } else if o.errCnt == 0 {
- state = 0
- } else {
- return
- }
- //状态处理
- if o.lastStateTime.IsZero() {
- t0, s0, err := getState(o.lampDid)
- if err != nil {
- o.state = state
- o.lastStateTime = t
- return
- }
- o.state = s0
- o.lastStateTime = t0
- }
- if o.state == 0 && state == 1 { //在线->离线
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: t})
- } else if o.state == 1 && state == 0 { //离线->在线
- GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_ONLINE, Time: t})
- }
- o.state = state
- o.lastStateTime = t
- }
- func HLConvertExcept(except uint16) string {
- //-1初始值 0正常,1异常亮灯,2异常熄灯,3亮度异常
- switch except {
- case protocol.LE_OK: //正常(正常开灯或熄灯状态)
- return "正常"
- case protocol.LE_ON: //亮灯异常(本该关灯状态)
- return "熄灯时段亮灯"
- case protocol.LE_OFF: //亮灯,但亮度异常(本该开灯状态,但开灯亮度不对)
- return "亮灯时段熄灯"
- case protocol.LE_ON_BRIGHTNESS:
- return "亮灯但亮度异常"
- default:
- return "状态未知"
- }
- }
- // HandleAlarm 告警开始,结束 终端->平台的报警
- func (o *HlZigbeeLampController) HandleAlarm(alarm *protocol.LampAlarm) {
- if o.mapAlarm == nil {
- o.mapAlarm = make(map[string]int64)
- }
- key := o.lampDid + "@" + alarm.StartTime
- if alarm.EndTime != "" { //告警结束
- if t, err := util.MlParseTime(alarm.EndTime); err == nil {
- aid, ok := o.mapAlarm[key]
- if !ok {
- if str, err := redisCltRawData.HGet(DeviceAlarmId, key).Result(); err == nil {
- if id, err := strconv.Atoi(str); err == nil {
- aid = int64(id)
- }
- }
- }
- if aid > 0 {
- oo := models.DeviceAlarm{ID: aid, TEnd: t, EValue: float32(alarm.Brightness)}
- if err := oo.Update(); err != nil {
- logrus.Errorf("更新告警[%d]信息失败:%s", aid, err.Error())
- }
- delete(o.mapAlarm, key)
- if err := redisCltRawData.HDel(DeviceAlarmId, key).Err(); err != nil {
- logrus.Errorf("更新告警[%d]信息失败:%s", aid, err.Error())
- }
- }
- }
- } else { //告警开始
- t, err := util.MlParseTime(alarm.StartTime)
- if err != nil {
- return
- }
- oo := models.DeviceAlarm{
- DID: alarm.DID,
- TStart: t,
- Threshold: 0,
- SValue: float32(alarm.AlarmBrightness),
- Content: ConvertExcept(alarm.AlarmType),
- AlarmType: alarm.AlarmType,
- Level: 1,
- }
- if err := models.G_db.Create(&oo).Error; err != nil {
- logrus.Errorf("告警信息[%v]入库失败:%s", alarm, err.Error())
- } else {
- o.mapAlarm[key] = oo.ID
- if err := redisCltRawData.HSet(DeviceAlarmId, key, oo.ID).Err(); err != nil {
- logrus.Errorf("设备[%s]告警数据[%d]缓存失败:%s", o.lampDid, oo.ID, err.Error())
- }
- }
- }
- }
- //每小时入库一次设备数据,这里值不计顺序
- func (o *HlZigbeeLampController) checkSaveData() {
- if o.nextHourTime.Before(o.lastDataTime) {
- //判断小时是否一样,不一样则以数据时间为准
- if !util.New(o.nextHourTime).BeginningOfHour().Equal(util.New(o.lastDataTime).BeginningOfHour()) {
- o.nextHourTime = util.New(o.lastDataTime).BeginningOfHour()
- }
- if len(o.mapData) > 0 {
- var datas []models.DeviceHourData
- for k, v := range o.mapData {
- o := models.DeviceHourData{ID: o.lampDid, Sid: k, Val: float32(v), Time: o.nextHourTime, CreatedAt: time.Now()}
- datas = append(datas, o)
- }
- if err := models.MultiInsertDeviceHourData(datas); err != nil {
- //TODO 是否考虑备份数据
- logrus.Errorf("设备[%s]小时[%s]数据插入数据库失败:%s", o.lampDid, o.nextHourTime.Format("2006-01-02 15:04:05"), err.Error())
- }
- }
- o.nextHourTime = o.nextHourTime.Add(time.Hour)
- o.mapData = make(map[uint16]float64)
- }
- }
|