#include "FuncOrdinary.h" #include "GlobalVariable.h" #include "FromRedis.h" #include "ToEQMDataBase.h" #include "UniversalFunc.h" #include "GlobalConfig.h" FuncOrdinary::FuncOrdinary() { m_logger = spdlog::get("SPAServer"); if(m_logger == nullptr) { SPDLOG_LOGGER_ERROR(m_logger, "SPAServer logger is nullptr"); return; } /* 给变量分配内存 */ } FuncOrdinary::~FuncOrdinary() { } /* 任务线程 */ void FuncOrdinary::thread_task() { if(m_funcAct.appFunction == AppFunction::APP_NONE) { SPDLOG_LOGGER_ERROR(m_logger, "未设置线程功能信息"); return; } SPDLOG_LOGGER_INFO(m_logger, "开启 {} 功能线程, ChannelID:{}",m_funcAct.strFunctionName, m_funcAct.ChannelID); /* 设置线程运行状态 */ m_bRunning = true; while (GThreadInfo.getRunning()) { /* 从全局的信息块中更新功能信息,如是否需要退出线程,主要是更新摄像机关联的算法信息 */ GThreadInfo.updateFuncInfo(m_funcAct); /* 如果线程的运行状态为停止 */ if(m_funcAct.RunState == RunTimeState::RUN_STATE_STOP) { /* 退出线程 */ break; } /* 读取Redis信息,处理数据,第一层循环是根据房间读取,第二个循环是根据房间内的摄像机读取 */ for(const auto& roomInfo : m_funcAct.listRoomCamActInfo) { for(const auto& it : roomInfo.mapCameraAction) { /* 读取Redis数据 */ std::string strKey = std::to_string(it.first) + ":" + it.second; std::string strRetValue; if(!m_fromRedis->getRedisString(strKey, strRetValue)) { SPDLOG_LOGGER_ERROR(m_logger, "读取Redis数据失败, Key:{}", strKey); std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } /* 解析数据,先设置基础数据 */ AlarmInfo newAlarmInfo; newAlarmInfo.ActionID = it.second; newAlarmInfo.appFunction = m_funcAct.appFunction; parseRedisBaseData(strRetValue, newAlarmInfo); parseRedisBBoxesData(strRetValue, newAlarmInfo); /* 判断事件的时效性,超过多少秒不更新就可能是超脑挂了 */ if(isEventTimeVaild(newAlarmInfo.EventTime)) { SPDLOG_LOGGER_WARN(m_logger, "Redis Key:{} 数据长时间没有更新,EventTime:{}",strKey, newAlarmInfo.EventTime); std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } /* ------------------------------------------------------------------------------- * 判断有无报警记录,新的报警就先创建一个报警信息,写入报警列表,超过报警的最小误报时间后, * 再写入到EQM的tAlarmInfo表中,正在报警的记录就判断和更新,结束报警就更新tAlarmInfo的 * 报警数据的结束时间,并清空pAlarmInfo * ------------------------------------------------------------------------------- */ /* 找出数组中与当前报警ID相同的的报警信息 */ auto pAlarmInfo = m_listAlarm.findAlarmInfo(newAlarmInfo); /* 没有找到报警记录,就新建一个 */ if(pAlarmInfo == nullptr) { /* 新记录有报警,新建报警记录 */ if(newAlarmInfo.Is_Alarm) { /* 图片不能是空,如果是空的,就不写入数据库 */ if(!newAlarmInfo.ImageInfo.empty()) { /* 违禁品检测,开始时间是事件时间 */ newAlarmInfo.StartTime = newAlarmInfo.EventTime; newAlarmInfo.EndTime = ""; /* 保存新的报警记录 */ newAlarmInfo.Is_InsertEQM = false; m_listAlarm.addAlarmInfo(newAlarmInfo); }else { SPDLOG_LOGGER_WARN(m_logger, "频道:{}, 房间:{}, 摄像机:{}, 算法:{}, 有报警区域, 但是没有图片信息", m_funcAct.ChannelID, roomInfo.RoomID, it.first, it.second); } } } /* 已有报警记录 */ else { /* 更新图片信息 */ if(!newAlarmInfo.ImageInfo.empty()) { pAlarmInfo->ImageInfo = newAlarmInfo.ImageInfo; } /* 判断是否还在报警中 */ if(newAlarmInfo.Is_Alarm) { /* 正在报警中,检查是否已经写入到数据库中 */ if(pAlarmInfo->Is_InsertEQM) { /* 已经写入到数据库中,不做处理 */ }else { /* 判断报警时长间隔是否足够 */ if(!isAlarmTimeVaild(*pAlarmInfo)) { /* 不够报警时间,直接进行下一个循环 */ continue; } /* 写入到数据库 */ pAlarmInfo->Is_InsertEQM = false; if(m_toEQMDataBase->insertAlarmInfo(*pAlarmInfo)) { pAlarmInfo->Is_InsertEQM = true; }else { SPDLOG_LOGGER_ERROR(m_logger, "写入tAlarmInfo报警数据失败, 频率:{}, 房间:{}, 算法:{} ", m_funcAct.ChannelID, roomInfo.RoomID, it.second); } } } else { /* 报警结束,判断时长,如果小于设置的最小间隔,可能是误报警 * 更新数据库结束时间,结束时间是此时电脑时间 */ if(isAlarmTimeVaild(*pAlarmInfo)) { pAlarmInfo->EndTime = chronoToStrTime(std::chrono::system_clock::now()); /* 判断是否已经写入到数据库中 */ if(pAlarmInfo->Is_InsertEQM) { /* 已经写入到数据库中,更新结束时间 */ m_toEQMDataBase->updateAlarmEndTime(*pAlarmInfo); }else { /* 没有写入到数据库中,写入到数据库中 */ m_toEQMDataBase->insertAlarmInfo(*pAlarmInfo); } SPDLOG_LOGGER_INFO(m_logger, "频率:{}, 房间:{}, 摄像机:{}, 算法:{}, 报警结束", m_funcAct.ChannelID, roomInfo.RoomID, it.first, it.second); } /* 删除这个报警信息 */ m_listAlarm.deleteAlarmInfo(*pAlarmInfo); } } } } /* 休眠设置的时间 */ std::this_thread::sleep_for(std::chrono::milliseconds(GConfig.ThreadSleepMS)); } /* 设置线程退出的状态,设置成 RUN_STATE_EXITCOMPLET ,就会被管理线程回收 */ GThreadInfo.setThreadState(m_funcAct, RunTimeState::RUN_STATE_EXITCOMPLET); SPDLOG_LOGGER_INFO(m_logger, "{} 功能线程退出,Channel:{}", m_funcAct.strFunctionName, m_funcAct.ChannelID); m_bRunning = false; } /* 判断报警时长是否符合不同应用的最小间隔 */ bool FuncOrdinary::isAlarmTimeVaild(const AlarmInfo& info) { /* 违禁品物品 */ if(info.appFunction == AppFunction::APP_Contraband) { if(timeDiffWithNow(info.EventTime) < GConfig.AppContraband_Time) { return false; } } /* 玩手机识别 */ else if(info.appFunction == AppFunction::APP_PlayPhone) { if(timeDiffWithNow(info.EventTime) < GConfig.AppPlayPhone) { return false; } } /* 老鼠识别 */ else if(info.appFunction == AppFunction::APP_Mouse) { if(timeDiffWithNow(info.EventTime) < GConfig.AppMouse) { return false; } } /* 未戴口罩识别 */ else if(info.appFunction == AppFunction::APP_NoMask) { if(timeDiffWithNow(info.EventTime) < GConfig.AppMask) { return false; } } /* 疲劳检测 */ else if(info.appFunction == AppFunction::APP_Fatigue) { if(timeDiffWithNow(info.EventTime) < GConfig.AppTired_Time) { return false; } } /* 摔倒识别 */ else if(info.appFunction == AppFunction::APP_AllDown) { return true; } return true; }