ymlampcontroller.go 6.7 KB

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