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) } }