chzblampcontroller.go 7.9 KB

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