FuncOnAndOffWork.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. #include "FuncOnAndOffWork.h"
  2. #include "GlobalVariable.h"
  3. #include "GlobalConfig.h"
  4. #include "UniversalFunc.h"
  5. #include "FromRedis.h"
  6. #include "FromWebAPI.h"
  7. DecCondition::DecCondition()
  8. {
  9. ChannelID = -1;
  10. leaveTimeMaxNum = 0;
  11. leaveNumOnTime = 0;
  12. leaveOneTime = 0;
  13. }
  14. DecCondition::DecCondition(const DecCondition& other)
  15. {
  16. ChannelID = other.ChannelID;
  17. leaveTimeMaxNum = other.leaveTimeMaxNum;
  18. leaveNumOnTime = other.leaveNumOnTime;
  19. leaveOneTime = other.leaveOneTime;
  20. }
  21. DecCondition& DecCondition::operator=(const DecCondition& other)
  22. {
  23. if (this != &other)
  24. {
  25. ChannelID = other.ChannelID;
  26. leaveTimeMaxNum = other.leaveTimeMaxNum;
  27. leaveNumOnTime = other.leaveNumOnTime;
  28. leaveOneTime = other.leaveOneTime;
  29. }
  30. return *this;
  31. }
  32. RoomOnWorkInfo::RoomOnWorkInfo()
  33. {
  34. bOnWork = false;
  35. PKID = 0;
  36. ChannelID = 0;
  37. RoomID = 0;
  38. CameraID = 0;
  39. RoomType = Enum_RoomType::ROOM_NONE;
  40. strRoomName = "";
  41. StartTime = QDateTime::fromString("1970-01-01 00:00:00", "yyyy-MM-dd hh:mm:ss");
  42. EndTime = QDateTime::fromString("1970-01-01 00:00:00", "yyyy-MM-dd hh:mm:ss");
  43. strImagePath = "";
  44. listLeaveTime.clear();
  45. listPersonInfo.clear();
  46. }
  47. RoomOnWorkInfo& RoomOnWorkInfo::operator=(const RoomOnWorkInfo& other)
  48. {
  49. if (this != &other)
  50. {
  51. bOnWork = other.bOnWork;
  52. PKID = other.PKID;
  53. ChannelID = other.ChannelID;
  54. RoomID = other.RoomID;
  55. CameraID = other.CameraID;
  56. RoomType = other.RoomType;
  57. strRoomName = other.strRoomName;
  58. StartTime = other.StartTime;
  59. EndTime = other.EndTime;
  60. strImagePath = other.strImagePath;
  61. listPersonInfo = other.listPersonInfo;
  62. listLeaveTime = other.listLeaveTime;
  63. }
  64. return *this;
  65. }
  66. RoomOnWorkInfo::RoomOnWorkInfo(const RoomOnWorkInfo& other)
  67. {
  68. bOnWork = other.bOnWork;
  69. PKID = other.PKID;
  70. ChannelID = other.ChannelID;
  71. RoomID = other.RoomID;
  72. CameraID = other.CameraID;
  73. RoomType = other.RoomType;
  74. strRoomName = other.strRoomName;
  75. StartTime = other.StartTime;
  76. EndTime = other.EndTime;
  77. strImagePath = other.strImagePath;
  78. listPersonInfo = other.listPersonInfo;
  79. listLeaveTime = other.listLeaveTime;
  80. }
  81. void RoomOnWorkInfo::clear()
  82. {
  83. bOnWork = false;
  84. PKID = 0;
  85. ChannelID = 0;
  86. RoomID = 0;
  87. CameraID = 0;
  88. RoomType = Enum_RoomType::ROOM_NONE;
  89. strRoomName = "";
  90. StartTime = QDateTime::fromString("1970-01-01 00:00:00", "yyyy-MM-dd hh:mm:ss");
  91. EndTime = QDateTime::fromString("1970-01-01 00:00:00", "yyyy-MM-dd hh:mm:ss");
  92. strImagePath = "";
  93. listLeaveTime.clear();
  94. listPersonInfo.clear();
  95. }
  96. /* 添加人员信息,进行查重 */
  97. void RoomOnWorkInfo::addPersonInfo(const std::list<PersonInfo>& vecInfo)
  98. {
  99. for(auto& it : vecInfo)
  100. {
  101. /* 查重 */
  102. bool bFind = false;
  103. for(auto& it0 : listPersonInfo)
  104. {
  105. if(it0.PersonID == it.PersonID && it0.PersonName == it.PersonName)
  106. {
  107. bFind = true;
  108. break;
  109. }
  110. }
  111. if(!bFind)
  112. {
  113. listPersonInfo.push_back(it);
  114. }
  115. }
  116. }
  117. /* 更新在岗时间 */
  118. void RoomOnWorkInfo::updateOnWorkTime(const QDateTime& time)
  119. {
  120. for(auto& it : listPersonInfo)
  121. {
  122. it.lastOnWork = time;
  123. }
  124. }
  125. /* 获取人脸ID字符串 */
  126. std::string RoomOnWorkInfo::getFaceIDListString()
  127. {
  128. std::string strFaceList;
  129. for(const auto& it : listPersonInfo)
  130. {
  131. strFaceList += it.PersonID + ";" ;
  132. }
  133. /* 去掉最后的“;” */
  134. strFaceList = strFaceList.substr(0, strFaceList.size() - 1);
  135. return strFaceList;
  136. }
  137. /* 获取人脸字符串 */
  138. std::string RoomOnWorkInfo::getFaceNameListString()
  139. {
  140. std::string strFaceName;
  141. for(const auto& it : listPersonInfo)
  142. {
  143. strFaceName += it.PersonName + ";";
  144. }
  145. /* 去掉最后的“;” */
  146. strFaceName = strFaceName.substr(0, strFaceName.size() - 1);
  147. return strFaceName;
  148. }
  149. /* 获取离岗时间字符串 */
  150. std::string RoomOnWorkInfo::getLeaveTimeString()
  151. {
  152. std::string strLeaveTime;
  153. for(const auto& it : listLeaveTime)
  154. {
  155. strLeaveTime += it.toString("yyyy-MM-dd hh:mm:ss").toStdString() + ";";
  156. }
  157. /* 去掉最后的“;” */
  158. strLeaveTime = strLeaveTime.substr(0, strLeaveTime.size() - 1);
  159. return strLeaveTime;
  160. }
  161. /* 获取人员最后在岗时间字符串 */
  162. std::string RoomOnWorkInfo::getLastOnWorkTimeString()
  163. {
  164. std::string strLastOnWorkTime;
  165. for(const auto& it : listPersonInfo)
  166. {
  167. strLastOnWorkTime += it.lastOnWork.toString("yyyy-MM-dd hh:mm:ss").toStdString() + ";";
  168. }
  169. /* 去掉最后的“;” */
  170. strLastOnWorkTime = strLastOnWorkTime.substr(0, strLastOnWorkTime.size() - 1);
  171. return strLastOnWorkTime;
  172. }
  173. /* 添加一次离岗时间 */
  174. void RoomOnWorkInfo::addOneLeaveTime(const QDateTime& time, int maxLeaveTime)
  175. {
  176. if(isLeaveTimeExist(time))
  177. {
  178. return;
  179. }
  180. /* 对时间个数进行限制,不能太多 */
  181. if(listLeaveTime.size() > maxLeaveTime)
  182. {
  183. listLeaveTime.pop_front();
  184. }
  185. listLeaveTime.push_back(time);
  186. }
  187. /* 对离岗时间进行查重 */
  188. bool RoomOnWorkInfo::isLeaveTimeExist(const QDateTime& time)
  189. {
  190. for(auto& it : listLeaveTime)
  191. {
  192. if(it == time)
  193. {
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. FuncOnAndOffWork::FuncOnAndOffWork()
  200. {
  201. m_logger = spdlog::get("SPAServer");
  202. if(m_logger == nullptr)
  203. {
  204. SPDLOG_ERROR("FuncOnAndOffWork logger is nullptr");
  205. return;
  206. }
  207. }
  208. FuncOnAndOffWork::~FuncOnAndOffWork()
  209. {
  210. }
  211. void FuncOnAndOffWork::task()
  212. {
  213. while(GThreadInfo.getRunning())
  214. {
  215. /* 休眠一段时间 */
  216. std::this_thread::sleep_for(std::chrono::milliseconds(GVariable.ThreadSleepMS));
  217. /* 判断是否需要退出 */
  218. if(m_funcThreadInfo.RunState == RunTimeState::RUN_STATE_STOP)
  219. {
  220. break;
  221. }
  222. /* 判断是否在检测时间段内 */
  223. if(!isInDetectTime(m_periodInfo))
  224. {
  225. continue;
  226. }
  227. /* -----------------------------------------------------------------------
  228. * 读取Redis数据(读取全部的数据,后面根据不同情况自己处理)
  229. * ----------------------------------------------------------------------- */
  230. readRedisData(m_listSrcAlarm);
  231. /* -----------------------------------------------------------------------
  232. * 处理数据
  233. * ----------------------------------------------------------------------- */
  234. /* 记录人员在岗情况 */
  235. recordPersonOnWork();
  236. /* 处理直播间在岗离岗报警情况 */
  237. processRoomOnWorkAlarm();
  238. }
  239. /* 清空数据 */
  240. for(auto& it : m_mapRoomOnWorkInfo)
  241. {
  242. delete it.second;
  243. }
  244. m_mapRoomOnWorkInfo.clear();
  245. m_listSrcAlarm.clearAlarmInfo();
  246. }
  247. /* 记录在岗离岗情况 */
  248. void FuncOnAndOffWork::recordPersonOnWork()
  249. {
  250. /* -----------------------------------------------------------------------
  251. * 处理数据
  252. * ----------------------------------------------------------------------- */
  253. /* 处理数据,将报警信息的人脸信息取出来,放入到人脸信息列表中 */
  254. for(auto& alarmInfo : m_listSrcAlarm.listAlarmInfo)
  255. {
  256. /* 添加到人脸列表中,会自动去重,自动创建对应的频道和房间信息 */
  257. m_pListRoomFace->addRoomFaceInfo(*alarmInfo);
  258. }
  259. /* 计算人脸个数,每个房间逐个对比 */
  260. m_nowTime = QDateTime::currentDateTime();
  261. for(auto it = m_pListRoomFace->listRoomFaceInfo.begin(); it != m_pListRoomFace->listRoomFaceInfo.end(); )
  262. {
  263. /* m_pListRoomFace的数据不会删除,会和历史对比,这里的MaxNum和MinNum就是历史数据 */
  264. if(it->MaxNum < it->listPersonInfo.size())
  265. {
  266. it->MaxNum = it->listPersonInfo.size();
  267. }
  268. if(it->MinNum > it->listPersonInfo.size())
  269. {
  270. it->MinNum = it->listPersonInfo.size();
  271. }
  272. /* 判断是否需要写入数据库,一定时间写入一次,默认是10分钟写一次 */
  273. if(m_nowTime.toSecsSinceEpoch() - it->StartTime.toSecsSinceEpoch() > GVariable.AppUpdateOnWorkTimeInterval_Time)
  274. {
  275. /* 写入数据库 */
  276. if(m_fromWebAPI->insertOnWorkInfo(*it))
  277. {
  278. SPDLOG_LOGGER_INFO(m_logger, "☆ 新增人脸信息,频道[{}][{}],摄像头[{}][{}],时间范围[{} - {}],人数范围[{} - {}]",
  279. it->ChannelID, GConfig.getChannelName(it->ChannelID), it->CameraID, GConfig.getCameraName(it->CameraID),
  280. it->StartTime.toString("yyyy-MM-dd hh:mm:ss").toStdString(), it->EndTime.toString("yyyy-MM-dd hh:mm:ss").toStdString(),
  281. it->MinNum, it->MaxNum);
  282. /* 删除这条信息 */
  283. it = m_pListRoomFace->listRoomFaceInfo.erase(it);
  284. continue;
  285. }else {
  286. SPDLOG_LOGGER_ERROR(m_logger, "写入数据库tWorkOnInfo失败");
  287. }
  288. }
  289. ++it;
  290. }
  291. }
  292. /* 处理房间人员在岗离岗报警信息(这里只有直播间的在岗离岗报警) */
  293. void FuncOnAndOffWork::processRoomOnWorkAlarm()
  294. {
  295. /* 先获取所有的直播间,这里不需要读取所有的房间数据
  296. * 理论上一个频率只有一个直播间 */
  297. std::map<int, RoomCamActInfo> mapRoomCamActInfo;
  298. for(auto& it : m_funcThreadInfo.listRoomCamActInfo)
  299. {
  300. /* 判断是不是直播间 */
  301. if(it.RoomType == Enum_RoomType::ROOM_LIVE)
  302. {
  303. mapRoomCamActInfo.insert(std::make_pair(it.RoomID, it));
  304. }
  305. }
  306. /* 获取当前频率的在岗离岗信息,在表格“tChannel”中 */
  307. if(!getCurrentFrequencyInfo(m_funcThreadInfo.ChannelID, m_nowChnDetecRule))
  308. {
  309. SPDLOG_LOGGER_ERROR(m_logger, "{} 获取在岗离岗信息失败", m_baseLog);
  310. return;
  311. }
  312. /* 获取当前时间 */
  313. m_nowTime = QDateTime::currentDateTime();
  314. /* 创建本轮的临时变量 */
  315. m_nowRoomOnWorkInfo.clear();
  316. /* 对所有房间挨个判断 */
  317. for(auto& roomInfo : mapRoomCamActInfo)
  318. {
  319. bool bHasPeople = false;
  320. bool bOnWork = false;
  321. m_strBaseInfo = fmt::format("频道[{}][{}], 房间[{}][{}]:", m_funcThreadInfo.ChannelID, m_funcThreadInfo.strChannelName, roomInfo.second.RoomID, roomInfo.second.strRoomName);
  322. /* 先使用人员计数判断直播间有没有人,如果直播间有人,再使用人脸识别算法判断是否是认识的人(是认识的人才会被识别到) */
  323. for(auto& alarm : m_listSrcAlarm.listAlarmInfo)
  324. {
  325. if(alarm->RoomID == roomInfo.first)
  326. {
  327. if(alarm->ActionID == GVariable.ActPersonCount())
  328. {
  329. if(alarm->listBbox.size() > 0)
  330. {
  331. bHasPeople = true;
  332. break;
  333. }
  334. }
  335. }
  336. }
  337. /* 如果有人,记录可以识别到的人员信息,记录在岗时间 */
  338. if(bHasPeople)
  339. {
  340. for(auto& alarm : m_listSrcAlarm.listAlarmInfo)
  341. {
  342. if(alarm->RoomID == roomInfo.first)
  343. {
  344. if(alarm->ActionID == GVariable.ActFaceIdentify())
  345. {
  346. if(alarm->listPersonInfo.size() > 0)
  347. {
  348. bOnWork = true;
  349. /* 记录人员信息 */
  350. m_nowRoomOnWorkInfo.addPersonInfo(alarm->listPersonInfo);
  351. /* 更新在岗时间 */
  352. m_nowRoomOnWorkInfo.updateOnWorkTime(m_nowTime);
  353. }
  354. }
  355. }
  356. }
  357. }
  358. /* 查找这个房间对应的房间信息 */
  359. RoomOnWorkInfo* pRoomOnWorkInfo = nullptr;
  360. auto it0 = m_mapRoomOnWorkInfo.find(roomInfo.first);
  361. if(it0 != m_mapRoomOnWorkInfo.end())
  362. {
  363. pRoomOnWorkInfo = it0->second;
  364. }
  365. else
  366. {
  367. /* 没找到,就创建一个 */
  368. pRoomOnWorkInfo = new RoomOnWorkInfo();
  369. pRoomOnWorkInfo->bOnWork = bOnWork;
  370. pRoomOnWorkInfo->ChannelID = m_funcThreadInfo.ChannelID;
  371. pRoomOnWorkInfo->RoomID = roomInfo.first;
  372. pRoomOnWorkInfo->RoomID = roomInfo.second.RoomID;
  373. pRoomOnWorkInfo->RoomType = roomInfo.second.RoomType;
  374. pRoomOnWorkInfo->StartTime = m_nowTime;
  375. pRoomOnWorkInfo->strImagePath = m_nowRoomOnWorkInfo.strImagePath;
  376. pRoomOnWorkInfo->listPersonInfo = m_nowRoomOnWorkInfo.listPersonInfo;
  377. m_mapRoomOnWorkInfo.insert(std::make_pair(roomInfo.first, pRoomOnWorkInfo));
  378. }
  379. if(pRoomOnWorkInfo == nullptr)
  380. {
  381. SPDLOG_LOGGER_ERROR(m_logger, "{} 创建房间在岗信息失败, 房间: {}", m_baseLog, roomInfo.second.strRoomName);
  382. continue;
  383. }
  384. /* 处理此次在岗和离岗 */
  385. if(bOnWork)
  386. {
  387. /* 在岗 */
  388. onWorkProcess(pRoomOnWorkInfo, roomInfo);
  389. } else
  390. {
  391. /* 离岗 */
  392. offWorkProcess(pRoomOnWorkInfo, roomInfo);
  393. }
  394. }
  395. }
  396. /* 获取当前频率信息 */
  397. bool FuncOnAndOffWork::getCurrentFrequencyInfo(const int ChannelID, DecCondition& info)
  398. {
  399. auto str = m_fromWebAPI->getChannelInfo(ChannelID);
  400. if(str == "")
  401. {
  402. SPDLOG_LOGGER_ERROR(m_logger, "获取通道信息失败, ChannelID:{}", ChannelID);
  403. return false;
  404. }
  405. /* 解析数据 */
  406. try
  407. {
  408. nJson json1 = nJson::parse(str);
  409. info.ChannelID = ChannelID;
  410. info.strChannelName = json1["chnName"].is_null() ? "" : json1["chnName"].get<std::string>();
  411. info.leaveTimeMaxNum = json1["leaveNum"].is_null() ? 0 : json1["leaveNum"].get<int>();
  412. info.leaveNumOnTime = json1["leaveNumTime"].is_null() ? 0 : json1["leaveNumTime"].get<int>();
  413. info.leaveOneTime = json1["leaveLongTime"].is_null() ? 0 : json1["leaveLongTime"].get<int>();
  414. }
  415. catch (const nJson::parse_error& e) {
  416. SPDLOG_LOGGER_ERROR(m_logger,"解析 tChannel数据失败 数据失败:{}, 错误ID:{}",e.what(), e.id);
  417. return false;
  418. }
  419. catch (const nJson::type_error& e) {
  420. SPDLOG_LOGGER_ERROR(m_logger,"解析 tChannel数据失败 数据失败:{}, 错误ID:{}",e.what(), e.id);
  421. return false;
  422. }
  423. catch(...) {
  424. SPDLOG_LOGGER_ERROR(m_logger,"解析 tChannel数据失败 数据失败");
  425. return false;
  426. }
  427. return true;
  428. }
  429. /**
  430. * @brief 人员在岗情况处理
  431. 1、判断上次是在岗还是离岗
  432. (1) 上次是离岗,结束离岗报警,需要判断是否已经写入数据库
  433. 1) 已经写入数据库,更新数据库报警记录,更新结束时间,清空内存中的报警记录,主要是清空PKID,置为0
  434. 2) 还未写入数据库,打印个日志,不需要做什么操作
  435. (2) 上次是在岗,更新数据,主要是更新图片
  436. 1) 如果上次在岗记录已经写入数据库,判断人脸列表是否相等
  437. 1) 不相等,更新数据库
  438. 2) 相等,更新在岗时间
  439. 2) 如果上次在岗记录还未写入数据库,直接写入数据库
  440. * @param pRoomOnWorkInfo 这个是内存中的记录,是历史记录
  441. * @param roomInfo
  442. */
  443. void FuncOnAndOffWork::onWorkProcess(RoomOnWorkInfo* pRoomOnWorkInfo, std::pair<const int, RoomCamActInfo>& roomInfo)
  444. {
  445. /* 判断上次是否在岗,上次是离岗,结束离岗报警 */
  446. if(pRoomOnWorkInfo->bOnWork == false)
  447. {
  448. SPDLOG_LOGGER_DEBUG(m_logger, "{} 上次是离岗", m_strBaseInfo);
  449. /* 判断是否已经写入数据库,已经写入就结束报警 */
  450. if(pRoomOnWorkInfo->PKID > 0)
  451. {
  452. SPDLOG_LOGGER_INFO(m_logger, "·人员报警结束,{}", m_strBaseInfo);
  453. /* 结束报警,写入数据库 */
  454. m_fromWebAPI->endAlarmInfoByPKID(pRoomOnWorkInfo->PKID, m_nowTime);
  455. /* 删除离岗记录,重新开始 */
  456. pRoomOnWorkInfo->PKID = 0;
  457. pRoomOnWorkInfo->listLeaveTime.clear();
  458. pRoomOnWorkInfo->StartTime = m_nowTime;
  459. }else
  460. {
  461. /* 没有写入数据库 */
  462. SPDLOG_LOGGER_INFO(m_logger, "·{}人员离岗报警结束,结束时间:{}", m_strBaseInfo, m_nowTime.toString("yyyy-MM-dd hh:mm:ss").toStdString());
  463. }
  464. }
  465. pRoomOnWorkInfo->bOnWork = true;
  466. /* 记录在岗最后一次的状态,主要是更新图片 */
  467. if(m_nowRoomOnWorkInfo.strImagePath != "")
  468. {
  469. pRoomOnWorkInfo->strImagePath = m_nowRoomOnWorkInfo.strImagePath;
  470. }
  471. /* 在岗记录还未写入数据库,就写入数据库 */
  472. if(0 == pRoomOnWorkInfo->PKID)
  473. {
  474. SPDLOG_LOGGER_DEBUG(m_logger, "{}在岗人脸:{},{}", m_strBaseInfo, m_nowRoomOnWorkInfo.getFaceNameListString(), m_nowRoomOnWorkInfo.getLeaveTimeString());
  475. /* 删除离岗人员和非法人员,未知人员不判断为离岗 */
  476. deleteNoWorkPerson(m_nowRoomOnWorkInfo.listPersonInfo);
  477. SPDLOG_LOGGER_DEBUG(m_logger, "{}删除离岗后的人员信息:{}", m_strBaseInfo, m_nowRoomOnWorkInfo.getFaceNameListString());
  478. /* 写入数据库 */
  479. if(m_nowRoomOnWorkInfo.listPersonInfo.size() > 0)
  480. {
  481. pRoomOnWorkInfo->listPersonInfo.assign(m_nowRoomOnWorkInfo.listPersonInfo.begin(), m_nowRoomOnWorkInfo.listPersonInfo.end());
  482. AlarmInfo alarmInfo;
  483. alarmInfo.appFunction = m_funcThreadInfo.appFunction;
  484. alarmInfo.ChannelID = m_funcThreadInfo.ChannelID;
  485. alarmInfo.RoomID = pRoomOnWorkInfo->RoomID;
  486. alarmInfo.DeviceID = 0;
  487. if(roomInfo.second.mapCameraAction.size() == 1)
  488. {
  489. alarmInfo.DeviceID = roomInfo.second.mapCameraAction.begin()->first;
  490. }
  491. alarmInfo.listBbox = getCameraNameList(roomInfo.second.mapCameraAction); /* 直播间摄像机列表 */
  492. alarmInfo.ImageInfo = pRoomOnWorkInfo->strImagePath;
  493. alarmInfo.EventTime = pRoomOnWorkInfo->StartTime;
  494. alarmInfo.ActionID = "";
  495. alarmInfo.Is_OnWork = true;
  496. alarmInfo.listPersonInfo = pRoomOnWorkInfo->listPersonInfo;
  497. alarmInfo.ActionDes = fmt::format("人员在岗[{}][{}]", alarmInfo.getFaceIDListString(), alarmInfo.getFaceNameListString());
  498. int PKID = 0;
  499. m_fromWebAPI->insertAlarmInfo(alarmInfo, PKID);
  500. if(PKID > 0)
  501. {
  502. pRoomOnWorkInfo->PKID = PKID;
  503. }
  504. }
  505. } else
  506. {
  507. /* 在岗记录已经写入数据库,对比人脸列表是否相等 */
  508. if(m_nowRoomOnWorkInfo.getFaceIDListString() != pRoomOnWorkInfo->getFaceIDListString())
  509. {
  510. if(m_nowRoomOnWorkInfo.listPersonInfo.size() > 0)
  511. {
  512. /* 合并在岗人员 */
  513. m_nowRoomOnWorkInfo.addPersonInfo(pRoomOnWorkInfo->listPersonInfo);
  514. SPDLOG_LOGGER_DEBUG(m_logger, "合并在岗人脸信息前: {}; {}", m_nowRoomOnWorkInfo.getFaceNameListString(), m_nowRoomOnWorkInfo.getLastOnWorkTimeString());
  515. /* 删除离岗人员和非法人员 */
  516. deleteNoWorkPerson(m_nowRoomOnWorkInfo.listPersonInfo);
  517. SPDLOG_LOGGER_DEBUG(m_logger, "删除离岗人员信息后: {}", m_nowRoomOnWorkInfo.getFaceNameListString());
  518. /* 更新在岗信息 */
  519. if(m_nowRoomOnWorkInfo.listPersonInfo.size() > 0)
  520. {
  521. pRoomOnWorkInfo->listPersonInfo.assign(m_nowRoomOnWorkInfo.listPersonInfo.begin(), m_nowRoomOnWorkInfo.listPersonInfo.end());
  522. /* 更新到数据库 */
  523. if(m_fromWebAPI->updatePersonInfo(*pRoomOnWorkInfo))
  524. {
  525. SPDLOG_LOGGER_INFO(m_logger, "·更新在岗人员信息成功,{}{}{}", m_strBaseInfo, pRoomOnWorkInfo->getFaceIDListString(), pRoomOnWorkInfo->getFaceNameListString());
  526. }else {
  527. SPDLOG_LOGGER_ERROR(m_logger, "·更新在岗人员信息失败,{}", m_strBaseInfo);
  528. }
  529. }
  530. }
  531. if(m_nowRoomOnWorkInfo.listPersonInfo.size() <= 0)
  532. {
  533. SPDLOG_LOGGER_DEBUG(m_logger, "没有在岗人员信息,不更新在岗人员");
  534. }
  535. }else
  536. {
  537. /* 人员信息没有变化,更新在岗时间 */
  538. pRoomOnWorkInfo->updateOnWorkTime(m_nowTime);
  539. SPDLOG_LOGGER_DEBUG(m_logger, "人员信息没有变化,更新在岗时间: {}", m_nowRoomOnWorkInfo.getLastOnWorkTimeString());
  540. }
  541. }
  542. }
  543. /**
  544. * @brief 人员离岗情况处理
  545. 1、判断上次是在岗还是离岗
  546. (1) 上次是在岗,开启离岗报警,然后判断是否已经写入数据库,更新离岗时间
  547. 1) 已经写入数据库,更新在岗结束,离岗开始
  548. 2) 还未写入数据库,打印个日志,不需要做什么操作
  549. (2) 上次是离岗,不需要做啥
  550. 2、更新在岗离岗状态,主要是更新图片
  551. 3、判断有没有写入数据库
  552. * @param pRoomOnWorkInfo
  553. * @param roomInfo
  554. */
  555. void FuncOnAndOffWork::offWorkProcess(RoomOnWorkInfo* pRoomOnWorkInfo, std::pair<const int, RoomCamActInfo>& roomInfo)
  556. {
  557. if(true == pRoomOnWorkInfo->bOnWork)
  558. {
  559. /* 上次是在岗 */
  560. SPDLOG_LOGGER_DEBUG(m_logger, "{} 上次是在岗", m_strBaseInfo);
  561. pRoomOnWorkInfo->bOnWork = false;
  562. /* 判断有没有写入数据库 */
  563. if(pRoomOnWorkInfo->PKID > 0)
  564. {
  565. SPDLOG_LOGGER_INFO(m_logger, "人员在岗结束(PKID:{}),离岗开始,{}", pRoomOnWorkInfo->PKID, m_strBaseInfo);
  566. /* 更新数据库,结束在岗 */
  567. m_fromWebAPI->endAlarmInfoByPKID(pRoomOnWorkInfo->PKID, m_nowTime);
  568. pRoomOnWorkInfo->PKID = 0;
  569. pRoomOnWorkInfo->StartTime = m_nowTime;
  570. }else
  571. {
  572. /* 没有写入数据库 */
  573. SPDLOG_LOGGER_INFO(m_logger, "人员在岗结束,离岗开始,{}", m_strBaseInfo);
  574. }
  575. pRoomOnWorkInfo->addOneLeaveTime(m_nowTime, m_nowChnDetecRule.leaveTimeMaxNum);
  576. auto strLeaveTime = pRoomOnWorkInfo->getLeaveTimeString();
  577. if(strLeaveTime != "")
  578. {
  579. SPDLOG_LOGGER_DEBUG(m_logger, "★ 人员最近离岗时间, {}, {}", m_strBaseInfo, strLeaveTime);
  580. }
  581. } else
  582. {
  583. /* 上次是离岗 */
  584. SPDLOG_LOGGER_DEBUG(m_logger, "{} 没有人员,可能离岗了", m_strBaseInfo);
  585. }
  586. if(!m_nowRoomOnWorkInfo.strImagePath.empty())
  587. {
  588. pRoomOnWorkInfo->strImagePath = m_nowRoomOnWorkInfo.strImagePath;
  589. }
  590. /* 判断有没有写入数据库 */
  591. if(pRoomOnWorkInfo->PKID == 0)
  592. {
  593. /* 没有写入数据库,现在写入 */
  594. /* 判断单次离岗超时,计算时间差,leaveOneTime设置为0表示不启用检测 */
  595. int nLeaveTime = pRoomOnWorkInfo->StartTime.secsTo(m_nowTime);
  596. if(nLeaveTime > m_nowChnDetecRule.leaveOneTime && m_nowChnDetecRule.leaveOneTime > 0)
  597. {
  598. AlarmInfo alarmInfo;
  599. alarmInfo.appFunction = m_funcThreadInfo.appFunction;
  600. alarmInfo.ChannelID = m_funcThreadInfo.ChannelID;
  601. alarmInfo.RoomID = pRoomOnWorkInfo->RoomID;
  602. alarmInfo.DeviceID = 0;
  603. if(roomInfo.second.mapCameraAction.size() == 1)
  604. {
  605. alarmInfo.DeviceID = roomInfo.second.mapCameraAction.begin()->first;
  606. }
  607. alarmInfo.listBbox = getCameraNameList(roomInfo.second.mapCameraAction); /* 直播间摄像机列表 */
  608. alarmInfo.ImageInfo = pRoomOnWorkInfo->strImagePath;
  609. alarmInfo.EventTime = pRoomOnWorkInfo->StartTime;
  610. alarmInfo.ActionID = "";
  611. alarmInfo.Is_OnWork = false;
  612. alarmInfo.listPersonInfo = pRoomOnWorkInfo->listPersonInfo;
  613. if(alarmInfo.listPersonInfo.size() > 0)
  614. {
  615. alarmInfo.ActionDes = fmt::format("离岗人员[{}][{}],人员单次离岗时间超过{}秒", alarmInfo.getFaceIDListString(), alarmInfo.getFaceNameListString(), m_nowChnDetecRule.leaveOneTime);
  616. }else {
  617. alarmInfo.ActionDes = fmt::format("人员单次离岗时间超过{}秒", m_nowChnDetecRule.leaveOneTime);
  618. }
  619. /* 写入数据库 */
  620. m_fromWebAPI->insertAlarmInfo(alarmInfo, pRoomOnWorkInfo->PKID);
  621. if(pRoomOnWorkInfo->PKID > 0)
  622. {
  623. SPDLOG_LOGGER_INFO(m_logger, "写入离岗报警成功,{}{}", m_strBaseInfo, pRoomOnWorkInfo->getFaceNameListString());
  624. }else {
  625. SPDLOG_LOGGER_ERROR(m_logger, "写入离岗报警失败,{}", m_strBaseInfo);
  626. }
  627. pRoomOnWorkInfo->listLeaveTime.clear();
  628. /* 获取当前频率,应用的报警信息的最大PKID */
  629. int maxPKID = 0;
  630. m_fromWebAPI->getMaxAlarmPKID(m_funcThreadInfo.ChannelID, m_funcThreadInfo.appFunction, maxPKID);
  631. if(maxPKID > 0)
  632. {
  633. std::string actionDes = "人员离岗了清空在岗人员信息,方便客户端显示";
  634. /* 不知道这里为啥要单独获取PKID,按理说用上面返回的即可 */
  635. if(m_fromWebAPI->clearOnWorkAlarmInfo(maxPKID, true, actionDes))
  636. {
  637. SPDLOG_LOGGER_INFO(m_logger, "☆ {} 人员离岗了清空在岗人员信息,方便客户端显示", m_strBaseInfo);
  638. } else {
  639. SPDLOG_LOGGER_ERROR(m_logger, "× {} 人员离岗了清空在岗人员信息失败", m_strBaseInfo);
  640. }
  641. }
  642. }
  643. /* 判断多次频繁离岗 */
  644. if((pRoomOnWorkInfo->listLeaveTime.size() >= m_nowChnDetecRule.leaveNumOnTime) &&
  645. (m_nowChnDetecRule.leaveNumOnTime > 0) && (m_nowChnDetecRule.leaveTimeMaxNum > 0))
  646. {
  647. /* 计算距离第一次离岗时间长度 */
  648. int nLeaveTime = pRoomOnWorkInfo->listLeaveTime.front().secsTo(m_nowTime);
  649. if(nLeaveTime <= m_nowChnDetecRule.leaveOneTime)
  650. {
  651. AlarmInfo alarmInfo;
  652. alarmInfo.appFunction = m_funcThreadInfo.appFunction;
  653. alarmInfo.ChannelID = m_funcThreadInfo.ChannelID;
  654. alarmInfo.RoomID = pRoomOnWorkInfo->RoomID;
  655. alarmInfo.DeviceID = 0;
  656. if(roomInfo.second.mapCameraAction.size() == 1)
  657. {
  658. alarmInfo.DeviceID = roomInfo.second.mapCameraAction.begin()->first;
  659. }
  660. alarmInfo.listBbox = getCameraNameList(roomInfo.second.mapCameraAction); /* 直播间摄像机列表 */
  661. alarmInfo.ImageInfo = pRoomOnWorkInfo->strImagePath;
  662. alarmInfo.EventTime = pRoomOnWorkInfo->listLeaveTime.front();
  663. alarmInfo.ActionID = "";
  664. alarmInfo.Is_OnWork = false;
  665. alarmInfo.listPersonInfo = pRoomOnWorkInfo->listPersonInfo;
  666. if(alarmInfo.listPersonInfo.size() > 0)
  667. {
  668. alarmInfo.ActionDes = fmt::format("离岗人员[{}],人员离岗([]秒内发生{}次离岗)",
  669. alarmInfo.getFaceNameListString(), m_nowChnDetecRule.leaveOneTime, m_nowChnDetecRule.leaveNumOnTime);
  670. }else {
  671. alarmInfo.ActionDes = fmt::format("人员离岗({}秒内发生{}次离岗)", m_nowChnDetecRule.leaveOneTime, m_nowChnDetecRule.leaveNumOnTime);
  672. }
  673. /* 写入数据库报警表 */
  674. m_fromWebAPI->insertAlarmInfo(alarmInfo, pRoomOnWorkInfo->PKID);
  675. if(pRoomOnWorkInfo->PKID > 0)
  676. {
  677. SPDLOG_LOGGER_INFO(m_logger, "写入离岗报警成功,{}{}", m_strBaseInfo, pRoomOnWorkInfo->getFaceNameListString());
  678. }else {
  679. SPDLOG_LOGGER_ERROR(m_logger, "写入离岗报警失败,{}", m_strBaseInfo);
  680. }
  681. pRoomOnWorkInfo->listLeaveTime.clear();
  682. /* 获取当前频率,应用的报警信息的最大PKID */
  683. int maxPKID = 0;
  684. m_fromWebAPI->getMaxAlarmPKID(m_funcThreadInfo.ChannelID, m_funcThreadInfo.appFunction, maxPKID);
  685. if(maxPKID > 0)
  686. {
  687. std::string actionDes = "人员离岗了清空在岗人员信息,方便客户端显示";
  688. /* 不知道这里为啥要单独获取PKID,按理说用上面返回的即可 */
  689. if(m_fromWebAPI->clearOnWorkAlarmInfo(maxPKID, true, actionDes))
  690. {
  691. SPDLOG_LOGGER_INFO(m_logger, "☆ {} 人员离岗了清空在岗人员信息,方便客户端显示", m_strBaseInfo);
  692. } else {
  693. SPDLOG_LOGGER_ERROR(m_logger, "× {} 人员离岗了清空在岗人员信息失败", m_strBaseInfo);
  694. }
  695. }
  696. }
  697. }
  698. }
  699. }
  700. /* 删除离岗人员和未知人员信息 */
  701. void FuncOnAndOffWork::deleteNoWorkPerson(std::list<PersonInfo>& listPersonInfo)
  702. {
  703. /* 删除离岗人员 */
  704. for(auto it = listPersonInfo.begin(); it != listPersonInfo.end();)
  705. {
  706. if((it->lastOnWork < m_nowTime) || it->PersonID == "-1" || it->PersonID == "-2")
  707. {
  708. it = listPersonInfo.erase(it);
  709. } else
  710. {
  711. ++it;
  712. }
  713. }
  714. }
  715. /* 获取当前直播间的摄像机名称列表 */
  716. std::list<std::string> FuncOnAndOffWork::getCameraNameList(const std::map<int, std::list<std::string>>& mapCameraAction)
  717. {
  718. std::list<std::string> listCameraName;
  719. for(const auto& it : mapCameraAction)
  720. {
  721. listCameraName.push_back(m_funcThreadInfo.getCameraName(it.first));
  722. }
  723. return listCameraName;
  724. }
  725. /* 创建报警记录 */
  726. // void FuncOnAndOffWork::createAlarmInfo(RoomOnWorkInfo* pRoomOnWorkInfo, std::pair<const int, RoomCamActInfo>& roomInfo, std::string actionDes, AlarmInfo& alarmInfo)
  727. // {
  728. // if(pRoomOnWorkInfo == nullptr)
  729. // {
  730. // return;
  731. // }
  732. // alarmInfo.appFunction = m_funcThreadInfo.appFunction;
  733. // alarmInfo.ChannelID = m_funcThreadInfo.ChannelID;
  734. // alarmInfo.RoomID = pRoomOnWorkInfo->RoomID;
  735. // alarmInfo.DeviceID = 0;
  736. // if(roomInfo.second.mapCameraAction.size() == 1)
  737. // {
  738. // alarmInfo.DeviceID = roomInfo.second.mapCameraAction.begin()->first;
  739. // }
  740. // alarmInfo.listBbox = getCameraNameList(roomInfo.second.mapCameraAction); /* 直播间摄像机列表 */
  741. // alarmInfo.ImageInfo = pRoomOnWorkInfo->strImagePath;
  742. // alarmInfo.EventTime = pRoomOnWorkInfo->StartTime.toString("yyyy-MM-dd hh:mm:ss").toStdString();
  743. // alarmInfo.ActionID = "";
  744. // alarmInfo.Is_OnWork = false;
  745. // alarmInfo.listPersonInfo = pRoomOnWorkInfo->listPersonInfo;
  746. // alarmInfo.ActionDes = actionDes;
  747. // }
  748. /* 读取Redis数据 */
  749. // void FuncOnAndOffWork::readRedisData()
  750. // {
  751. // m_listSrcAlarm.clearAlarmInfo();
  752. // /* 读取Redis数据 */
  753. // for(const auto& roomInfo : m_funcThreadInfo.listRoomCamActInfo)
  754. // {
  755. // for(const auto& cam : roomInfo.mapCameraAction)
  756. // {
  757. // for(const auto act : cam.second)
  758. // {
  759. // /* 读取Redis数据 */
  760. // std::string strKey = std::to_string(cam.first) + ":" + act;
  761. // std::string strRetValue;
  762. // // SPDLOG_LOGGER_DEBUG(m_logger, "读取Redis数据, Key:{}", strKey);
  763. // if(!m_fromRedis->getRedisString(strKey, strRetValue))
  764. // {
  765. // // SPDLOG_LOGGER_ERROR(m_logger, "读取Redis数据失败, Key:{}", strKey);
  766. // std::this_thread::sleep_for(std::chrono::milliseconds(10));
  767. // continue;
  768. // }
  769. // /* 解析数据,先设置基础数据 */
  770. // AlarmInfo newAlarmInfo;
  771. // newAlarmInfo.ChannelID = m_funcThreadInfo.ChannelID;
  772. // newAlarmInfo.appFunction = m_funcThreadInfo.appFunction;
  773. // newAlarmInfo.RoomID = roomInfo.RoomID;
  774. // newAlarmInfo.DeviceID = cam.first;
  775. // newAlarmInfo.ActionID = act;
  776. // parseRedisBaseData(strRetValue, newAlarmInfo);
  777. // parseRedisBBoxesData(strRetValue, newAlarmInfo);
  778. // /* 判断事件的时效性,超过多少秒不更新就可能是超脑挂了 */
  779. // if(!isEventTimeVaild(newAlarmInfo.EventTime))
  780. // {
  781. // /* 事件时间超过600秒,可能是超脑挂了 */
  782. // SPDLOG_LOGGER_WARN(m_logger, "Redis Key:{} 数据长时间没有更新,EventTime:{}",strKey, newAlarmInfo.EventTime.toString("yyyy-MM-dd hh:mm:ss").toStdString());
  783. // std::this_thread::sleep_for(std::chrono::milliseconds(100));
  784. // continue;
  785. // }
  786. // m_listSrcAlarm.addAlarmInfo(newAlarmInfo);
  787. // SPDLOG_LOGGER_DEBUG(m_logger, "报警数据: {}, 房间ID: {}, 报警时间: {}, bBox数目: {}, Person数目: {}",
  788. // strKey, roomInfo.RoomID, newAlarmInfo.EventTime.toString("yyyy-MM-dd hh:mm:ss").toStdString(),
  789. // newAlarmInfo.listBbox.size(), newAlarmInfo.listPersonInfo.size());
  790. // }
  791. // }
  792. // }
  793. // }