#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() { m_listSrcAlarm.clearAlarmInfo(); m_listAlarmBuffer.clearAlarmInfo(); } /* 任务线程 */ void FuncOrdinary::task() { /* 初始化WebAPI和Redis接口 */ if(!initDataInterface()) { return; } /* 保存上次获取时间段的时间 */ QDateTime lastTime = QDateTime::fromString("2025-05-07 00:00:00", "yyyy-MM-dd hh:mm:ss"); 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) { /* 退出线程 */ break; } /* 判断是否在检测时间段内 */ if(!isInDetectTime(m_periodInfo)) { continue; } // SPDLOG_LOGGER_DEBUG(m_logger, "{} 在检测时间段内", m_baseLog); /* ========================= 读取Redis数据 ========================= */ readRedisData(m_listSrcAlarm); // SPDLOG_LOGGER_DEBUG(m_logger, "读取Redis数据成功, 报警数据数目: {}", m_listAlarm.listAlarmInfo.size()); // continue; /* ========================= 处理报警数据 ========================= */ for(auto newAlarm : m_listSrcAlarm.listAlarmInfo) { /* 找到该报警相关的房间信息 */ 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_listAlarmBuffer.findAlarmInfo(*newAlarm); /* 没有找到报警记录,就新建一个 */ if(pAlarmInfo == nullptr) { /* 判断这条记录有没有报警,没有就不做任何处理 */ if(!newAlarm->Is_Alarm) { // SPDLOG_LOGGER_TRACE(m_logger, "{}, 没有报警,直接跳过", tmpBaseLog); continue; } /* 新记录有报警,新建报警记录 */ if(newAlarm->Is_Alarm) { /* 图片不能是空,如果是空的,就不写入数据库,现在是先加入缓存中 */ if(!newAlarm->ImageInfo.empty()) { /* 违禁品检测,开始时间是事件时间 */ newAlarm->StartTime = newAlarm->EventTime; newAlarm->EndTime = GVariable.nullTime; /* 保存新的报警记录 */ newAlarm->Is_InsertEQM = false; m_listAlarmBuffer.addAlarmInfo(*newAlarm); SPDLOG_LOGGER_INFO(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(!isOutAlarmTimeVaild(*pAlarmInfo)) { /* 不够报警时间,直接进行下一个循环 */ continue; } /* 写入到数据库 */ pAlarmInfo->Is_InsertEQM = false; int pkid = 0; if(m_fromWebAPI->insertAlarmInfo(*pAlarmInfo, pkid)) { pAlarmInfo->Is_InsertEQM = true; pAlarmInfo->PKID = pkid; }else { SPDLOG_LOGGER_ERROR(m_logger, "{} 写入tAlarmInfo报警数据失败! ", tmpBaseLog); } SPDLOG_LOGGER_INFO(m_logger, "{}, 报警开始,写入数据库", tmpBaseLog); } } else { /* 报警结束,先判断有无写入数据库,已经写入,直接结束报警 * 没有写入数据库,判断时长,如果小于设置的最小间隔,可能是误报警 * 更新数据库结束时间,结束时间是此时电脑时间 */ pAlarmInfo->EndTime = m_nowTime; /* 判断是否已经写入到数据库中 */ if(pAlarmInfo->Is_InsertEQM) { /* 已经写入到数据库中,更新结束时间 */ m_fromWebAPI->endAlarmInfoByPKID(pAlarmInfo->PKID, m_nowTime); }else { /* 判断报警时长间隔是否足够 */ if(isOutAlarmTimeVaild(*pAlarmInfo)) { /* 时长足够且没有写入到数据库中,写入到数据库中 */ m_fromWebAPI->insertAlarmInfo(*pAlarmInfo); } } SPDLOG_LOGGER_INFO(m_logger, "{} 报警结束", tmpBaseLog); /* 从缓存中删除这个报警信息 */ m_listAlarmBuffer.deleteAlarmInfo(*pAlarmInfo); } } } } m_listSrcAlarm.clearAlarmInfo(); m_listAlarmBuffer.clearAlarmInfo(); } /* 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::isOutAlarmTimeVaild(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; } /* 读取Redis数据 */ // void FuncOrdinary::readRedisData(std::list& listAlarmTemp) // { // 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); // printRawDataInfo(roomInfo, strKey, newAlarmInfo); // } // } // } // }