#include "FuncOrdinary.h" #include "GlobalVariable.h" #include "FromRedis.h" #include "FromWebAPI.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::task() { /* 初始化WebAPI和Redis接口 */ if(!initDataInterface()) { return; } /* 保存上次获取时间段的时间 */ QDateTime lastTime = QDateTime::fromString("2025-05-07 00:00:00", "yyyy-MM-dd hh:mm:ss"); /* 创建临时的报警信息 */ std::list listAlarmTemp; while (GThreadInfo.getRunning()) { /* 休眠设置的时间 */ std::this_thread::sleep_for(std::chrono::milliseconds(GVariable.ThreadSleepMS)); /* 从全局的信息块中更新功能信息,如是否需要退出线程,主要是更新摄像机关联的算法信息 */ GThreadInfo.updateFuncInfo(m_funcThreadInfo); /* 如果线程的运行状态为停止 */ if(m_funcThreadInfo.RunState == RunTimeState::RUN_STATE_STOP) { /* 退出线程 */ return; } /* 判断是否在检测时间段内 */ if(!isInDetectTime(m_periodInfo)) { continue; } // SPDLOG_LOGGER_DEBUG(m_logger, "{} 在检测时间段内", m_baseLog); /* ========================= 读取Redis数据 ========================= */ listAlarmTemp.clear(); /* 读取Redis数据 */ for(const auto& roomInfo : m_funcThreadInfo.listRoomCamActInfo) { for(const auto& cam : roomInfo.mapCameraAction) { for(const auto act : cam.second) { /* 读取Redis数据 */ std::string strKey = std::to_string(cam.first) + ":" + act; std::string strRetValue; // SPDLOG_LOGGER_DEBUG(m_logger, "读取Redis数据, Key:{}", strKey); 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.ChannelID = m_funcThreadInfo.ChannelID; newAlarmInfo.appFunction = m_funcThreadInfo.appFunction; newAlarmInfo.RoomID = roomInfo.RoomID; newAlarmInfo.DeviceID = cam.first; newAlarmInfo.ActionID = act; parseRedisBaseData(strRetValue, newAlarmInfo); parseRedisBBoxesData(strRetValue, newAlarmInfo); /* 判断事件的时效性,超过多少秒不更新就可能是超脑挂了 */ if(!isEventTimeVaild(newAlarmInfo.EventTime)) { /* 事件时间超过600秒,可能是超脑挂了 */ SPDLOG_LOGGER_WARN(m_logger, "Redis Key:{} 数据长时间没有更新,EventTime:{}",strKey, newAlarmInfo.EventTime.toString("yyyy-MM-dd hh:mm:ss").toStdString()); std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } listAlarmTemp.push_back(newAlarmInfo); } } } SPDLOG_LOGGER_DEBUG(m_logger, "读取Redis数据成功, 报警数据数目: {}", m_listAlarm.listAlarmInfo.size()); // continue; /* ========================= 处理报警数据 ========================= */ for(auto newAlarm : listAlarmTemp) { /* 找到该报警相关的房间信息 */ auto roomInfo = m_funcThreadInfo.findRoomInfo(newAlarm.RoomID); if(roomInfo.RoomID == -1) { SPDLOG_LOGGER_ERROR(m_logger, "{} 房间ID: {} 不存在", m_baseLog, newAlarm.RoomID); continue; } /* 创建基础日志信息 */ std::string tmpBaseLog = fmt::format("{} 房间: {}, 摄像机ID: {}, 算法ID: {}", m_baseLog, roomInfo.strRoomName, newAlarm.DeviceID, newAlarm.ActionID); /* ------------------------------------------------------------------------------- * 判断有无报警记录,新的报警就先创建一个报警信息,写入报警列表,超过报警的最小误报时间后, * 再写入到EQM的tAlarmInfo表中,正在报警的记录就判断和更新,结束报警就更新tAlarmInfo的 * 报警数据的结束时间,并清空pAlarmInfo * ------------------------------------------------------------------------------- */ /* 找出数组中与当前报警ID相同的的报警信息 */ auto pAlarmInfo = m_listAlarm.findAlarmInfo(newAlarm); /* 没有找到报警记录,就新建一个 */ if(pAlarmInfo == nullptr) { /* 新记录有报警,新建报警记录 */ if(newAlarm.Is_Alarm) { /* 图片不能是空,如果是空的,就不写入数据库 */ if(!newAlarm.ImageInfo.empty()) { /* 违禁品检测,开始时间是事件时间 */ newAlarm.StartTime = newAlarm.EventTime; newAlarm.EndTime = GVariable.nullTime; /* 保存新的报警记录 */ newAlarm.Is_InsertEQM = false; m_listAlarm.addAlarmInfo(newAlarm); SPDLOG_LOGGER_DEBUG(m_logger, "{} 新的报警,加入到缓存中", tmpBaseLog); }else { SPDLOG_LOGGER_WARN(m_logger, "{}, 有报警区域, 但是没有图片信息", tmpBaseLog); } } } else { /* 已有报警记录 */ /* 更新图片信息 */ if(!newAlarm.ImageInfo.empty()) { pAlarmInfo->ImageInfo = newAlarm.ImageInfo; } /* 判断是否还在报警中 */ if(newAlarm.Is_Alarm) { /* 正在报警中,检查是否已经写入到数据库中 */ if(pAlarmInfo->Is_InsertEQM) { /* 已经写入到数据库中,不做处理 */ }else { /* 判断报警时长间隔是否足够 */ if(!isAlarmTimeVaild(*pAlarmInfo)) { /* 不够报警时间,直接进行下一个循环 */ continue; } /* 写入到数据库 */ pAlarmInfo->Is_InsertEQM = false; if(m_fromWebAPI->insertAlarmInfo(*pAlarmInfo)) { pAlarmInfo->Is_InsertEQM = true; }else { SPDLOG_LOGGER_ERROR(m_logger, "{} 写入tAlarmInfo报警数据失败! ", tmpBaseLog); } SPDLOG_LOGGER_INFO(m_logger, "{}, 报警开始,写入数据库", tmpBaseLog); } } else { /* 报警结束,判断时长,如果小于设置的最小间隔,可能是误报警 * 更新数据库结束时间,结束时间是此时电脑时间 */ if(isAlarmTimeVaild(*pAlarmInfo)) { pAlarmInfo->EndTime = m_nowTime; /* 判断是否已经写入到数据库中 */ if(pAlarmInfo->Is_InsertEQM) { /* 已经写入到数据库中,更新结束时间 */ m_fromWebAPI->updateAlarmEndTime(*pAlarmInfo); }else { /* 没有写入到数据库中,写入到数据库中 */ m_fromWebAPI->insertAlarmInfo(*pAlarmInfo); } SPDLOG_LOGGER_INFO(m_logger, "{}, 报警结束", tmpBaseLog); } /* 删除这个报警信息 */ m_listAlarm.deleteAlarmInfo(*pAlarmInfo); } } } } } /* old处理方法 */ void FuncOrdinary::oldTask() { // /* 读取Redis信息,处理数据,第一层循环是根据房间读取,第二个循环是根据房间内的摄像机读取 */ // for(const auto& roomInfo : m_funcThreadInfo.listRoomCamActInfo) // { // for(const auto& it : roomInfo.mapCameraAction) // { // for(const auto act : it.second) // { // /* 读取Redis数据 */ // std::string strKey = std::to_string(it.first) + ":" + act; // 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 = act; // newAlarmInfo.appFunction = m_funcThreadInfo.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_funcThreadInfo.ChannelID, roomInfo.RoomID, it.first, act); // } // } // } // /* 已有报警记录 */ // 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_fromWebAPI->insertAlarmInfo(*pAlarmInfo)) // { // pAlarmInfo->Is_InsertEQM = true; // }else { // SPDLOG_LOGGER_ERROR(m_logger, "写入tAlarmInfo报警数据失败, 频率:{}, 房间:{}, 算法:{} ", m_funcThreadInfo.ChannelID, roomInfo.RoomID, act); // } // } // } else // { // /* 报警结束,判断时长,如果小于设置的最小间隔,可能是误报警 // * 更新数据库结束时间,结束时间是此时电脑时间 */ // if(isAlarmTimeVaild(*pAlarmInfo)) // { // pAlarmInfo->EndTime = chronoToStrTime(std::chrono::system_clock::now()); // /* 判断是否已经写入到数据库中 */ // if(pAlarmInfo->Is_InsertEQM) // { // /* 已经写入到数据库中,更新结束时间 */ // m_fromWebAPI->updateAlarmEndTime(*pAlarmInfo); // }else { // /* 没有写入到数据库中,写入到数据库中 */ // m_fromWebAPI->insertAlarmInfo(*pAlarmInfo); // } // SPDLOG_LOGGER_INFO(m_logger, "频率:{}, 房间:{}, 摄像机:{}, 算法:{}, 报警结束", m_funcThreadInfo.ChannelID, roomInfo.RoomID, it.first, act); // } // /* 删除这个报警信息 */ // m_listAlarm.deleteAlarmInfo(*pAlarmInfo); // } // } // } // } // } } /* 判断报警时长是否符合不同应用的最小间隔 */ bool FuncOrdinary::isAlarmTimeVaild(const AlarmInfo& info) { int alarmTime = 0; /* 违禁品物品 */ if(info.appFunction == AppFunction::APP_Contraband) { alarmTime = GVariable.AppContraband_Time; } /* 玩手机识别 */ else if(info.appFunction == AppFunction::APP_PlayPhone) { alarmTime = GVariable.AppPlayPhone; } /* 老鼠识别 */ else if(info.appFunction == AppFunction::APP_Mouse) { alarmTime = GVariable.AppMouse; } /* 未戴口罩识别 */ else if(info.appFunction == AppFunction::APP_NoMask) { alarmTime = GVariable.AppMask; } /* 疲劳检测 */ else if(info.appFunction == AppFunction::APP_Fatigue) { alarmTime = GVariable.AppTired_Time; } /* 摔倒识别 */ else if(info.appFunction == AppFunction::APP_AllDown) { return true; } if(info.EventTime.secsTo(m_nowTime) < alarmTime) { return false; } return true; }