alarm_plan.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. from typing import List, Tuple, Optional
  2. from datetime import datetime, time, date
  3. from threading import Thread, Lock
  4. from enum import Enum
  5. import uuid
  6. import json
  7. import traceback
  8. from datetime import datetime, timezone, timedelta
  9. from common.sys_comm import (
  10. LOGDBG, LOGINFO, LOGWARN, LOGERR,
  11. get_utc_time_ms, get_utc_time_s, get_bj_time_ms, get_bj_time_s,
  12. utc_to_bj_ms, bj_to_utc_ms, utc_to_bj_s, bj_to_utc_s
  13. )
  14. from core.time_plan import TimePlan
  15. from core.event_type import EventType, event_desc_map
  16. import core.g_LAS as g_las
  17. import core.alarm_plan_helper as helper
  18. from db.db_process import (
  19. db_req_que, DBRequest
  20. )
  21. import db.db_sqls as sqls
  22. import mqtt.mqtt_send as mqtt_send
  23. import device.dev_mng as g_Dev
  24. from device.dev_mng import (
  25. Device, g_dev_mgr
  26. )
  27. class EventAttr_Base:
  28. def __init__(self):
  29. return
  30. # 事件属性 事件事件
  31. class EventAttr_StayDetection(EventAttr_Base):
  32. def __init__(self, event_type):
  33. self.event_type_ = event_type
  34. self.enter_ts_: int = -1 # 进入时间(s)
  35. self.leave_ts_: int = -1 # 离开时间(s)
  36. self.stay_time_: int = -1 # 停留时长(s)
  37. return
  38. def reset(self):
  39. self.enter_ts_ = -1
  40. self.leave_ts_ = -1
  41. self.stay_time_ = -1
  42. # 事件属性 滞留事件
  43. class EventAttr_RetentionDetection(EventAttr_Base):
  44. def __init__(self, event_type):
  45. self.event_type_ = event_type
  46. self.enter_ts_: int = -1 # 进入时间(s)
  47. self.leave_ts_: int = -1 # 离开时间(s)
  48. self.stay_time_: int = -1 # 停留时长(s)
  49. return
  50. def reset(self):
  51. self.enter_ts_ = -1
  52. self.leave_ts_ = -1
  53. self.stay_time_ = -1
  54. # 事件属性 如厕事件
  55. class EventAttr_ToiletingDetection(EventAttr_Base):
  56. def __init__(self, event_type):
  57. self.event_type_ = event_type
  58. self.enter_ts_: int = -1 # 进入时间(ms)
  59. self.leave_ts_: int = -1 # 离开时间(ms)
  60. self.stay_time_: int = -1 # 停留时长(ms)
  61. return
  62. def reset(self):
  63. self.enter_ts_ = -1
  64. self.leave_ts_ = -1
  65. self.stay_time_ = -1
  66. # 事件属性 如厕频次统计
  67. class EventAttr_ToiletingFrequency(EventAttr_Base):
  68. def __init__(self, event_type):
  69. self.event_type_ = event_type
  70. self.count_: int = 0 # 统计次数
  71. self.event_list: list = []
  72. return
  73. def reset(self):
  74. self.count_ = 0
  75. self.event_list = []
  76. # 事件属性 夜间如厕频次统计
  77. class EventAttr_NightToiletingFrequency(EventAttr_Base):
  78. def __init__(self, event_type):
  79. self.event_type_ = event_type
  80. self.count_: int = 0 # 统计次数
  81. self.event_list: list = []
  82. return
  83. def reset(self):
  84. self.count_ = 0
  85. self.event_list = []
  86. # 事件属性 如厕频次异常
  87. class EventAttr_ToiletingFrequencyAbnormal(EventAttr_Base):
  88. def __init__(self, event_type):
  89. self.event_type_ = event_type
  90. self.count_: int = 0 # 统计次数
  91. self.threshold_count_: int = 0 # 异常阈值
  92. self.event_list: list = []
  93. return
  94. def reset(self):
  95. self.count_ = 0
  96. self.event_list = []
  97. # 事件属性 起夜异常
  98. class EventAttr_NightToiletingFrequencyAbnormal(EventAttr_Base):
  99. def __init__(self, event_type):
  100. self.event_type_ = event_type
  101. self.count_: int = 0 # 统计次数
  102. self.threshold_count_: int = 0 # 异常阈值
  103. self.event_list: list = []
  104. return
  105. def reset(self):
  106. self.count_ = 0
  107. self.event_list = []
  108. # 事件属性 卫生间频次统计
  109. class EventAttr_BathroomStayFrequency(EventAttr_Base):
  110. def __init__(self, event_type):
  111. self.event_type_ = event_type
  112. self.count_: int = 0 # 统计次数
  113. self.event_list: list = []
  114. return
  115. def reset(self):
  116. self.count_ = 0
  117. self.event_list = []
  118. # 事件属性 异常消失
  119. class EventAttr_TargetAbsence(EventAttr_Base):
  120. def __init__(self, event_type):
  121. self.event_type_ = event_type
  122. self.leave_ts_: int = -1 # 离开时间(ms)
  123. self.enter_ts_: int = -1 # 进入时间(ms)
  124. self.absence_time_: int = -1 # 消失时长(ms)
  125. self.time_threshold_: int = 300 # 触发消失时间阈值(ms)
  126. return
  127. def reset(self):
  128. self.leave_ts_ = -1
  129. self.enter_ts_ = -1
  130. self.absence_time_ = -1
  131. # 事件属性表
  132. event_attr_map = {
  133. EventType.STAY_DETECTION.value : EventAttr_StayDetection,
  134. EventType.RETENTION_DETECTION.value : EventAttr_RetentionDetection,
  135. EventType.TOILETING_DETECTION.value : EventAttr_ToiletingDetection,
  136. EventType.TOILETING_FREQUENCY.value : EventAttr_ToiletingFrequency,
  137. EventType.NIGHT_TOILETING_FREQUENCY.value : EventAttr_NightToiletingFrequency,
  138. EventType.TOILETING_FREQUENCY_ABNORMAL.value : EventAttr_ToiletingFrequencyAbnormal,
  139. EventType.NIGHT_TOILETING_FREQUENCY_ABNORMAL.value: EventAttr_NightToiletingFrequencyAbnormal,
  140. EventType.BATHROOM_STAY_FREQUENCY.value : EventAttr_BathroomStayFrequency,
  141. EventType.TARGET_ABSENCE.value : EventAttr_TargetAbsence,
  142. }
  143. class Cron:
  144. def __init__(self, h, m, s):
  145. self.h_ = None
  146. self.m_ = None
  147. self.s_ = None
  148. class AlarmPlan:
  149. def __init__(self,
  150. plan_uuid: str,
  151. name: str,
  152. dev_id: str,
  153. dev_name: str,
  154. enable: bool,
  155. time_plan: TimePlan,
  156. rect: list,
  157. event_type: int,
  158. threshold_time: int,
  159. merge_time: int,
  160. param: dict,
  161. cron: Optional[dict] = None
  162. ):
  163. self.lock_ = Lock()
  164. self.plan_uuid_ = plan_uuid # 计划id
  165. self.name_ = name # 计划名称
  166. self.dev_id_ = dev_id # 设备id
  167. self.dev_name_ = dev_name # 设备名称
  168. self.enable_ = enable # 是否启用
  169. self.time_plan_ = time_plan # 时间计划
  170. self.param_ = param # 参数
  171. # 维护状态(根据TimePlanu判断)
  172. self.status_ = 0 # 0未激活,1激活,-1过期
  173. self.status_update_ts_ = -1 # 状态更新时间,初始值为-1
  174. # 事件触发参数
  175. self.rect_ = rect # 检测区域 [left, top, width, height]
  176. self.threshold_time_ = threshold_time # 触发时间阈值
  177. self.merge_time_ = merge_time # 归并时间窗口
  178. self.event_type_ = event_type # 事件类型
  179. self.event_attr_ = self.init_event_attr() # 事件属性
  180. if self.event_attr_ is None:
  181. raise ValueError(f"Invalid event_type: {event_type}")
  182. self.cron_ = cron # {“hour": 6, "minute": 0}
  183. def execute(self):
  184. if self.status_ != 1:
  185. return
  186. g_las.g_alarm_plan_disp.dispatch(self.event_type_, self)
  187. # 初始化事件属性
  188. def init_event_attr(self):
  189. try:
  190. event_cls = event_attr_map.get(self.event_type_)
  191. if event_cls is None:
  192. return None
  193. event_attr = event_cls(self.event_type_)
  194. if ((self.event_type_ == EventType.TOILETING_FREQUENCY_ABNORMAL.value) or
  195. (self.event_type_ == EventType.NIGHT_TOILETING_FREQUENCY_ABNORMAL.value)):
  196. event_attr.threshold_count_ = int(self.param_.get("count", 0))
  197. if ((self.event_type_ == EventType.TARGET_ABSENCE.value)):
  198. event_attr.time_threshold_ = int(self.param_.get("time_threshold", 0))
  199. return event_attr
  200. except json.JSONDecodeError as e:
  201. tb_info = traceback.extract_tb(e.__traceback__)
  202. for frame in tb_info:
  203. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  204. except Exception as e:
  205. tb_info = traceback.extract_tb(e.__traceback__)
  206. for frame in tb_info:
  207. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  208. # 更新激活状态
  209. def update_status(self, now: Optional[datetime] = None) -> None:
  210. now = now or datetime.now()
  211. old_status = self.status_
  212. if not self.enable_:
  213. self.status_ = 0
  214. else:
  215. now_fmt = now.strftime("%Y-%m-%d")
  216. # 过期
  217. if now_fmt > self.time_plan_.stop_date_:
  218. self.status_ = -1
  219. elif now_fmt < self.time_plan_.start_date_:
  220. self.status_ = 0
  221. elif self.time_plan_.is_active_now(now):
  222. self.status_ = 1
  223. else:
  224. self.status_ = 0
  225. if self.status_ != old_status:
  226. self.status_update_ts = int(now.timestamp())
  227. LOGINFO(f"[Status] plan {self.plan_uuid_} status_ changed {old_status} -> {self.status_}")
  228. def is_point_in_rect(self, x:float, y:float, rect:list) -> bool:
  229. rx, ry, rw, rh = rect
  230. x_min = min(rx, rx + rw)
  231. x_max = max(rx, rx + rw)
  232. y_min = min(ry, ry - rh)
  233. y_max = max(ry, ry - rh)
  234. bRet: bool = x_min <= x <= x_max and y_min <= y <= y_max
  235. return bRet
  236. # 查找最近 t 秒内,最后一个落在 rect 内的 target_point 的 rtd_unit
  237. def find_latest_rtd_in_region(self, device: Device, rect: list, now: int=None, t: int=1):
  238. now_s = now if now else get_utc_time_s()
  239. rtd_que_copy = device.get_rtd_que_copy()
  240. with self.lock_:
  241. for rtd_unit in reversed(rtd_que_copy): # 倒序扫描
  242. ts_s = int(rtd_unit.get("timestamp", 0))
  243. if now_s - ts_s > t:
  244. break # 已经超过 t 秒,可以直接结束
  245. # 检查点是否在区域内
  246. for pt in rtd_unit.get("target_point", []):
  247. if len(pt) >= 2:
  248. x, y = pt[0], pt[1]
  249. if self.is_point_in_rect(x, y, rect):
  250. return rtd_unit
  251. return None
  252. # 停留事件
  253. def handle_stay_detection(self):
  254. try:
  255. dev_id = self.dev_id_
  256. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  257. if not device:
  258. return
  259. now = get_utc_time_s()
  260. # 查找最新的落在检测区域的目标
  261. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  262. if rtd_unit:
  263. timestamp = rtd_unit["timestamp"]
  264. pose = rtd_unit["pose"]
  265. target_point = rtd_unit["target_point"]
  266. if self.event_attr_.enter_ts_ == -1:
  267. self.event_attr_.enter_ts_ = timestamp
  268. else:
  269. self.event_attr_.leave_ts_ = timestamp
  270. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  271. return
  272. # 归并时间内,不认为事件结束
  273. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  274. return
  275. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  276. stay_time =self.event_attr_.stay_time_
  277. # 时间小于触发时间阈值,忽略并重置
  278. if stay_time < self.threshold_time_ :
  279. self.event_attr_.reset()
  280. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  281. return
  282. # 构造事件
  283. # 入库
  284. info = {
  285. "start_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  286. "end_time": utc_to_bj_s(self.event_attr_.leave_ts_),
  287. "stay_time": stay_time
  288. }
  289. event_uuid = str(uuid.uuid4())
  290. params = {
  291. "dev_id": dev_id,
  292. "uuid": event_uuid,
  293. "plan_uuid": self.plan_uuid_,
  294. "event_type": event_desc_map[self.event_type_],
  295. "info": json.dumps(info),
  296. "is_handle": 0,
  297. "create_time": get_bj_time_s(),
  298. "is_deleted": 0,
  299. "remark": json.dumps({}, ensure_ascii=False)
  300. }
  301. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  302. # 通知
  303. mqtt_send.alarm_event(dev_id, self.dev_name_, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  304. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  305. self.event_attr_.reset()
  306. except json.JSONDecodeError as e:
  307. tb_info = traceback.extract_tb(e.__traceback__)
  308. for frame in tb_info:
  309. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  310. except Exception as e:
  311. tb_info = traceback.extract_tb(e.__traceback__)
  312. for frame in tb_info:
  313. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  314. # 滞留事件
  315. def handle_retention_detection(self):
  316. try:
  317. dev_id = self.dev_id_
  318. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  319. if not device:
  320. return
  321. now = get_utc_time_s()
  322. # 查找最新的落在检测区域的目标
  323. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  324. if rtd_unit:
  325. timestamp = rtd_unit["timestamp"]
  326. pose = rtd_unit["pose"]
  327. target_point = rtd_unit["target_point"]
  328. if self.event_attr_.enter_ts_ == -1:
  329. self.event_attr_.enter_ts_ = timestamp
  330. else:
  331. self.event_attr_.leave_ts_ = timestamp
  332. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  333. return
  334. # 归并时间内,不认为事件结束
  335. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  336. return
  337. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  338. stay_time =self.event_attr_.stay_time_
  339. # 时间小于触发时间阈值,忽略并重置
  340. if stay_time < self.threshold_time_ :
  341. self.event_attr_.reset()
  342. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  343. return
  344. # 构造事件
  345. # 入库
  346. info = {
  347. "start_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  348. "end_time": utc_to_bj_s(self.event_attr_.leave_ts_),
  349. "stay_time": stay_time
  350. }
  351. event_uuid = str(uuid.uuid4())
  352. params = {
  353. "dev_id": dev_id,
  354. "uuid": event_uuid,
  355. "plan_uuid": self.plan_uuid_,
  356. "event_type": event_desc_map[self.event_type_],
  357. "info": json.dumps(info),
  358. "is_handle": 0,
  359. "create_time": get_bj_time_s(),
  360. "is_deleted": 0,
  361. "remark": json.dumps({}, ensure_ascii=False)
  362. }
  363. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  364. # 通知
  365. mqtt_send.alarm_event(dev_id, self.dev_name_, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  366. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  367. self.event_attr_.reset()
  368. except json.JSONDecodeError as e:
  369. tb_info = traceback.extract_tb(e.__traceback__)
  370. for frame in tb_info:
  371. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  372. except Exception as e:
  373. tb_info = traceback.extract_tb(e.__traceback__)
  374. for frame in tb_info:
  375. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  376. # 如厕事件
  377. def handle_toileting_detection(self):
  378. try:
  379. dev_id = self.dev_id_
  380. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  381. if not device:
  382. return
  383. now = get_utc_time_s()
  384. # 查找最新的落在检测区域的目标
  385. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  386. if rtd_unit:
  387. timestamp = rtd_unit["timestamp"]
  388. pose = rtd_unit["pose"]
  389. target_point = rtd_unit["target_point"]
  390. if self.event_attr_.enter_ts_ == -1:
  391. self.event_attr_.enter_ts_ = timestamp
  392. else:
  393. self.event_attr_.leave_ts_ = timestamp
  394. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  395. return
  396. # 归并时间内,不认为事件结束
  397. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  398. return
  399. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  400. stay_time =self.event_attr_.stay_time_
  401. # 时间小于触发时间阈值,忽略并重置
  402. if stay_time < self.threshold_time_ :
  403. self.event_attr_.reset()
  404. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  405. return
  406. # 构造事件
  407. # 入库
  408. info = {
  409. "start_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  410. "end_time": utc_to_bj_s(self.event_attr_.leave_ts_),
  411. "stay_time": stay_time
  412. }
  413. event_uuid = str(uuid.uuid4())
  414. params = {
  415. "dev_id": dev_id,
  416. "uuid": event_uuid,
  417. "plan_uuid": self.plan_uuid_,
  418. "event_type": event_desc_map[self.event_type_],
  419. "info": json.dumps(info),
  420. "is_handle": 0,
  421. "create_time": get_bj_time_s(),
  422. "is_deleted": 0,
  423. "remark": json.dumps({}, ensure_ascii=False)
  424. }
  425. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  426. # 通知
  427. mqtt_send.alarm_event(dev_id, self.dev_name_, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  428. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  429. self.event_attr_.reset()
  430. except json.JSONDecodeError as e:
  431. tb_info = traceback.extract_tb(e.__traceback__)
  432. for frame in tb_info:
  433. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  434. except Exception as e:
  435. tb_info = traceback.extract_tb(e.__traceback__)
  436. for frame in tb_info:
  437. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  438. # 如厕频次统计
  439. def handle_toileting_frequency(self):
  440. try:
  441. dev_id = self.dev_id_
  442. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  443. if not device:
  444. return
  445. start_dt, end_dt = helper.get_query_time_range(self.param_)
  446. params = {
  447. "dev_id" : self.dev_id_,
  448. "event_type": event_desc_map[EventType.TOILETING_DETECTION.value],
  449. "start_dt": start_dt,
  450. "end_dt": end_dt
  451. }
  452. userdata = {
  453. "start_dt" : start_dt,
  454. "end_dt" : end_dt
  455. }
  456. db_req_que.put(DBRequest(
  457. sql=sqls.sql_query_events_by_datetime,
  458. params=params,
  459. callback=self.cb_toileting_frequency,
  460. userdata=userdata))
  461. except json.JSONDecodeError as e:
  462. tb_info = traceback.extract_tb(e.__traceback__)
  463. for frame in tb_info:
  464. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  465. except Exception as e:
  466. tb_info = traceback.extract_tb(e.__traceback__)
  467. for frame in tb_info:
  468. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  469. # 夜间如厕频次统计
  470. def handle_night_toileting_frequency(self):
  471. try:
  472. dev_id = self.dev_id_
  473. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  474. if not device:
  475. return
  476. start_dt, end_dt = helper.get_query_time_range(self.param_)
  477. params = {
  478. "dev_id" : self.dev_id_,
  479. "event_type": event_desc_map[EventType.TOILETING_DETECTION.value],
  480. "start_dt": start_dt,
  481. "end_dt": end_dt
  482. }
  483. userdata = {
  484. "start_dt" : start_dt,
  485. "end_dt" : end_dt
  486. }
  487. db_req_que.put(DBRequest(
  488. sql=sqls.sql_query_events_by_datetime,
  489. params=params,
  490. callback=self.cb_night_toileting_frequency,
  491. userdata=userdata))
  492. except json.JSONDecodeError as e:
  493. tb_info = traceback.extract_tb(e.__traceback__)
  494. for frame in tb_info:
  495. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  496. except Exception as e:
  497. tb_info = traceback.extract_tb(e.__traceback__)
  498. for frame in tb_info:
  499. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  500. # 如厕频次异常
  501. def handle_toileting_frequency_abnormal(self):
  502. try:
  503. dev_id = self.dev_id_
  504. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  505. if not device:
  506. return
  507. start_dt, end_dt = helper.get_query_time_range(self.param_)
  508. params = {
  509. "dev_id" : self.dev_id_,
  510. "event_type": event_desc_map[EventType.TOILETING_DETECTION.value],
  511. "start_dt": start_dt,
  512. "end_dt": end_dt
  513. }
  514. userdata = {
  515. "start_dt" : start_dt,
  516. "end_dt" : end_dt
  517. }
  518. db_req_que.put(DBRequest(
  519. sql=sqls.sql_query_events_by_datetime,
  520. params=params,
  521. callback=self.cb_toileting_frequency_abnormal,
  522. userdata=userdata))
  523. except json.JSONDecodeError as e:
  524. tb_info = traceback.extract_tb(e.__traceback__)
  525. for frame in tb_info:
  526. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  527. except Exception as e:
  528. tb_info = traceback.extract_tb(e.__traceback__)
  529. for frame in tb_info:
  530. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  531. # 起夜异常
  532. def handle_night_toileting_frequency_abnormal(self):
  533. try:
  534. dev_id = self.dev_id_
  535. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  536. if not device:
  537. return
  538. start_dt, end_dt = helper.get_query_time_range(self.param_)
  539. params = {
  540. "dev_id" : self.dev_id_,
  541. "event_type": event_desc_map[EventType.TOILETING_DETECTION.value],
  542. "start_dt": start_dt,
  543. "end_dt": end_dt
  544. }
  545. userdata = {
  546. "start_dt" : start_dt,
  547. "end_dt" : end_dt
  548. }
  549. db_req_que.put(DBRequest(
  550. sql=sqls.sql_query_events_by_datetime,
  551. params=params,
  552. callback=self.cb_night_toileting_frequency_abnormal,
  553. userdata=userdata))
  554. except json.JSONDecodeError as e:
  555. tb_info = traceback.extract_tb(e.__traceback__)
  556. for frame in tb_info:
  557. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  558. except Exception as e:
  559. tb_info = traceback.extract_tb(e.__traceback__)
  560. for frame in tb_info:
  561. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  562. # 卫生间频次统计
  563. def handle_bathroom_stay_frequency(self):
  564. try:
  565. dev_id = self.dev_id_
  566. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  567. if not device:
  568. return
  569. start_dt, end_dt = helper.get_query_time_range(self.param_)
  570. params = {
  571. "dev_id" : self.dev_id_,
  572. "event_type": event_desc_map[EventType.STAY_DETECTION.value],
  573. "start_dt": start_dt,
  574. "end_dt": end_dt
  575. }
  576. userdata = {
  577. "start_dt" : start_dt,
  578. "end_dt" : end_dt
  579. }
  580. db_req_que.put(DBRequest(
  581. sql=sqls.sql_query_events_by_datetime,
  582. params=params,
  583. callback=self.cb_bathroom_stay_frequency,
  584. userdata=userdata))
  585. except json.JSONDecodeError as e:
  586. tb_info = traceback.extract_tb(e.__traceback__)
  587. for frame in tb_info:
  588. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  589. except Exception as e:
  590. tb_info = traceback.extract_tb(e.__traceback__)
  591. for frame in tb_info:
  592. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  593. # 异常消失
  594. def handle_target_absence(self):
  595. try:
  596. dev_id = self.dev_id_
  597. device:Device = g_Dev.g_dev_mgr.find_dev_map(dev_id)
  598. if not device:
  599. return
  600. now = get_utc_time_s()
  601. threshold: int = self.event_attr_.time_threshold_
  602. # 查找最新的落在检测区域的目标
  603. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  604. if rtd_unit:
  605. return
  606. timestamp = get_utc_time_s()
  607. if self.event_attr_.leave_ts_ == -1:
  608. self.event_attr_.leave_ts_ = timestamp
  609. else:
  610. self.event_attr_.enter_ts_ = timestamp
  611. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  612. return
  613. # 归并时间内,不认为事件结束
  614. if now - self.event_attr_.enter_ts_ < self.merge_time_:
  615. return
  616. self.event_attr_.absence_time_ = self.event_attr_.enter_ts_ - self.event_attr_.leave_ts_
  617. absence_time = self.event_attr_.absence_time_
  618. # 时间小于触发时间阈值,忽略并重置
  619. if absence_time < threshold :
  620. self.event_attr_.reset()
  621. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  622. return
  623. # 构造事件
  624. # 入库
  625. info = {
  626. "start_time": utc_to_bj_s(self.event_attr_.leave_ts_),
  627. "end_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  628. "absence_time": absence_time
  629. }
  630. event_uuid = str(uuid.uuid4())
  631. params = {
  632. "dev_id": dev_id,
  633. "uuid": event_uuid,
  634. "plan_uuid": self.plan_uuid_,
  635. "event_type": event_desc_map[self.event_type_],
  636. "info": json.dumps(info),
  637. "is_handle": 0,
  638. "create_time": get_bj_time_s(),
  639. "is_deleted": 0,
  640. "remark": json.dumps({}, ensure_ascii=False)
  641. }
  642. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  643. # 通知
  644. mqtt_send.alarm_event(dev_id, self.dev_name_, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  645. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, absence_time: {absence_time}")
  646. self.event_attr_.reset()
  647. except json.JSONDecodeError as e:
  648. tb_info = traceback.extract_tb(e.__traceback__)
  649. for frame in tb_info:
  650. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  651. except Exception as e:
  652. tb_info = traceback.extract_tb(e.__traceback__)
  653. for frame in tb_info:
  654. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  655. # 如厕频次统计回调
  656. def cb_toileting_frequency(self, result, userdata):
  657. try:
  658. if result:
  659. count = 0
  660. event_list = []
  661. for row in result:
  662. dev_id: str = row.get("dev_id")
  663. event_uuid: str = row.get("uuid")
  664. plan_uuid: str = row.get("plan_uuid")
  665. event_type: int = row.get("event_type")
  666. info: dict = json.loads(row["info"]) if row.get("info") else {}
  667. is_handle: str = row.get("is_handle")
  668. create_time: str = row.get("create_time")
  669. update_time: str = row.get("update_time")
  670. is_deleted: str = row.get("is_deleted")
  671. remark: str = row.get("remark")
  672. event_list.append(info)
  673. this_event_uuid = str(uuid.uuid4())
  674. last_info = {
  675. "start_time" : userdata["start_dt"],
  676. "end_time" : userdata["end_dt"],
  677. "count" : len(event_list),
  678. "event_list" : event_list
  679. }
  680. # 入库
  681. event_uuid = str(uuid.uuid4())
  682. params = {
  683. "dev_id": dev_id,
  684. "uuid": this_event_uuid,
  685. "plan_uuid": self.plan_uuid_,
  686. "event_type": event_desc_map[self.event_type_],
  687. "info": json.dumps(last_info, ensure_ascii=False),
  688. "is_handle": 0,
  689. "create_time": get_bj_time_s(),
  690. "is_deleted": 0,
  691. "remark": json.dumps({}, ensure_ascii=False)
  692. }
  693. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  694. # 通知
  695. mqtt_send.alarm_event(dev_id, self.dev_name_, this_event_uuid, plan_uuid, event_desc_map[self.event_type_], last_info, "events")
  696. LOGINFO(f"cb_toileting_frequency succeed")
  697. else:
  698. LOGDBG("cb_toileting_frequency, empty result")
  699. except json.JSONDecodeError as e:
  700. tb_info = traceback.extract_tb(e.__traceback__)
  701. for frame in tb_info:
  702. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  703. except Exception as e:
  704. tb_info = traceback.extract_tb(e.__traceback__)
  705. for frame in tb_info:
  706. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  707. return
  708. # 夜间如厕频次统计回调
  709. def cb_night_toileting_frequency(self, result, userdata):
  710. try:
  711. if result:
  712. count = 0
  713. event_list = []
  714. for row in result:
  715. dev_id: str = row.get("dev_id")
  716. event_uuid: str = row.get("uuid")
  717. plan_uuid: str = row.get("plan_uuid")
  718. event_type: int = row.get("event_type")
  719. info: dict = json.loads(row["info"]) if row.get("info") else {}
  720. is_handle: str = row.get("is_handle")
  721. create_time: str = row.get("create_time")
  722. update_time: str = row.get("update_time")
  723. is_deleted: str = row.get("is_deleted")
  724. remark: str = row.get("remark")
  725. event_list.append(info)
  726. this_event_uuid = str(uuid.uuid4())
  727. last_info = {
  728. "start_time" : userdata["start_dt"],
  729. "end_time" : userdata["end_dt"],
  730. "count" : len(event_list),
  731. "event_list" : event_list
  732. }
  733. # 入库
  734. event_uuid = str(uuid.uuid4())
  735. params = {
  736. "dev_id": dev_id,
  737. "uuid": this_event_uuid,
  738. "plan_uuid": self.plan_uuid_,
  739. "event_type": event_desc_map[self.event_type_],
  740. "info": json.dumps(last_info, ensure_ascii=False),
  741. "is_handle": 0,
  742. "create_time": get_bj_time_s(),
  743. "is_deleted": 0,
  744. "remark": json.dumps({}, ensure_ascii=False)
  745. }
  746. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  747. # 通知
  748. mqtt_send.alarm_event(dev_id, self.dev_name_, this_event_uuid, plan_uuid, event_desc_map[self.event_type_], last_info, "events")
  749. LOGINFO(f"cb_night_toileting_frequency succeed")
  750. else:
  751. LOGDBG("cb_night_toileting_frequency, empty result")
  752. except json.JSONDecodeError as e:
  753. tb_info = traceback.extract_tb(e.__traceback__)
  754. for frame in tb_info:
  755. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  756. except Exception as e:
  757. tb_info = traceback.extract_tb(e.__traceback__)
  758. for frame in tb_info:
  759. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  760. return
  761. # 如厕频次异常回调
  762. def cb_toileting_frequency_abnormal(self, result, userdata):
  763. try:
  764. if result:
  765. count = 0
  766. event_list = []
  767. for row in result:
  768. dev_id: str = row.get("dev_id")
  769. event_uuid: str = row.get("uuid")
  770. plan_uuid: str = row.get("plan_uuid")
  771. event_type: int = row.get("event_type")
  772. info: dict = json.loads(row["info"]) if row.get("info") else {}
  773. is_handle: str = row.get("is_handle")
  774. create_time: str = row.get("create_time")
  775. update_time: str = row.get("update_time")
  776. is_deleted: str = row.get("is_deleted")
  777. remark: str = row.get("remark")
  778. event_list.append(info)
  779. if len(event_list) < self.event_attr_.threshold_count_:
  780. return
  781. this_event_uuid = str(uuid.uuid4())
  782. last_info = {
  783. "start_time" : userdata["start_dt"],
  784. "end_time" : userdata["end_dt"],
  785. "count" : len(event_list),
  786. "event_list" : event_list
  787. }
  788. # 入库
  789. event_uuid = str(uuid.uuid4())
  790. params = {
  791. "dev_id": dev_id,
  792. "uuid": this_event_uuid,
  793. "plan_uuid": self.plan_uuid_,
  794. "event_type": event_desc_map[self.event_type_],
  795. "info": json.dumps(last_info, ensure_ascii=False),
  796. "is_handle": 0,
  797. "create_time": get_bj_time_s(),
  798. "is_deleted": 0,
  799. "remark": json.dumps({}, ensure_ascii=False)
  800. }
  801. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  802. # 通知
  803. mqtt_send.alarm_event(dev_id, self.dev_name_, this_event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], last_info, "events")
  804. LOGINFO(f"cb_toileting_frequency_abnormal succeed")
  805. else:
  806. LOGDBG("cb_toileting_frequency_abnormal, empty result")
  807. except json.JSONDecodeError as e:
  808. tb_info = traceback.extract_tb(e.__traceback__)
  809. for frame in tb_info:
  810. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  811. except Exception as e:
  812. tb_info = traceback.extract_tb(e.__traceback__)
  813. for frame in tb_info:
  814. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  815. return
  816. # 起夜异常回调
  817. def cb_night_toileting_frequency_abnormal(self, result, userdata):
  818. try:
  819. if result:
  820. count = 0
  821. event_list = []
  822. for row in result:
  823. dev_id: str = row.get("dev_id")
  824. event_uuid: str = row.get("uuid")
  825. plan_uuid: str = row.get("plan_uuid")
  826. event_type: int = row.get("event_type")
  827. info: dict = json.loads(row["info"]) if row.get("info") else {}
  828. is_handle: str = row.get("is_handle")
  829. create_time: str = row.get("create_time")
  830. update_time: str = row.get("update_time")
  831. is_deleted: str = row.get("is_deleted")
  832. remark: str = row.get("remark")
  833. event_list.append(info)
  834. if len(event_list) < self.event_attr_.threshold_count_:
  835. return
  836. this_event_uuid = str(uuid.uuid4())
  837. last_info = {
  838. "start_time" : userdata["start_dt"],
  839. "end_time" : userdata["end_dt"],
  840. "count" : len(event_list),
  841. "event_list" : event_list
  842. }
  843. # 入库
  844. event_uuid = str(uuid.uuid4())
  845. params = {
  846. "dev_id": dev_id,
  847. "uuid": this_event_uuid,
  848. "plan_uuid": self.plan_uuid_,
  849. "event_type": event_desc_map[self.event_type_],
  850. "info": json.dumps(last_info, ensure_ascii=False),
  851. "is_handle": 0,
  852. "create_time": get_bj_time_s(),
  853. "is_deleted": 0,
  854. "remark": json.dumps({}, ensure_ascii=False)
  855. }
  856. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  857. # 通知
  858. mqtt_send.alarm_event(dev_id, self.dev_name_, this_event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], last_info, "events")
  859. LOGINFO(f"cb_night_toileting_frequency_abnormal succeed")
  860. else:
  861. LOGDBG("cb_night_toileting_frequency_abnormal, empty result")
  862. except json.JSONDecodeError as e:
  863. tb_info = traceback.extract_tb(e.__traceback__)
  864. for frame in tb_info:
  865. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  866. except Exception as e:
  867. tb_info = traceback.extract_tb(e.__traceback__)
  868. for frame in tb_info:
  869. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  870. return
  871. # 卫生间频次统计回调
  872. def cb_bathroom_stay_frequency(self, result, userdata):
  873. try:
  874. if result:
  875. count = 0
  876. event_list = []
  877. for row in result:
  878. dev_id: str = row.get("dev_id")
  879. event_uuid: str = row.get("uuid")
  880. plan_uuid: str = row.get("plan_uuid")
  881. event_type: int = row.get("event_type")
  882. info: dict = json.loads(row["info"]) if row.get("info") else {}
  883. is_handle: str = row.get("is_handle")
  884. create_time: str = row.get("create_time")
  885. update_time: str = row.get("update_time")
  886. is_deleted: str = row.get("is_deleted")
  887. remark: str = row.get("remark")
  888. event_list.append(info)
  889. this_event_uuid = str(uuid.uuid4())
  890. last_info = {
  891. "start_time" : userdata["start_dt"],
  892. "end_time" : userdata["end_dt"],
  893. "count" : len(event_list),
  894. "event_list" : event_list
  895. }
  896. # 入库
  897. event_uuid = str(uuid.uuid4())
  898. params = {
  899. "dev_id": dev_id,
  900. "uuid": this_event_uuid,
  901. "plan_uuid": self.plan_uuid_,
  902. "event_type": event_desc_map[self.event_type_],
  903. "info": json.dumps(last_info, ensure_ascii=False),
  904. "is_handle": 0,
  905. "create_time": get_bj_time_s(),
  906. "is_deleted": 0,
  907. "remark": json.dumps({}, ensure_ascii=False)
  908. }
  909. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  910. # 通知
  911. mqtt_send.alarm_event(dev_id, self.dev_name_, this_event_uuid, plan_uuid, event_desc_map[self.event_type_], last_info, "events")
  912. LOGINFO(f"cb_bathroom_stay_frequency+- succeed")
  913. else:
  914. LOGDBG("cb_bathroom_stay_frequency, empty result")
  915. except json.JSONDecodeError as e:
  916. tb_info = traceback.extract_tb(e.__traceback__)
  917. for frame in tb_info:
  918. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  919. except Exception as e:
  920. tb_info = traceback.extract_tb(e.__traceback__)
  921. for frame in tb_info:
  922. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  923. return