alarm_plan.py 48 KB

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