hlzblampcontroller.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. package main
  2. import (
  3. "strconv"
  4. "time"
  5. "github.com/go-redis/redis/v7"
  6. "github.com/sirupsen/logrus"
  7. "lc/common/models"
  8. "lc/common/protocol"
  9. "lc/common/util"
  10. )
  11. // var LAMP_EVENT_ID string = "lamp_event_id"
  12. // var LAMP_ALARM_ID string = "lamp_alarm_id"
  13. type HlZigbeeLampController struct {
  14. tenant string //租户
  15. gid string //基础数据,网关ID
  16. did string //基础数据,集控器设备编码
  17. tid uint16 //基础数据,集控器物模型ID
  18. lampDid string //灯编码
  19. errCnt uint //连续错误计数
  20. lampOn uint8 //开灯状态,1开灯,0关灯,0xFF未知
  21. state uint8 //实时数据,0在线,1离线
  22. lastStateTime time.Time //实时数据,最新状态时间
  23. mapData map[uint16]float64 //实时数据,最新数据
  24. lastDataTime time.Time //实时数据,最新数据时间
  25. nextHourTime time.Time //实时数据
  26. mapAlarm map[string]int64 //告警开始时间->数据库记录ID
  27. // lampNumber uint32 //灯编号,长和单灯末端编号,海蓝通信编码
  28. }
  29. func (o *HlZigbeeLampController) SetDID(did string, pdid string) {
  30. if o.lampDid == "" { //0xFF未知
  31. o.state = 0xFF
  32. o.lampOn = 0xFF
  33. }
  34. if o.mapAlarm == nil {
  35. o.mapAlarm = make(map[string]int64)
  36. }
  37. o.did = did //集id
  38. o.lampDid = pdid //灯id
  39. }
  40. // HandleData 更新灯控数据
  41. func (o *HlZigbeeLampController) HandleData(tenant, gid, pdid string, tid uint16, t time.Time, mapData *protocol.CHZB_LampData) {
  42. if o.lampDid == "" {
  43. logrus.Debugf("HlZigbeeLampController.HandleData:设备编码为空:lampDid=%s", pdid)
  44. return
  45. }
  46. //更新基础数据
  47. o.tenant = tenant
  48. o.gid = gid
  49. o.lampDid = pdid //灯
  50. o.tid = tid
  51. if o.nextHourTime.IsZero() { //下一个小时
  52. o.nextHourTime = protocol.ToBJTime(util.BeginningOfHour().Add(1 * time.Hour))
  53. }
  54. if o.mapData == nil {
  55. o.mapData = make(map[uint16]float64)
  56. }
  57. if len(mapData.Data) > 0 { //按什么顺序?
  58. if v, ok := mapData.Data[1]; ok {
  59. o.handleLampEvent(t, v)
  60. }
  61. for k, v := range mapData.Data {
  62. o.mapData[k] = v
  63. }
  64. //需要告警,则推入告警管理器
  65. //TODO:
  66. bv := BizValue{ID: pdid, Time: t, Tid: tid, Data: o.mapData} //sid,值
  67. GetBizAlarmMgr().PushData(&bv)
  68. o.lastDataTime = t
  69. cacheData(o.lampDid, t, mapData.Data)
  70. o.errCnt = 0
  71. } else {
  72. o.errCnt++
  73. }
  74. o.handleStateChange(t) //灯控在离线事件
  75. cacheState(o.lampDid, protocol.ToBJTime(t).Format("2006-01-02 15:04:05"), o.state)
  76. o.checkSaveData() //1 hour
  77. }
  78. // UpdateState 上层每隔一分钟调用,置状态为离线
  79. func (o *HlZigbeeLampController) UpdateState() {
  80. if o.lampDid == "" {
  81. logrus.Debug("HlZigbeeLampController.handleStateChange:设备编码为空")
  82. return
  83. }
  84. if o.lastStateTime.IsZero() { //时间为空
  85. t0, s0, err := getState(o.lampDid)
  86. if err == nil {
  87. o.state = s0
  88. o.lastStateTime = t0
  89. } else {
  90. err := redisCltRawData.HGet(DeviceAlarmId, o.lampDid).Err()
  91. if err == redis.Nil {
  92. o.state = protocol.FAILED
  93. o.lastStateTime = util.MlNow()
  94. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
  95. }
  96. }
  97. }
  98. if o.state == protocol.FAILED || //若之前是离线状态的,则不修改状态
  99. (!o.lastDataTime.IsZero() && util.MlNow().Sub(o.lastDataTime).Minutes() < OfflineInterval) {
  100. return
  101. }
  102. //如果之前一直是在线状态的,则置为离线;若之前是离线状态的,则不修改状态
  103. if o.state == protocol.SUCCESS {
  104. o.state = protocol.FAILED
  105. o.lastStateTime = util.MlNow()
  106. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
  107. cacheState(o.lampDid, o.lastStateTime.Format("2006-01-02 15:04:05"), o.state)
  108. //检查是否要缓存数据
  109. o.checkSaveData() //1hour
  110. }
  111. }
  112. func (o *HlZigbeeLampController) SetOnOffLine(onoffline uint8) {
  113. if onoffline == protocol.FAILED { //使离线
  114. if o.state == protocol.FAILED || //若之前是离线状态的,则不修改状态
  115. (!o.lastDataTime.IsZero() && util.MlNow().Sub(o.lastDataTime).Minutes() < OfflineInterval) {
  116. return
  117. }
  118. //如果之前一直是在线状态的,则置为离线;若之前是离线状态的,则不修改状态
  119. if o.state == protocol.SUCCESS {
  120. o.state = protocol.FAILED
  121. o.lastStateTime = util.MlNow()
  122. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: o.lastStateTime})
  123. cacheState(o.lampDid, o.lastStateTime.Format("2006-01-02 15:04:05"), o.state)
  124. //检查是否要缓存数据
  125. o.checkSaveData() //1hour
  126. }
  127. } else if onoffline == protocol.SUCCESS { //使在线
  128. //单灯收到不online
  129. }
  130. }
  131. func (o *HlZigbeeLampController) handleLampEvent(t time.Time, lampOn float64) {
  132. //开关灯状态
  133. if o.lampOn == 0xFF {
  134. if lampOn > 0 {
  135. o.lampOn = 1
  136. } else {
  137. o.lampOn = 0
  138. }
  139. return
  140. }
  141. if lampOn > 0 && o.lampOn == 0 { //熄灯状态到开灯状态
  142. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_ONLAMP, Time: t, Value: lampOn})
  143. } else if lampOn == 0 && o.lampOn == 1 { //开灯状态到熄灯状态
  144. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLAMP, Time: t, Value: lampOn})
  145. }
  146. //更新状态
  147. if lampOn > 0 {
  148. o.lampOn = 1
  149. } else {
  150. o.lampOn = 0
  151. }
  152. }
  153. //灯控状态改变事件
  154. func (o *HlZigbeeLampController) handleStateChange(t time.Time) {
  155. if o.lampDid == "" {
  156. logrus.Debug("HlZigbeeLampController.handleStateChange:设备编码为空")
  157. return
  158. }
  159. //最新状态
  160. state := uint8(0)
  161. if o.errCnt >= 10 {
  162. state = 1
  163. } else if o.errCnt == 0 {
  164. state = 0
  165. } else {
  166. return
  167. }
  168. //状态处理
  169. if o.lastStateTime.IsZero() {
  170. t0, s0, err := getState(o.lampDid)
  171. if err != nil {
  172. o.state = state
  173. o.lastStateTime = t
  174. return
  175. }
  176. o.state = s0
  177. o.lastStateTime = t0
  178. }
  179. if o.state == 0 && state == 1 { //在线->离线
  180. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_OFFLINE, Time: t})
  181. } else if o.state == 1 && state == 0 { //离线->在线
  182. GetEventMgr().PushEvent(&EventObject{ID: o.lampDid, EventType: models.ET_ONLINE, Time: t})
  183. }
  184. o.state = state
  185. o.lastStateTime = t
  186. }
  187. func HLConvertExcept(except uint16) string {
  188. //-1初始值 0正常,1异常亮灯,2异常熄灯,3亮度异常
  189. switch except {
  190. case protocol.LE_OK: //正常(正常开灯或熄灯状态)
  191. return "正常"
  192. case protocol.LE_ON: //亮灯异常(本该关灯状态)
  193. return "熄灯时段亮灯"
  194. case protocol.LE_OFF: //亮灯,但亮度异常(本该开灯状态,但开灯亮度不对)
  195. return "亮灯时段熄灯"
  196. case protocol.LE_ON_BRIGHTNESS:
  197. return "亮灯但亮度异常"
  198. default:
  199. return "状态未知"
  200. }
  201. }
  202. // HandleAlarm 告警开始,结束 终端->平台的报警
  203. func (o *HlZigbeeLampController) HandleAlarm(alarm *protocol.LampAlarm) {
  204. if o.mapAlarm == nil {
  205. o.mapAlarm = make(map[string]int64)
  206. }
  207. key := o.lampDid + "@" + alarm.StartTime
  208. if alarm.EndTime != "" { //告警结束
  209. if t, err := util.MlParseTime(alarm.EndTime); err == nil {
  210. aid, ok := o.mapAlarm[key]
  211. if !ok {
  212. if str, err := redisCltRawData.HGet(DeviceAlarmId, key).Result(); err == nil {
  213. if id, err := strconv.Atoi(str); err == nil {
  214. aid = int64(id)
  215. }
  216. }
  217. }
  218. if aid > 0 {
  219. oo := models.DeviceAlarm{ID: aid, TEnd: t, EValue: float32(alarm.Brightness)}
  220. if err := oo.Update(); err != nil {
  221. logrus.Errorf("更新告警[%d]信息失败:%s", aid, err.Error())
  222. }
  223. delete(o.mapAlarm, key)
  224. if err := redisCltRawData.HDel(DeviceAlarmId, key).Err(); err != nil {
  225. logrus.Errorf("更新告警[%d]信息失败:%s", aid, err.Error())
  226. }
  227. }
  228. }
  229. } else { //告警开始
  230. t, err := util.MlParseTime(alarm.StartTime)
  231. if err != nil {
  232. return
  233. }
  234. oo := models.DeviceAlarm{
  235. DID: alarm.DID,
  236. TStart: t,
  237. Threshold: 0,
  238. SValue: float32(alarm.AlarmBrightness),
  239. Content: ConvertExcept(alarm.AlarmType),
  240. AlarmType: alarm.AlarmType,
  241. Level: 1,
  242. }
  243. if err := models.G_db.Create(&oo).Error; err != nil {
  244. logrus.Errorf("告警信息[%v]入库失败:%s", alarm, err.Error())
  245. } else {
  246. o.mapAlarm[key] = oo.ID
  247. if err := redisCltRawData.HSet(DeviceAlarmId, key, oo.ID).Err(); err != nil {
  248. logrus.Errorf("设备[%s]告警数据[%d]缓存失败:%s", o.lampDid, oo.ID, err.Error())
  249. }
  250. }
  251. }
  252. }
  253. //每小时入库一次设备数据,这里值不计顺序
  254. func (o *HlZigbeeLampController) checkSaveData() {
  255. if o.nextHourTime.Before(o.lastDataTime) {
  256. //判断小时是否一样,不一样则以数据时间为准
  257. if !util.New(o.nextHourTime).BeginningOfHour().Equal(util.New(o.lastDataTime).BeginningOfHour()) {
  258. o.nextHourTime = util.New(o.lastDataTime).BeginningOfHour()
  259. }
  260. if len(o.mapData) > 0 {
  261. var datas []models.DeviceHourData
  262. for k, v := range o.mapData {
  263. o := models.DeviceHourData{ID: o.lampDid, Sid: k, Val: float32(v), Time: o.nextHourTime, CreatedAt: time.Now()}
  264. datas = append(datas, o)
  265. }
  266. if err := models.MultiInsertDeviceHourData(datas); err != nil {
  267. //TODO 是否考虑备份数据
  268. logrus.Errorf("设备[%s]小时[%s]数据插入数据库失败:%s", o.lampDid, o.nextHourTime.Format("2006-01-02 15:04:05"), err.Error())
  269. }
  270. }
  271. o.nextHourTime = o.nextHourTime.Add(time.Hour)
  272. o.mapData = make(map[uint16]float64)
  273. }
  274. }