|
@@ -6,6 +6,7 @@
|
|
|
// #include "CurlFtp.h"
|
|
|
#include "ThreadPool/ThreadPool.h"
|
|
|
|
|
|
+#include <QApplication>
|
|
|
#include <QVector>
|
|
|
|
|
|
|
|
@@ -18,6 +19,16 @@ SPAServer::SPAServer()
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ /* 读取全局的配置文件 */
|
|
|
+ QString strConfigFile = QApplication::applicationDirPath() + "/config.ini";
|
|
|
+ if(!g_config.readConfig(strConfigFile))
|
|
|
+ {
|
|
|
+ /* 读取配置文件失败,直接退出程序 */
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ g_config.printValue();
|
|
|
+
|
|
|
+
|
|
|
m_threadRunning = true;
|
|
|
/* 初始化WebAPI */
|
|
|
m_toEQMDataBase.initWebApi("http://192.1.3.133:31000/v6/", "", "4c2f9fc91c22dd98331e47af2e2964f4");
|
|
@@ -46,7 +57,7 @@ void SPAServer::startServer()
|
|
|
info.vecAction.push_back("OD210_026_005246_001-IZRTKyEx");
|
|
|
m_keyContraband = "OD210_026_005246_001-IZRTKyEx";
|
|
|
// CPPTP.add_task(&SPAServer::threadFromRedis, this, info);
|
|
|
- threadFromRedis(info);
|
|
|
+ // threadFromRedis(info);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -393,7 +404,10 @@ void SPAServer::threadFromRedis(const CameraThreadInfo& info)
|
|
|
for(const auto& it : vecKey)
|
|
|
{
|
|
|
SPDLOG_LOGGER_INFO(m_logger, "读取Redis信息, Key: {}", it);
|
|
|
- fromRedis.getRedisString(it, strRetValue);
|
|
|
+ if( !fromRedis.getRedisString(it, strRetValue) )
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
SPDLOG_LOGGER_TRACE(m_logger, "Redis Value:\n{}", strRetValue);
|
|
|
/* 解析数据 */
|
|
|
AlarmInfo alarmInfo;
|
|
@@ -543,8 +557,174 @@ void SPAServer::parseRedisData(const std::string& strData, AlarmInfo& alarmInfo)
|
|
|
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * @brief 解析Redis的基础通用数据,不包含bBoxes数组数据
|
|
|
+ *
|
|
|
+ * @param strData Redis返回的源数据,JSON格式
|
|
|
+ * @param alarmInfo 解析出来的数据
|
|
|
+ */
|
|
|
+void SPAServer::parseRedisBaseData(const std::string& strData, AlarmInfo& alarmInfo)
|
|
|
+{
|
|
|
+ try
|
|
|
+ {
|
|
|
+ nJson json0;
|
|
|
+ json0 = nJson::parse(strData);
|
|
|
+ alarmInfo.AlarmID = json0["alarmId"].get<int>();
|
|
|
+ alarmInfo.ChannelID = json0["channel"].get<int>();
|
|
|
+ alarmInfo.PicUrl = json0["picUrl"].get<std::string>();
|
|
|
+ alarmInfo.ImageInfo = json0["imageInfo"].get<std::string>();
|
|
|
+
|
|
|
+ /* 解析时间,需要将时间中的“T”换成空格 */
|
|
|
+ alarmInfo.StartTime = json0["beginTime"].get<std::string>();
|
|
|
+ std::replace(alarmInfo.StartTime.begin(), alarmInfo.StartTime.end(), 'T', ' ');
|
|
|
+ alarmInfo.EndTime = json0["endTime"].get<std::string>();
|
|
|
+ std::replace(alarmInfo.EndTime.begin(), alarmInfo.EndTime.end(), 'T', ' ');
|
|
|
+ alarmInfo.EventTime = json0["eventTime"].get<std::string>();
|
|
|
+ std::replace(alarmInfo.EventTime.begin(), alarmInfo.EventTime.end(), 'T', ' ');
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (const nJson::parse_error& e)
|
|
|
+ {
|
|
|
+ SPDLOG_LOGGER_ERROR(m_logger, "解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ catch (const nJson::type_error& e)
|
|
|
+ {
|
|
|
+ SPDLOG_LOGGER_ERROR(m_logger, "解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 解析Redis的bBoxes数据,这个内容可能根据算法ID不同,内容不同
|
|
|
+ *
|
|
|
+ * @param strData
|
|
|
+ * @param alarmInfo
|
|
|
+ */
|
|
|
+void SPAServer::parseRedisBBoxesData(const std::string& strData, AlarmInfo& alarmInfo)
|
|
|
+{
|
|
|
+ try
|
|
|
+ {
|
|
|
+ nJson json0;
|
|
|
+ json0 = nJson::parse(strData);
|
|
|
+ /* 判断bBoxes有无数据,有数据就解析,没数据就直接返回了 */
|
|
|
+ nJson json1 = json0["bBoxes"];
|
|
|
+
|
|
|
+ std::string labelList; /* 记录违禁品 */
|
|
|
+ std::string bboxList; /* 记录bbox */
|
|
|
+ if(!json1.empty())
|
|
|
+ {
|
|
|
+ for(auto& it0 : json1)
|
|
|
+ {
|
|
|
+ /* 如果status是true,就不是报警,直接跳过 */
|
|
|
+ bool status = it0["status"].get<bool>();
|
|
|
+ if(status)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* 这一条Box数据报警了将其内容存储起来 */
|
|
|
+ alarmInfo.Is_Alarm = true;
|
|
|
+ /* 解析label,违禁品关键字,先判断这个是不是违禁品检测的算法ID */
|
|
|
+ if(alarmInfo.ActionID == m_keyContraband)
|
|
|
+ {
|
|
|
+ /* 解析报警,取出报警类型 */
|
|
|
+ nJson label = it0["label"];
|
|
|
+ for(auto& it1 : label)
|
|
|
+ {
|
|
|
+ std::string strLabel = it1.get<std::string>();
|
|
|
+ /* 检测是否已经加入到字符串中了 */
|
|
|
+ strLabel += "|";
|
|
|
+ if(labelList.find(strLabel) != std::string::npos)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ labelList += strLabel;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* 解析bbox,貌似是在图像中的位置 */
|
|
|
+ nJson bbox = it0["bbox"];
|
|
|
+ std::string strBbox;
|
|
|
+ for(auto& it1 : bbox)
|
|
|
+ {
|
|
|
+ strBbox += std::to_string(it1.get<int>()) + ",";
|
|
|
+ }
|
|
|
+ /* 去掉最后一个“,” */
|
|
|
+ if(!strBbox.empty())
|
|
|
+ {
|
|
|
+ strBbox.pop_back();
|
|
|
+ }
|
|
|
+ bboxList += strBbox + "|";
|
|
|
+ }
|
|
|
+ /* 去掉最后一个“|” */
|
|
|
+ if(!labelList.empty())
|
|
|
+ {
|
|
|
+ labelList.pop_back();
|
|
|
+ }
|
|
|
+ if(!bboxList.empty())
|
|
|
+ {
|
|
|
+ bboxList.pop_back();
|
|
|
+ }
|
|
|
+ SPDLOG_LOGGER_DEBUG(m_logger, "违禁品列表:{}", labelList);
|
|
|
+ SPDLOG_LOGGER_DEBUG(m_logger, "bbox列表:{}", bboxList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 如果有报警的Box,解析出报警的说明 */
|
|
|
+ if(alarmInfo.Is_Alarm)
|
|
|
+ {
|
|
|
+ /* 添加报警信息的提示信息 */
|
|
|
+ alarmInfo.BboxList = bboxList;
|
|
|
+ /* 违禁品报警信息,违禁品列表不是空的,就添加补充的文字 */
|
|
|
+ if( (alarmInfo.ActionID == m_keyContraband) && !labelList.empty() )
|
|
|
+ {
|
|
|
+ alarmInfo.ActionDes = fmt::format("出现违禁品[{}]告警", labelList);
|
|
|
+ SPDLOG_LOGGER_INFO(m_logger, "{}", alarmInfo.ActionDes);
|
|
|
+ }else {
|
|
|
+ /* 其他报警信息,直接获取 */
|
|
|
+ alarmInfo.ActionDes = json0["actionDes"].get<std::string>();
|
|
|
+ }
|
|
|
+ /* 判断有没有报警数据 */
|
|
|
+ if(alarmInfo.ImageInfo.empty())
|
|
|
+ {
|
|
|
+ SPDLOG_LOGGER_WARN(m_logger, "有报警区域,但是没有图片信息");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ /* 如果是人员报警,就存储人员报警信息 */
|
|
|
+ if(alarmInfo.ActionID == m_KeyFace)
|
|
|
+ {
|
|
|
+ nJson jsonArray = json0["personList"];
|
|
|
+ for(auto& it : jsonArray)
|
|
|
+ {
|
|
|
+ PersonInfo personInfo;
|
|
|
+ personInfo.PersonID = it["personId"].get<std::string>();
|
|
|
+ personInfo.PersonName = it["personName"].get<std::string>();
|
|
|
+ alarmInfo.vecPersonInfo.push_back(personInfo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (const nJson::parse_error& e)
|
|
|
+ {
|
|
|
+ SPDLOG_LOGGER_ERROR(m_logger, "解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ catch (const nJson::type_error& e)
|
|
|
+ {
|
|
|
+ SPDLOG_LOGGER_ERROR(m_logger, "解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
-/* 判断时间是否长时间没有更新 */
|
|
|
+/**
|
|
|
+ * @brief 判断时间是否长时间没有更新,默认的是600秒,超过这个时间Redis还未更新,可能是超脑挂了
|
|
|
+ *
|
|
|
+ * @param strTime
|
|
|
+ * @return true
|
|
|
+ * @return false
|
|
|
+ */
|
|
|
bool SPAServer::isEventTimeVaild(const std::string& strTime)
|
|
|
{
|
|
|
/* 获取当前时间 */
|
|
@@ -572,8 +752,8 @@ bool SPAServer::isEventTimeVaild(const std::string& strTime)
|
|
|
2、 将算法信息加入到不同的列表中
|
|
|
需要多个摄像机配合的加入到m_runListRoomActionInfo列表
|
|
|
不需要多个摄像机配合的加入到m_runListActionInfo列表
|
|
|
- 3、 每次刷新都会清空算法的摄像机列表,但是不会动其他基本信息,如果是列表中没有对应的信息,就会创建新的ActionInfo
|
|
|
- 那么RunState的状态是INIT,需要开启新的线程
|
|
|
+ 3、 每次刷新都会清空ActionInfo或者RoomActionInfo的摄像机列表,但是不会动其他基本信息,如果是列表中没有对应的信息,
|
|
|
+ 就会创建新的ActionInfo,那么RunState的状态是INIT,需要开启新的线程
|
|
|
如果刷新完成后,算法对应的摄像机列表为空,那么对应的线程就会停止运行,将RunState设置为STOP,本线程的下一轮
|
|
|
循环就会删除这个ActionInfo
|
|
|
|
|
@@ -588,11 +768,12 @@ void SPAServer::threadRoomCamera()
|
|
|
while (m_threadRunning)
|
|
|
{
|
|
|
/* 先获取EQM数据库信息,取出房间和摄像机关联信息 */
|
|
|
-
|
|
|
+ m_mutexActionInfo.lock();
|
|
|
+ toEQMDataBase->getActionInfo(m_listActionInfo);
|
|
|
/* 取出每个房间的所有算法,int是RoomID,string是Action */
|
|
|
- std::multimap<int, std::string> mapCameraActionID;
|
|
|
+ // std::multimap<int, std::string> mapCameraActionID;
|
|
|
m_mutexRunRAI.lock();
|
|
|
- m_mutexActionInfo.lock();
|
|
|
+
|
|
|
/* 先清理已经退出的线程所用到的Action或者RoomAction */
|
|
|
m_runListRoomActionInfo.clearStopRoomAction();
|
|
|
m_runListActionInfo.clearStopAction();
|
|
@@ -638,7 +819,8 @@ void SPAServer::threadRoomCamera()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- /* 这个容器用于不关联的算法信息,每个设备上的每个算法是一个线程 */
|
|
|
+ /* 这个容器用于不关联的算法信息,每个设备上的每个算法是一个线程
|
|
|
+ * ActionInfo.RunState = RUN_STATE_INIT ,就是新增的算法,需要创建新的线程 */
|
|
|
for(const auto& it0 : m_runListActionInfo.getData())
|
|
|
{
|
|
|
/* 违禁物品识别,不需要多个摄像机融合判断,多少个报警都直接上报 */
|
|
@@ -694,7 +876,7 @@ bool SPAServer::insertCameraToAction(RoomActionInfo* pRAInfo, std::list<RoomCame
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-/* 更新房间、算法需要的摄像机个数 */
|
|
|
+/* 更新房间、算法需要的摄像机个数,从内存中更新 */
|
|
|
bool SPAServer::updateRoomActionCameraCount(std::shared_ptr<RoomActionInfo> pRAInfo)
|
|
|
{
|
|
|
std::lock_guard<std::mutex> look(m_mutexRunRAI);
|
|
@@ -710,7 +892,7 @@ bool SPAServer::updateRoomActionCameraCount(std::shared_ptr<RoomActionInfo> pRAI
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-/* 更新算法的摄像机ID */
|
|
|
+/* 更新算法的摄像机ID,这里是从内存中的数组里获取 */
|
|
|
bool SPAServer::updateActionCameraID(std::shared_ptr<ActionInfo> pInfo)
|
|
|
{
|
|
|
pInfo->CameraID = -1;
|
|
@@ -843,10 +1025,13 @@ void SPAServer::threadActPersonNumber(RoomActionInfo* RAInfo)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief 报警判断条件:机房内出现摄像头的违禁物品识别算法输出结果包含指定违规内容时,记为报警行为,直接
|
|
|
- 展示报警结果
|
|
|
+ * @brief 违禁物品识别
|
|
|
+ 报警判断条件:机房内出现摄像头的违禁物品识别算法输出结果包含指定违规内容时,记为报警行为,直接
|
|
|
+ 展示报警结果
|
|
|
+ 这里应该是不区分违禁物品是什么,只要有违禁物品就报警。如果一个违禁物品消失之前又出现了第二个违禁物品,
|
|
|
+ 两个违禁物品都消失后,这次报警才会结束。
|
|
|
*
|
|
|
- * @param info
|
|
|
+ * @param info 线程信息
|
|
|
*/
|
|
|
void SPAServer::threadActContraband(ActionInfo* info)
|
|
|
{
|
|
@@ -876,7 +1061,8 @@ void SPAServer::threadActContraband(ActionInfo* info)
|
|
|
}
|
|
|
/* 解析数据 */
|
|
|
AlarmInfo alarmInfo;
|
|
|
- parseRedisData(strRetValue, alarmInfo);
|
|
|
+ parseRedisBaseData(strRetValue, alarmInfo);
|
|
|
+ parseRedisBBoxesData(strRetValue, alarmInfo);
|
|
|
/* 判断事件的时效性,超过多少秒不更新就可能是超脑挂了 */
|
|
|
if(isEventTimeVaild(alarmInfo.EventTime))
|
|
|
{
|
|
@@ -884,6 +1070,7 @@ void SPAServer::threadActContraband(ActionInfo* info)
|
|
|
continue;
|
|
|
}
|
|
|
/* 判断有无报警记录,写入到EQM数据库 */
|
|
|
+
|
|
|
}
|
|
|
/* 设置线程退出的状态 */
|
|
|
setThreadState(pActInfo, RunTimeState::RUN_STATE_STOP);
|
|
@@ -891,9 +1078,10 @@ void SPAServer::threadActContraband(ActionInfo* info)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief 1、野猫、宠物识别:报警判断条件:机房内出现摄像头的宠物识别算法输出报警结果时,记为报警行为,直接
|
|
|
+ * @brief 非法入侵检测
|
|
|
+ 1、野猫、宠物识别:报警判断条件:机房内出现摄像头的宠物识别算法输出报警结果时,记为报警行为,直接
|
|
|
展示报警结果
|
|
|
- 2、无权限人员识别:报警判断条件:当判断出区域非法入侵行为时,依据人脸识别结果,判断是否为人脸库人
|
|
|
+ 2、无权限人员识别:报警判断条件:当判断出区域非法入侵行为时,依据人脸识别结果,判断是否为人脸库人
|
|
|
员,非人脸库的人员时判断为非法入侵行为(背影需要报警需要结合区域人员检测算法判断)
|
|
|
*
|
|
|
* @param info
|