alarm_plan.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  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 device.dev_mng import (
  19. Device,
  20. dev_map_push, dev_map_pop, dev_map_find, dev_map_delete
  21. )
  22. from db.db_process import (
  23. db_req_que, DBRequest
  24. )
  25. import db.db_sqls as sqls
  26. import mqtt.mqtt_send as mqtt_send
  27. from device.dev_mng import g_dev_map, g_dev_map_lock
  28. class EventAttr_Base:
  29. def __init__(self):
  30. return
  31. # 事件属性 事件事件
  32. class EventAttr_StayDetection(EventAttr_Base):
  33. def __init__(self, event_type):
  34. self.event_type_ = event_type
  35. self.enter_ts_: int = -1 # 进入时间(s)
  36. self.leave_ts_: int = -1 # 离开时间(s)
  37. self.stay_time_: int = -1 # 停留时长(s)
  38. return
  39. def reset(self):
  40. self.enter_ts_ = -1
  41. self.leave_ts_ = -1
  42. self.stay_time_ = -1
  43. # 事件属性 滞留事件
  44. class EventAttr_RetentionDetection(EventAttr_Base):
  45. def __init__(self, event_type):
  46. self.event_type_ = event_type
  47. self.enter_ts_: int = -1 # 进入时间(s)
  48. self.leave_ts_: int = -1 # 离开时间(s)
  49. self.stay_time_: int = -1 # 停留时长(s)
  50. return
  51. def reset(self):
  52. self.enter_ts_ = -1
  53. self.leave_ts_ = -1
  54. self.stay_time_ = -1
  55. # 事件属性 如厕事件
  56. class EventAttr_ToiletingDetection(EventAttr_Base):
  57. def __init__(self, event_type):
  58. self.event_type_ = event_type
  59. self.enter_ts_: int = -1 # 进入时间(ms)
  60. self.leave_ts_: int = -1 # 离开时间(ms)
  61. self.stay_time_: int = -1 # 停留时长(ms)
  62. return
  63. def reset(self):
  64. self.enter_ts_ = -1
  65. self.leave_ts_ = -1
  66. self.stay_time_ = -1
  67. # 事件属性 如厕频次统计
  68. class EventAttr_ToiletingFrequency(EventAttr_Base):
  69. def __init__(self, event_type):
  70. self.event_type_ = event_type
  71. self.count_: int = 0
  72. self.event_list: list = []
  73. return
  74. def reset(self):
  75. self.count_ = 0
  76. self.event_list = []
  77. # 事件属性 夜间如厕频次统计
  78. class EventAttr_NightToiletingFrequency(EventAttr_Base):
  79. def __init__(self, event_type):
  80. self.event_type_ = event_type
  81. self.enter_ts_: int = -1 # 进入时间(ms)
  82. self.leave_ts_: int = -1 # 离开时间(ms)
  83. self.stay_time_: int = -1 # 停留时长(ms)
  84. return
  85. def reset(self):
  86. self.enter_ts_ = -1
  87. self.leave_ts_ = -1
  88. self.stay_time_ = -1
  89. # 事件属性 如厕频次异常
  90. class EventAttr_ToiletingFrequencyAbnormal(EventAttr_Base):
  91. def __init__(self, event_type):
  92. self.event_type_ = event_type
  93. self.enter_ts_: int = -1 # 进入时间(ms)
  94. self.leave_ts_: int = -1 # 离开时间(ms)
  95. self.stay_time_: int = -1 # 停留时长(ms)
  96. return
  97. def reset(self):
  98. self.enter_ts_ = -1
  99. self.leave_ts_ = -1
  100. self.stay_time_ = -1
  101. # 事件属性 起夜异常
  102. class EventAttr_NightToiletingFrequencyAbnormal(EventAttr_Base):
  103. def __init__(self, event_type):
  104. self.event_type_ = event_type
  105. self.enter_ts_: int = -1 # 进入时间(ms)
  106. self.leave_ts_: int = -1 # 离开时间(ms)
  107. self.stay_time_: int = -1 # 停留时长(ms)
  108. return
  109. def reset(self):
  110. self.enter_ts_ = -1
  111. self.leave_ts_ = -1
  112. self.stay_time_ = -1
  113. # 事件属性 卫生间频次统计
  114. class EventAttr_BathroomStayFrequency(EventAttr_Base):
  115. def __init__(self, event_type):
  116. self.event_type_ = event_type
  117. self.enter_ts_: int = -1 # 进入时间(ms)
  118. self.leave_ts_: int = -1 # 离开时间(ms)
  119. self.stay_time_: int = -1 # 停留时长(ms)
  120. return
  121. def reset(self):
  122. self.enter_ts_ = -1
  123. self.leave_ts_ = -1
  124. self.stay_time_ = -1
  125. # 事件属性 异常消失
  126. class EventAttr_TargetAbsence(EventAttr_Base):
  127. def __init__(self, event_type):
  128. self.event_type_ = event_type
  129. self.enter_ts_: int = -1 # 进入时间(ms)
  130. self.leave_ts_: int = -1 # 离开时间(ms)
  131. self.stay_time_: int = -1 # 停留时长(ms)
  132. return
  133. def reset(self):
  134. self.enter_ts_ = -1
  135. self.leave_ts_ = -1
  136. self.stay_time_ = -1
  137. # 事件属性表
  138. event_attr_map = {
  139. EventType.STAY_DETECTION.value : EventAttr_StayDetection,
  140. EventType.RETENTION_DETECTION.value : EventAttr_RetentionDetection,
  141. EventType.TOILETING_DETECTION.value : EventAttr_ToiletingDetection,
  142. EventType.TOILETING_FREQUENCY.value : EventAttr_ToiletingFrequency,
  143. EventType.NIGHT_TOILETING_FREQUENCY.value : EventAttr_NightToiletingFrequency,
  144. EventType.TOILETING_FREQUENCY_ABNORMAL.value : EventAttr_ToiletingFrequencyAbnormal,
  145. EventType.NIGHT_TOILETING_FREQUENCY_ABNORMAL.value: EventAttr_NightToiletingFrequencyAbnormal,
  146. EventType.BATHROOM_STAY_FREQUENCY.value : EventAttr_BathroomStayFrequency,
  147. EventType.TARGET_ABSENCE.value : EventAttr_TargetAbsence,
  148. }
  149. class Cron:
  150. def __init__(self, h, m, s):
  151. self.h_ = None
  152. self.m_ = None
  153. self.s_ = None
  154. class AlarmPlan:
  155. def __init__(self,
  156. plan_uuid: str,
  157. name: str,
  158. dev_id: str,
  159. enable: bool,
  160. time_plan: TimePlan,
  161. rect: list,
  162. event_type: int,
  163. threshold_time: int,
  164. merge_time: int,
  165. param: dict,
  166. cron: Optional[dict] = None
  167. ):
  168. self.lock_ = Lock()
  169. self.plan_uuid_ = plan_uuid # 计划id
  170. self.name_ = name # 计划名称
  171. self.dev_id_ = dev_id # 设备id
  172. self.enable_ = enable # 是否启用
  173. self.time_plan_ = time_plan # 时间计划
  174. self.param_ = param # 参数
  175. # 维护状态(根据TimePlanu判断)
  176. self.status_ = 0 # 0未激活,1激活,-1过期
  177. self.status_update_ts_ = -1 # 状态更新时间,初始值为-1
  178. # 事件触发参数
  179. self.rect_ = rect # 检测区域 [left, top, width, height]
  180. self.threshold_time_ = threshold_time # 触发时间阈值
  181. self.merge_time_ = merge_time # 归并时间窗口
  182. self.event_type_ = event_type # 事件类型
  183. self.event_attr_ = self.init_event_attr() # 事件属性
  184. if self.event_attr_ is None:
  185. raise ValueError(f"Invalid event_type: {event_type}")
  186. self.cron_ = cron # {“hour": 6, "minute": 0}
  187. def execute(self):
  188. if self.status_ != 1:
  189. return
  190. g_las.g_event_dispatcher.dispatch(self.event_type_, self)
  191. # 初始化事件属性
  192. def init_event_attr(self):
  193. event_cls = event_attr_map.get(self.event_type_)
  194. if event_cls is None:
  195. return None
  196. event_attr = event_cls(self.event_type_)
  197. return event_attr
  198. # 更新激活状态
  199. def update_status(self, now: Optional[datetime] = None) -> None:
  200. now = now or datetime.now()
  201. old_status = self.status_
  202. if not self.enable_:
  203. self.status_ = 0
  204. else:
  205. now_fmt = now.strftime("%Y-%m-%d")
  206. # 过期
  207. if now_fmt > self.time_plan_.stop_date_:
  208. self.status_ = -1
  209. elif now_fmt < self.time_plan_.start_date_:
  210. self.status_ = 0
  211. elif self.time_plan_.is_active_now(now):
  212. self.status_ = 1
  213. else:
  214. self.status_ = 0
  215. if self.status_ != old_status:
  216. self.status_update_ts = int(now.timestamp())
  217. LOGINFO(f"[Status] plan {self.plan_uuid_} status_ changed {old_status} -> {self.status_}")
  218. def is_point_in_rect(self, x:float, y:float, rect:list) -> bool:
  219. rx, ry, rw, rh = rect
  220. x_min = min(rx, rx + rw)
  221. x_max = max(rx, rx + rw)
  222. y_min = min(ry, ry - rh)
  223. y_max = max(ry, ry - rh)
  224. bRet: bool = x_min <= x <= x_max and y_min <= y <= y_max
  225. return bRet
  226. # 查找最近 t 秒内,最后一个落在 rect 内的 target_point 的 rtd_unit
  227. def find_latest_rtd_in_region(self, device: Device, rect: list, now: int=None, t: int=1):
  228. now_s = now if now else get_utc_time_s()
  229. rtd_que_copy = device.get_rtd_que_copy()
  230. with self.lock_:
  231. for rtd_unit in reversed(rtd_que_copy): # 倒序扫描
  232. ts_s = int(rtd_unit.get("timestamp", 0))
  233. if now_s - ts_s > t:
  234. break # 已经超过 t 秒,可以直接结束
  235. # 检查点是否在区域内
  236. for pt in rtd_unit.get("target_point", []):
  237. if len(pt) >= 2:
  238. x, y = pt[0], pt[1]
  239. if self.is_point_in_rect(x, y, rect):
  240. return rtd_unit
  241. return None
  242. # 停留事件
  243. def handle_stay_detection(self):
  244. try:
  245. dev_id = self.dev_id_
  246. device:Device = dev_map_find(dev_id)
  247. if not device:
  248. return
  249. now = get_utc_time_s()
  250. # 查找最新的落在检测区域的目标
  251. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  252. if rtd_unit:
  253. timestamp = rtd_unit["timestamp"]
  254. pose = rtd_unit["pose"]
  255. target_point = rtd_unit["target_point"]
  256. if self.event_attr_.enter_ts_ == -1:
  257. self.event_attr_.enter_ts_ = timestamp
  258. else:
  259. self.event_attr_.leave_ts_ = timestamp
  260. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  261. return
  262. # 归并时间内,不认为事件结束
  263. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  264. return
  265. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  266. stay_time =self.event_attr_.stay_time_
  267. # 时间小于触发时间阈值,忽略并重置
  268. if stay_time < self.threshold_time_ :
  269. self.event_attr_.reset()
  270. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  271. return
  272. # 构造事件
  273. # 入库
  274. info = {
  275. "enter_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  276. "leave_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  277. "stay_time": stay_time
  278. }
  279. event_uuid = str(uuid.uuid4())
  280. params = {
  281. "dev_id": dev_id,
  282. "uuid": event_uuid,
  283. "plan_uuid": self.plan_uuid_,
  284. "event_type": event_desc_map[self.event_type_],
  285. "info": json.dumps(info),
  286. "is_handle": 0,
  287. "create_time": get_bj_time_s(),
  288. "is_deleted": 0,
  289. "remark": json.dumps({}, ensure_ascii=False)
  290. }
  291. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  292. # 通知
  293. mqtt_send.alarm_event(dev_id, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  294. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  295. self.event_attr_.reset()
  296. except json.JSONDecodeError as e:
  297. tb_info = traceback.extract_tb(e.__traceback__)
  298. for frame in tb_info:
  299. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  300. except Exception as e:
  301. tb_info = traceback.extract_tb(e.__traceback__)
  302. for frame in tb_info:
  303. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  304. # 滞留事件
  305. def handle_retention_detection(self):
  306. try:
  307. dev_id = self.dev_id_
  308. device:Device = dev_map_find(dev_id)
  309. if not device:
  310. return
  311. now = get_utc_time_s()
  312. # 查找最新的落在检测区域的目标
  313. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  314. if rtd_unit:
  315. timestamp = rtd_unit["timestamp"]
  316. pose = rtd_unit["pose"]
  317. target_point = rtd_unit["target_point"]
  318. if self.event_attr_.enter_ts_ == -1:
  319. self.event_attr_.enter_ts_ = timestamp
  320. else:
  321. self.event_attr_.leave_ts_ = timestamp
  322. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  323. return
  324. # 归并时间内,不认为事件结束
  325. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  326. return
  327. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  328. stay_time =self.event_attr_.stay_time_
  329. # 时间小于触发时间阈值,忽略并重置
  330. if stay_time < self.threshold_time_ :
  331. self.event_attr_.reset()
  332. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  333. return
  334. # 构造事件
  335. # 入库
  336. info = {
  337. "enter_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  338. "leave_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  339. "stay_time": stay_time
  340. }
  341. event_uuid = str(uuid.uuid4())
  342. params = {
  343. "dev_id": dev_id,
  344. "uuid": event_uuid,
  345. "plan_uuid": self.plan_uuid_,
  346. "event_type": event_desc_map[self.event_type_],
  347. "info": json.dumps(info),
  348. "is_handle": 0,
  349. "create_time": get_bj_time_s(),
  350. "is_deleted": 0,
  351. "remark": json.dumps({}, ensure_ascii=False)
  352. }
  353. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  354. # 通知
  355. mqtt_send.alarm_event(dev_id, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  356. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  357. self.event_attr_.reset()
  358. except json.JSONDecodeError as e:
  359. tb_info = traceback.extract_tb(e.__traceback__)
  360. for frame in tb_info:
  361. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  362. except Exception as e:
  363. tb_info = traceback.extract_tb(e.__traceback__)
  364. for frame in tb_info:
  365. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  366. # 如厕事件
  367. def handle_toileting_detection(self):
  368. try:
  369. dev_id = self.dev_id_
  370. device:Device = dev_map_find(dev_id)
  371. if not device:
  372. return
  373. now = get_utc_time_s()
  374. # 查找最新的落在检测区域的目标
  375. rtd_unit = self.find_latest_rtd_in_region(device, self.rect_, now, 3)
  376. if rtd_unit:
  377. timestamp = rtd_unit["timestamp"]
  378. pose = rtd_unit["pose"]
  379. target_point = rtd_unit["target_point"]
  380. if self.event_attr_.enter_ts_ == -1:
  381. self.event_attr_.enter_ts_ = timestamp
  382. else:
  383. self.event_attr_.leave_ts_ = timestamp
  384. if self.event_attr_.enter_ts_ == -1 or self.event_attr_.leave_ts_ == -1:
  385. return
  386. # 归并时间内,不认为事件结束
  387. if now - self.event_attr_.leave_ts_ < self.merge_time_:
  388. return
  389. self.event_attr_.stay_time_ = self.event_attr_.leave_ts_ - self.event_attr_.enter_ts_
  390. stay_time =self.event_attr_.stay_time_
  391. # 时间小于触发时间阈值,忽略并重置
  392. if stay_time < self.threshold_time_ :
  393. self.event_attr_.reset()
  394. LOGINFO(f"less than threshold_time, alarm_plan: {self.plan_uuid_}, event_type: {self.event_type_}")
  395. return
  396. # 构造事件
  397. # 入库
  398. info = {
  399. "enter_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  400. "leave_time": utc_to_bj_s(self.event_attr_.enter_ts_),
  401. "stay_time": stay_time
  402. }
  403. event_uuid = str(uuid.uuid4())
  404. params = {
  405. "dev_id": dev_id,
  406. "uuid": event_uuid,
  407. "plan_uuid": self.plan_uuid_,
  408. "event_type": event_desc_map[self.event_type_],
  409. "info": json.dumps(info),
  410. "is_handle": 0,
  411. "create_time": get_bj_time_s(),
  412. "is_deleted": 0,
  413. "remark": json.dumps({}, ensure_ascii=False)
  414. }
  415. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  416. # 通知
  417. mqtt_send.alarm_event(dev_id, event_uuid, self.plan_uuid_, event_desc_map[self.event_type_], info, "events")
  418. LOGDBG(f"new event: {event_desc_map[self.event_type_]}, stay_time: {stay_time}")
  419. self.event_attr_.reset()
  420. except json.JSONDecodeError as e:
  421. tb_info = traceback.extract_tb(e.__traceback__)
  422. for frame in tb_info:
  423. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  424. except Exception as e:
  425. tb_info = traceback.extract_tb(e.__traceback__)
  426. for frame in tb_info:
  427. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  428. # 如厕频次统计
  429. def handle_toileting_frequency(self):
  430. try:
  431. dev_id = self.dev_id_
  432. device:Device = dev_map_find(dev_id)
  433. if not device:
  434. return
  435. start_dt, end_dt = helper.get_query_time_range(self.param_)
  436. params = {
  437. "event_type": event_desc_map[EventType.TOILETING_DETECTION.value],
  438. "start_dt": start_dt,
  439. "end_dt": end_dt
  440. }
  441. db_req_que.put(DBRequest(
  442. sql=sqls.sql_query_events_by_datetime,
  443. params=params,
  444. callback=self.cb_toileting_frequency))
  445. except json.JSONDecodeError as e:
  446. tb_info = traceback.extract_tb(e.__traceback__)
  447. for frame in tb_info:
  448. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  449. except Exception as e:
  450. tb_info = traceback.extract_tb(e.__traceback__)
  451. for frame in tb_info:
  452. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  453. def cb_toileting_frequency(self, result):
  454. try:
  455. if result:
  456. count = 0
  457. event_list = []
  458. for row in result:
  459. dev_id: str = row.get("dev_id")
  460. event_uuid: str = row.get("uuid")
  461. plan_uuid: str = row.get("plan_uuid")
  462. event_type: int = row.get("event_type")
  463. info: dict = json.loads(row["info"]) if row.get("info") else {}
  464. is_handle: str = row.get("is_handle")
  465. create_time: str = row.get("create_time")
  466. update_time: str = row.get("update_time")
  467. is_deleted: str = row.get("is_deleted")
  468. remark: str = row.get("remark")
  469. event_list.append(info)
  470. this_event_uuid = str(uuid.uuid4())
  471. new_param = helper.normalize_param_time(self.param_)
  472. last_info = {
  473. "start_time" : new_param["start_time"],
  474. "end_time" : new_param["end_time"],
  475. "count" : len(event_list),
  476. "event_list" : event_list
  477. }
  478. # 入库
  479. event_uuid = str(uuid.uuid4())
  480. params = {
  481. "dev_id": dev_id,
  482. "uuid": this_event_uuid,
  483. "plan_uuid": self.plan_uuid_,
  484. "event_type": event_desc_map[self.event_type_],
  485. "info": json.dumps(last_info, ensure_ascii=False),
  486. "is_handle": 0,
  487. "create_time": get_bj_time_s(),
  488. "is_deleted": 0,
  489. "remark": json.dumps({}, ensure_ascii=False)
  490. }
  491. db_req_que.put(DBRequest(sql=sqls.sql_insert_events, params=params, callback=None))
  492. # 通知
  493. mqtt_send.alarm_event(dev_id, this_event_uuid, plan_uuid, event_desc_map[self.event_type_], last_info, "events")
  494. LOGINFO(f"EventAttr_ToiletingFrequency succeed")
  495. else:
  496. LOGDBG("EventAttr_ToiletingFrequency, empty result")
  497. except json.JSONDecodeError as e:
  498. tb_info = traceback.extract_tb(e.__traceback__)
  499. for frame in tb_info:
  500. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error:{e}, {e.doc}")
  501. except Exception as e:
  502. tb_info = traceback.extract_tb(e.__traceback__)
  503. for frame in tb_info:
  504. LOGERR(f"[{frame.filename}:{frame.lineno}] @{frame.name}(), error: {e}")
  505. return
  506. # 夜间如厕频次统计
  507. def handle_night_toileting_frequency(self):
  508. return
  509. # 如厕频次异常
  510. def handle_toileting_frequency_abnormal(self):
  511. return
  512. # 起夜异常
  513. def handle_night_toileting_frequency_abnormal(self):
  514. return
  515. # 卫生间频次统计
  516. def handle_bathroom_stay_frequency(self):
  517. return
  518. # 异常消失
  519. def handle_target_absence(self):
  520. return