Переглянути джерело

V0.2.2
1、完成了普通的检测功能(一个算法一个摄像机)
2、添加了更新报警信息表的功能
3、添加了其他读写数据库的功能

Apple 6 місяців тому
батько
коміт
b3cfc1c25e

+ 139 - 3
SecurePlayAuxServer/GlobalInfo/GlobalInfo.cpp

@@ -66,6 +66,120 @@ bool DeviceInfo::isEqualAlgorithmInfo(const std::vector<AlgorithmInfo>& other)
     return true;
 }
 
+/* ====================================================================================
+ * **************************    AlarmInfo成员函数    ****************************
+ * ====================================================================================*/
+
+AlarmInfo::AlarmInfo() 
+{
+    Is_Alarm = false;
+    AlarmID = 0;
+    DeviceID = 0;
+    RoomID = 0;
+    ChannelID = 0;
+    State = 0;
+    OnWork = 0;
+    StartTime = "";
+    EndTime = "";
+    EventTime = "";
+    PicUrl = "";
+    ImageInfo = "";
+    AppID = "";
+    ActionID = "";
+    ActionDes = "";
+    FaceIDList = "";
+    FaceNameList = "";
+    BboxList = "";
+}
+
+AlarmInfo& AlarmInfo::operator=(AlarmInfo& other)
+{
+    if(this != &other)
+    {
+        Is_Alarm = other.Is_Alarm;
+        AlarmID = other.AlarmID;
+        DeviceID = other.DeviceID;
+        RoomID = other.RoomID;
+        ChannelID = other.ChannelID;
+        State = other.State;
+        OnWork = other.OnWork;
+        StartTime = other.StartTime;
+        EndTime = other.EndTime;
+        EventTime = other.EventTime;
+        PicUrl = other.PicUrl;
+        ImageInfo = other.ImageInfo;
+        AppID = other.AppID;
+        ActionID = other.ActionID;
+        ActionDes = other.ActionDes;
+        FaceIDList = other.FaceIDList;
+        FaceNameList = other.FaceNameList;
+        BboxList = other.BboxList;
+        vecPersonInfo = other.vecPersonInfo;
+    }
+    return *this;
+}
+
+/* 清空内容 */
+void AlarmInfo::reInit()
+{
+    Is_Alarm = false;
+    AlarmID = 0;
+    DeviceID = 0;
+    RoomID = 0;
+    ChannelID = 0;
+    State = 0;
+    OnWork = 0;
+    StartTime = "";
+    EndTime = "";
+    EventTime = "";
+    PicUrl = "";
+    ImageInfo = "";
+    AppID = "";
+    ActionID = "";
+    ActionDes = "";
+    FaceIDList = "";
+    FaceNameList = "";
+    BboxList = "";
+}
+
+AlarmRuleInfo::AlarmRuleInfo()
+{
+    LiveMinEnable = false;
+    LiveMaxEnable = false;
+    DicMinEnable = false;
+    DicMaxEnable = false;
+    LiveDicMinEnable = false;
+    LiveDicMaxEnable = false;
+    LiveMin = 0;
+    LiveMax = 0;
+    DicMin = 0;
+    DicMax = 0;
+    LiveDicMin = 0;
+    LiveDicMax = 0;
+    RuleName = "";
+}
+
+AlarmRuleInfo& AlarmRuleInfo::operator=(AlarmRuleInfo& other)
+{
+    if(this != &other)
+    {
+        LiveMinEnable = other.LiveMinEnable;
+        LiveMaxEnable = other.LiveMaxEnable;
+        DicMinEnable = other.DicMinEnable;
+        DicMaxEnable = other.DicMaxEnable;
+        LiveDicMinEnable = other.LiveDicMinEnable;
+        LiveDicMaxEnable = other.LiveDicMaxEnable;
+        LiveMin = other.LiveMin;
+        LiveMax = other.LiveMax;
+        DicMin = other.DicMin;
+        DicMax = other.DicMax;
+        LiveDicMin = other.LiveDicMin;
+        LiveDicMax = other.LiveDicMax;
+        RuleName = other.RuleName;
+    }
+    return *this;
+}
+
 /* ====================================================================================
  * **************************    ListActionInfo成员函数    ****************************
  * ====================================================================================*/
@@ -198,6 +312,28 @@ void ListActionInfo::clear()
  * ***********************    ListRoomActionInfo成员函数    ***************************
  * ====================================================================================*/
 
+/* 对比频道信息、房间信息、算法ID是否相等 */
+bool RoomActionInfo::isEqualBaseInfo(const RoomActionInfo& other)
+{
+    if(ChannelID != other.ChannelID) {
+        return false;
+    }
+    if(RoomID != other.RoomID) {
+        return false;
+    }
+    if(ActionID != other.ActionID) {
+        return false;
+    }
+    if(RoomType != other.RoomType) {
+        return false;
+    }
+
+
+
+    return true;
+}
+
+
 /* 添加关联信息,会自动进行重复判断,如果已有相同的room和action关联信息,则跳过 */
 bool ListRoomActionInfo::insertRoomActionInfo(const RoomActionInfo& info)
 {
@@ -323,7 +459,7 @@ GlobalConfig g_config;
 
 GlobalConfig::GlobalConfig()
 {
-
+    ThreadSleepMS = 300;
 }
 
 /* 读取配置文件 */
@@ -340,7 +476,7 @@ bool GlobalConfig::readConfig(const QString& strConfigFile)
 
     settings.beginGroup("System");
     AppPeopleOnWork = settings.value("APPPEPOLEONWORK", 300).toInt();         /* 离岗时间 */
-    AppBadthing = settings.value("APPBADTHING", 50).toInt();                 /* 违禁物品出现的时间 */
+    Contraband = settings.value("APPBADTHING", 50).toInt();                 /* 违禁物品出现的时间 */
     AppBadMan = settings.value("APPBADMAN", 50).toInt();                     /* 非法入侵 */
     AppTired = settings.value("APPTIRED", 50).toInt();                       /* 疲劳检测时间 */
     AppPeopleCont = settings.value("APPPEPOLECONT", 50).toInt();             /* 人员聚集时间 */
@@ -370,7 +506,7 @@ bool GlobalConfig::readConfig(const QString& strConfigFile)
 void GlobalConfig::printValue()
 {
     SPDLOG_INFO("APPPEPOLEONWORK:   {}", AppPeopleOnWork);
-    SPDLOG_INFO("APPBADTHING:       {}", AppBadthing);
+    SPDLOG_INFO("APPBADTHING:       {}", Contraband);
     SPDLOG_INFO("APPBADMAN:         {}", AppBadMan);
     SPDLOG_INFO("APPTIRED:          {}", AppTired);
     SPDLOG_INFO("APPPEOPLECONT:     {}", AppPeopleCont);

+ 36 - 21
SecurePlayAuxServer/GlobalInfo/GlobalInfo.h

@@ -187,28 +187,36 @@ struct AlarmInfo
     std::string BboxList;       /* Bbox列表,应该是图片中的报警位置 */
     std::vector<PersonInfo> vecPersonInfo;    /* 人员信息 */
 
-    AlarmInfo() {
-        Is_Alarm = false;
-        AlarmID = 0;
-        DeviceID = 0;
-        RoomID = 0;
-        ChannelID = 0;
-        State = 0;
-        OnWork = 0;
-        StartTime = "";
-        EndTime = "";
-        EventTime = "";
-        PicUrl = "";
-        ImageInfo = "";
-        AppID = "";
-        ActionID = "";
-        ActionDes = "";
-        FaceIDList = "";
-        FaceNameList = "";
-        BboxList = "";
-    }
+    AlarmInfo();
+    AlarmInfo& operator=(AlarmInfo& other);
+
+    void reInit();
+
 };
 
+/**
+ * @brief 报警规则
+ * 
+ */
+struct AlarmRuleInfo
+{
+    bool LiveMinEnable;         /* 启用直播间最小人数 */
+    bool LiveMaxEnable;         /* 启用直播间最大人数 */
+    bool DicMinEnable;          /* 启用导播间最小人数 */
+    bool DicMaxEnable;          /* 启用导播间最大人数 */
+    bool LiveDicMinEnable;      /* 启用直播间和导播间最小人数 */
+    bool LiveDicMaxEnable;      /* 启用直播间和导播间最大人数 */
+    int LiveMin;                /* 直播间最小人数 */
+    int LiveMax;                /* 直播间最大人数 */
+    int DicMin;                 /* 导播间最小人数 */
+    int DicMax;                 /* 导播间最大人数 */
+    int LiveDicMin;             /* 直播间和导播间最小人数 */
+    int LiveDicMax;             /* 直播间和导播间最大人数 */
+    std::string RuleName;       /* 规则名称 */
+
+    AlarmRuleInfo();
+    AlarmRuleInfo& operator=(AlarmRuleInfo& other);
+};
 
 /**
  * @brief 房间和摄像机关联信息,这个是从客户端配置的
@@ -260,6 +268,7 @@ struct ActionInfo
     int RoomType;                   /* 房间类型 */
     std::string ActionID;           /* 算法ID */
     std::string strRoomName;        /* 房间名称 */
+    std::string strActionName;      /* 算法名称,这个是自定义的,不需要对比 */
 
     ActionInfo() {
         RunState = RunTimeState::RUN_STATE_INIT;
@@ -269,6 +278,7 @@ struct ActionInfo
         RoomType = -1;
         ActionID = "";
         strRoomName = "";
+        strActionName = "";
     }
     ActionInfo& operator=(const ActionInfo& other) {
         if (this != &other) {
@@ -378,6 +388,9 @@ struct RoomActionInfo
         }
         return *this;
     }
+
+    /* 对比频道信息、房间信息、算法ID是否相等 */
+    bool isEqualBaseInfo(const RoomActionInfo& other);
     
 
 };
@@ -431,7 +444,7 @@ public:
     void printValue();
     
     int AppPeopleOnWork;        /* 离岗时间 */
-    int AppBadthing;            /* 违禁物品出现的时间 */
+    int Contraband;             /* 违禁物品出现的时间 */
     int AppBadMan;              /* 非法入侵 */
     int AppTired;               /* 疲劳检测时间 */
     int AppPeopleCont;          /* 区域人员统计 */
@@ -442,6 +455,8 @@ public:
     int CheckSet;               /* 服务端多久检测一次配置 */
     int EventTimeValid;         /* 事件时间有效期 */
 
+    int ThreadSleepMS;          /* 任务线程休眠时间,单位是ms */
+
     std::string Key;            /* Key */
     std::string Secret;         /* Secret */
  };

+ 144 - 22
SecurePlayAuxServer/SPAServer.cpp

@@ -371,7 +371,7 @@ void SPAServer::compareDeviceAlgorithmInfo(const std::vector<DeviceInfo>& vecNew
 /**
  * @brief 从Redis获取数据线程函数,这个是摄像机线程
  *        一个设备一个线程,这个线程的相关变量只在这个线程中使用
- * 
+ *      (注意,这个函数未被使用,不从这里获取Redis数据)
  * @param info 
  */
 void SPAServer::threadFromRedis(const CameraThreadInfo& info)
@@ -773,12 +773,11 @@ void SPAServer::threadRoomCamera()
         /* 取出每个房间的所有算法,int是RoomID,string是Action */
         // std::multimap<int, std::string> mapCameraActionID;
         m_mutexRunRAI.lock();
-        
+        m_mutexRunAI.lock();
         /* 先清理已经退出的线程所用到的Action或者RoomAction */
         m_runListRoomActionInfo.clearStopRoomAction();
         m_runListActionInfo.clearStopAction();
 
-        m_mutexRunActionInfo.lock();
         /* 将算法信息加入到不同的列表中
          * 需要多个摄像机配合的加入到m_runListRoomActionInfo列表
          * 不需要多个摄像机配合的加入到m_runListActionInfo列表
@@ -795,7 +794,7 @@ void SPAServer::threadRoomCamera()
             }
         }
         m_mutexActionInfo.unlock();
-        /* 这个容器只用于多个摄像机融合的算法,一个房间的一个算法是一个线程 */
+        /* 这个容器只用于多个摄像机融合的算法,一个功能是一个线程 */
         for(const auto& it0 : m_runListRoomActionInfo.getData())
         {
             /* 人员在岗识别 */
@@ -815,9 +814,14 @@ void SPAServer::threadRoomCamera()
                 SPDLOG_LOGGER_INFO(m_logger, "RoomID:{} 区域人员检测", it0->RoomID);
                 if(it0->RunState == RunTimeState::RUN_STATE_INIT)
                 {
-                    CPPTP.add_task(&SPAServer::threadActPersonNumber, this, it0);
+                    CPPTP.add_task(&SPAServer::threadActRegionalPersonnelDetection, this, it0);
                 }
             }
+            /* 非法入侵 */
+            else if( it0->ActionID == ActIllegalInvasion)
+            {
+
+            }
         }
         /* 这个容器用于不关联的算法信息,每个设备上的每个算法是一个线程
          * ActionInfo.RunState = RUN_STATE_INIT ,就是新增的算法,需要创建新的线程 */
@@ -827,9 +831,10 @@ void SPAServer::threadRoomCamera()
             if (it0->ActionID == ActContraband)
             {
                 SPDLOG_LOGGER_INFO(m_logger, "RoomID:{} 违禁品检测", it0->RoomID);
+                it0->strActionName = "违禁品检测";
                 if(it0->RunState == RunTimeState::RUN_STATE_INIT)
                 {
-                    CPPTP.add_task(&SPAServer::threadActContraband, this, it0);
+                    CPPTP.add_task(&SPAServer::threadActNormal, this, it0);
                 }
             }
             /* 非法入侵检测,这个也不需要摄像机融合 */
@@ -844,7 +849,10 @@ void SPAServer::threadRoomCamera()
             }
         }
         m_mutexRunRAI.unlock();
-        m_mutexRunActionInfo.unlock();
+        m_mutexRunAI.unlock();
+
+        /* 休眠n秒,默认应该是300秒 */
+        std::this_thread::sleep_for(std::chrono::seconds(g_config.CheckSet));
     }
 }
 
@@ -896,7 +904,7 @@ bool SPAServer::updateRoomActionCameraCount(std::shared_ptr<RoomActionInfo> pRAI
 bool SPAServer::updateActionCameraID(std::shared_ptr<ActionInfo> pInfo)
 {
     pInfo->CameraID = -1;
-    std::lock_guard<std::mutex> look(m_mutexRunActionInfo);
+    std::lock_guard<std::mutex> look(m_mutexRunAI);
     for(auto& it : m_runListActionInfo.getData())
     {
         if(it->isEqualBaseInfo(*pInfo))
@@ -911,7 +919,7 @@ bool SPAServer::updateActionCameraID(std::shared_ptr<ActionInfo> pInfo)
 /* 设置线程状态 */
 void SPAServer::setThreadState(std::shared_ptr<ActionInfo> pInfo, RunTimeState state)
 {
-    std::lock_guard<std::mutex> look(m_mutexRunActionInfo);
+    std::lock_guard<std::mutex> look(m_mutexRunAI);
     for(auto& it : m_runListActionInfo.getData())
     {
         if(it->isEqualBaseInfo(*pInfo))
@@ -922,6 +930,20 @@ void SPAServer::setThreadState(std::shared_ptr<ActionInfo> pInfo, RunTimeState s
     }
 }
 
+/* 设置线程状态 */
+void SPAServer::setThreadState(std::shared_ptr<RoomActionInfo> pInfo, RunTimeState state)
+{
+    std::lock_guard<std::mutex> look(m_mutexRunRAI);
+    for(auto& it : m_runListRoomActionInfo.getData())
+    {
+        if(it->isEqualBaseInfo(*pInfo))
+        {
+            it->RunState = state;
+            break;
+        }
+    }
+}
+
 
 
 /**
@@ -986,22 +1008,27 @@ void SPAServer::threadActPersonWork(RoomActionInfo* RAInfo)
  * 
  * @param RAInfo 传入的房间ID和算法ID
  */
-void SPAServer::threadActPersonNumber(RoomActionInfo* RAInfo)
+void SPAServer::threadActRegionalPersonnelDetection(RoomActionInfo* RAInfo)
 {
     SPDLOG_LOGGER_INFO(m_logger, "开启区域人员检测线程,RoomID:{} ,Action:{}", RAInfo->RoomID, RAInfo->ActionID);
     /* 创建读取Redis的实例 */
     std::shared_ptr<FromRedis> fromRedis = std::make_shared<FromRedis>();
     /* 局部变量 */
     std::shared_ptr<RoomActionInfo> pRAInfo = std::make_shared<RoomActionInfo>();
+    /* 创建写入数据库实例 */
+    std::shared_ptr<ToEQMDataBase> toEQMDataBase = std::make_shared<ToEQMDataBase>();
     *pRAInfo = *RAInfo;
     int CameraCount = 0;
-    /* 摄像机数目等于0就退出线程 */
+    /* 保存每个摄像机的报警信息 */
+    std::shared_ptr<std::list<AlarmInfo>> pListAlarmInfo = std::make_shared<std::list<AlarmInfo>>();
+    
     while (m_threadRunning)
     {
         /* 更新算法关联的摄像机个数 */
         pRAInfo->listCameraID.clear();
         updateRoomActionCameraCount(pRAInfo);
         CameraCount = pRAInfo->listCameraID.size();
+        /* 摄像机数目等于0就退出线程 */
         if(CameraCount == 0)
         {
             break;
@@ -1015,33 +1042,44 @@ void SPAServer::threadActPersonNumber(RoomActionInfo* RAInfo)
             if(!fromRedis->getRedisString(strKey, strRetValue))
             {
                 SPDLOG_LOGGER_ERROR(m_logger, "读取Redis数据失败, Key:{}", strKey);
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                 continue;
             }
             /* 解析数据 */
             AlarmInfo alarmInfo;
-            parseRedisData(strRetValue, alarmInfo);
+            parseRedisBaseData(strRetValue, alarmInfo);
+            parseRedisBBoxesData(strRetValue, alarmInfo);
         }
     }
+    setThreadState(pRAInfo, RunTimeState::RUN_STATE_STOP);
+    SPDLOG_LOGGER_INFO(m_logger, "关闭区域人员检测线程,RoomID:{} ,Action:{}", RAInfo->RoomID, RAInfo->ActionID);
 }
 
 /**
- * @brief 违禁物品识别
-            报警判断条件:机房内出现摄像头的违禁物品识别算法输出结果包含指定违规内容时,记为报警行为,直接
-            展示报警结果
-            这里应该是不区分违禁物品是什么,只要有违禁物品就报警。如果一个违禁物品消失之前又出现了第二个违禁物品,
-            两个违禁物品都消失后,这次报警才会结束。
+ * @brief 普通任务识别,一个算法只需要一个摄像机,包括 违禁品识别、玩手机检测、老鼠等
+            1、报警判断条件:机房内出现摄像头的违禁物品识别算法输出结果包含指定违规内容时,记为报警行为,直接
+                展示报警结果
+            2、这里应该是不区分违禁物品是什么,只要有违禁物品就报警。如果一个违禁物品消失之前又出现了第二个违禁物品,
+                两个违禁物品都消失后,这次报警才会结束。
+            3、一有报警信息就写入到EQM数据库的tAlarmInfo表中,报警开始时间是EventTime
+            4、如果报警结束时间小于设置的最小间隔,就删除这条数据,超过这个时间,就更新结束时间
+            5、报警信息要有图片,如果当前没有图片,就使用上一张图片
  * 
  * @param info 线程信息
  */
-void SPAServer::threadActContraband(ActionInfo* info)
+void SPAServer::threadActNormal(ActionInfo* info)
 {
-    SPDLOG_LOGGER_INFO(m_logger, "开启违禁物品识别线程,RoomID:{} ,Action:{}", info->RoomID, info->ActionID);
+    SPDLOG_LOGGER_INFO(m_logger, "开启 {} 线程,RoomID:{} ,Action:{}",info->strActionName, info->RoomID, info->ActionID);
     /* 创建读取Redis的实例 */
     std::shared_ptr<FromRedis> fromRedis = std::make_shared<FromRedis>();
+    /* 创建写入EQM数据库实例 */
+    std::shared_ptr<ToEQMDataBase> toEQMDataBase = std::make_shared<ToEQMDataBase>();
     /* 局部变量,保存这个线程的信息 */
     std::shared_ptr<ActionInfo> pActInfo = std::make_shared<ActionInfo>();
     *pActInfo = *info;
-    /* 摄像机ID小于0就退出线程 */
+    /* 保存报警数据 */
+    std::shared_ptr<AlarmInfo> pAlarmInfo = std::make_shared<AlarmInfo>();
+
     while (m_threadRunning)
     {
         /* 更新算法关联的摄像机ID */
@@ -1057,6 +1095,7 @@ void SPAServer::threadActContraband(ActionInfo* info)
         if(!fromRedis->getRedisString(strKey, strRetValue))
         {
             SPDLOG_LOGGER_ERROR(m_logger, "读取Redis数据失败, Key:{}", strKey);
+            std::this_thread::sleep_for(std::chrono::milliseconds(100));
             continue;
         }
         /* 解析数据 */
@@ -1067,14 +1106,65 @@ void SPAServer::threadActContraband(ActionInfo* info)
         if(isEventTimeVaild(alarmInfo.EventTime))
         {
             SPDLOG_LOGGER_WARN(m_logger, "Redis Key:{} 数据长时间没有更新,EventTime:{}",strKey, alarmInfo.EventTime);
+            std::this_thread::sleep_for(std::chrono::milliseconds(100));
             continue;
         }
-        /* 判断有无报警记录,写入到EQM数据库 */
+        /* 判断有无报警记录,新的报警就写入到EQM的tAlarmInfo表中,正在报警的记录就判断和更新,
+         * 结束报警就更新tAlarmInfo的报警数据的结束时间,并清空pAlarmInfo */
+        if(pAlarmInfo->Is_Alarm)
+        {
+            /* 正在报警中,检查是否报警结束 */
+            if(alarmInfo.Is_Alarm)
+            {
+                /* 更新图片信息 */
+                if(!alarmInfo.ImageInfo.empty())
+                {
+                    pAlarmInfo->ImageInfo = alarmInfo.ImageInfo;
+                }
+            } else {
+                /* 报警结束,判断时长,更新数据库结束时间,结束时间是此时电脑时间 */
+                if(timeDiffWithNow(pAlarmInfo->EventTime) > g_config.Contraband)
+                {
+                    pAlarmInfo->EndTime = chronoToStrTime(std::chrono::system_clock::now());
+                    toEQMDataBase->updateAlarmEndTime(*pAlarmInfo);
+                }else {
+                    /* 不够报警时间,目前不做任何处理,不删除EQM报警记录 */
+                }
 
+                /* 清空报警信息 */
+                pAlarmInfo->reInit();
+            }
+        }
+        else
+        {
+            /* 是新的报警 */
+            if(alarmInfo.Is_Alarm)
+            {
+                /* 布片不能是空,如果是空的,就不写入数据库 */
+                if(!alarmInfo.ImageInfo.empty())
+                {
+                    /* 违禁品检测,开始时间是事件时间 */
+                    alarmInfo.StartTime = alarmInfo.EventTime;
+                    alarmInfo.EndTime = "";
+                    if(toEQMDataBase->insertAlarmInfo(alarmInfo))
+                    {
+                        /* 保存新的报警记录 */
+                        *pAlarmInfo = alarmInfo;
+                    }else {
+                        SPDLOG_LOGGER_ERROR(m_logger, "写入tAlarmInfo报警数据失败, Key: {}", strKey);
+                    }
+                }else {
+                    SPDLOG_LOGGER_ERROR(m_logger, "频道:{}, 房间:{}, 摄像机:{}, 算法:{}, 有报警区域, 但是没有图片信息", pActInfo->ChannelID, pActInfo->RoomID, pActInfo->CameraID, pActInfo->ActionID);
+                }
+            }
+        }
+
+        /* 休眠设置的时间 */
+        std::this_thread::sleep_for(std::chrono::seconds(g_config.ThreadSleepMS));
     }
     /* 设置线程退出的状态 */
     setThreadState(pActInfo, RunTimeState::RUN_STATE_STOP);
-    SPDLOG_LOGGER_INFO(m_logger, "违禁物品识别线程退出,RoomID:{} ,CameraID, Action:{}", pActInfo->RoomID, pActInfo->CameraID, pActInfo->ActionID);
+    SPDLOG_LOGGER_INFO(m_logger, "{} 线程退出,RoomID:{} ,CameraID, Action:{}",pActInfo->strActionName, pActInfo->RoomID, pActInfo->CameraID, pActInfo->ActionID);
 }
 
 /**
@@ -1128,3 +1218,35 @@ void SPAServer::threadActIllegalInvasion(ActionInfo* info)
     SPDLOG_LOGGER_INFO(m_logger, "非法入侵检测线程退出,RoomID:{} ,CameraID:{}, Action:{}", pActInfo->RoomID, pActInfo->CameraID, pActInfo->ActionID);
 }
 
+/* 计算与当前时间的时间差,返回秒 */
+int SPAServer::timeDiffWithNow(const std::string& strTime)
+{
+    auto now = std::chrono::system_clock::now();
+
+    auto eventTime = strTimeToChrono(strTime);
+    std::chrono::duration<double> diff = now - eventTime;
+
+    return diff.count();
+}
+
+/* 字符串时间转换成std::chrono时间点 */
+std::chrono::system_clock::time_point SPAServer::strTimeToChrono(const std::string& strTime)
+{
+    std::istringstream iss(strTime);
+    std::tm tmEvent = {};
+    iss >> std::get_time(&tmEvent, "%Y-%m-%d %H:%M:%S");
+
+    return std::chrono::system_clock::from_time_t(std::mktime(&tmEvent));
+}
+
+/* 时间点转换成字符串 */
+std::string SPAServer::chronoToStrTime(const std::chrono::system_clock::time_point& timePoint)
+{
+    std::time_t time = std::chrono::system_clock::to_time_t(timePoint);
+    std::tm tmEvent = *std::localtime(&time);
+    char buf[64] = {0};
+    std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tmEvent);
+
+    return std::string(buf);
+}
+

+ 16 - 7
SecurePlayAuxServer/SPAServer.h

@@ -31,7 +31,7 @@ private:
     /* 对比设备和算法关联表是否需要更新 */
     void compareDeviceAlgorithmInfo(const std::vector<DeviceInfo>& vecNewInfo, std::vector<DeviceInfo>& vecDevUpdate);
 
-    /* 从Redis获取数据线程函数,这个是摄像机线程 */
+    /* 从Redis获取数据线程函数,这个是摄像机线程(注意,这个函数未被使用,不从这里获取Redis数据) */
     void threadFromRedis(const CameraThreadInfo& info);
 
     /* 解析Redis基础数据 */
@@ -53,17 +53,26 @@ private:
     bool updateActionCameraID(std::shared_ptr<ActionInfo> pInfo);
     /* 设置线程状态 */
     void setThreadState(std::shared_ptr<ActionInfo> pInfo, RunTimeState state);
+    void setThreadState(std::shared_ptr<RoomActionInfo> pInfo, RunTimeState state);
     
     /* 人员在岗识别线程,应该是人脸识别线程,这个需要房间内多个摄像机共同识别 */
     void threadActPersonWork(RoomActionInfo* RAInfo);
     /* 区域人员检测,检测这个区域内的人数,不能少于多少人,不能多余多少人 */
-    void threadActPersonNumber(RoomActionInfo* RAInfo);
-    /* 违禁物品识别 */
-    void threadActContraband(ActionInfo* info);
+    void threadActRegionalPersonnelDetection(RoomActionInfo* RAInfo);
+    /* 普通任务线程,一个算法值对应一个摄像机 */
+    void threadActNormal(ActionInfo* info);
     /* 非法入侵检测 */
     void threadActIllegalInvasion(ActionInfo* info);
 
 
+    /* 计算与当前时间的时间差,返回秒 */
+    int timeDiffWithNow(const std::string& strTime);
+    /* 字符串时间转换成std::chrono时间点 */
+    std::chrono::system_clock::time_point strTimeToChrono(const std::string& strTime);
+    /* 时间点转换成字符串 */
+    std::string chronoToStrTime(const std::chrono::system_clock::time_point& timePoint);
+
+
 private:
     std::shared_ptr<spdlog::logger> m_logger = nullptr;
 
@@ -77,9 +86,9 @@ private:
 
     std::mutex m_mutexActionID;                         /* 算法ID的互斥锁 */
     std::string ActPersonWork;                          /* 人员在岗识别 */
-    std::string ActPersonNumber;                        /* 区域人员检测 */
-    std::string ActContraband;                          /* 违禁品检测 */
+    std::string ActPersonNumber;                        /* 区域人员检测(区域人员计数) */
     std::string ActIllegalInvasion;                     /* 非法入侵检测 */
+    std::string ActContraband;                          /* 违禁品检测 */
     std::string ActFatigueDetection;                    /* 疲劳检测 */
 
     /* 算法信息,这个就是tAction在内存中的数据,方便后续对比,程序启动的时候会先获取一份 */
@@ -97,7 +106,7 @@ private:
     std::mutex m_mutexRunRAI;
     ListRoomActionInfo m_runListRoomActionInfo;
     /* 运行时的算法信息列表,这个容器存储的是不需要摄像机融合的算法 */
-    std::mutex m_mutexRunActionInfo;
+    std::mutex m_mutexRunAI;
     ListActionInfo m_runListActionInfo;
 
 };

+ 90 - 1
SecurePlayAuxServer/communication/ToEQMDataBase.cpp

@@ -835,7 +835,7 @@ bool ToEQMDataBase::insertAlarmInfo(const AlarmInfo& alarmInfo)
     auto ret = m_httpApi->DBQDoInterface(enDBOperatorType::EDBOT_Insert, strCmd, strRet);
     if(ret < 0)
     {
-        SPDLOG_LOGGER_DEBUG(m_logger,"插入报警信息失败:{}, 错误信息:{}",ret,m_httpApi->DoGetLastError(&ret).toStdString());
+        SPDLOG_LOGGER_WARN(m_logger,"插入报警信息失败:{}, 错误信息:{}",ret,m_httpApi->DoGetLastError(&ret).toStdString());
         return false;
     }
     SPDLOG_LOGGER_DEBUG(m_logger,"插入报警信息成功!");
@@ -843,3 +843,92 @@ bool ToEQMDataBase::insertAlarmInfo(const AlarmInfo& alarmInfo)
     return true;
 }
 
+/* 更新报警结束时间 */
+bool ToEQMDataBase::updateAlarmEndTime(const AlarmInfo& alarmInfo)
+{
+    if(m_httpApi == nullptr)
+    {
+        SPDLOG_LOGGER_ERROR(m_logger,"WebApi is nullptr");
+        return false;
+    }
+    nJson json0;
+    json0["opName"] = "SPSS_UpdateAlarmEndTime";
+    nJson json1;
+    json1["EndTime"] = alarmInfo.EndTime;
+    json1["ChannelID"] = alarmInfo.ChannelID;
+    json1["RoomID"] = alarmInfo.RoomID;
+    json1["CamerID"] = alarmInfo.DeviceID;
+    json1["ActionID"] = alarmInfo.ActionID;
+    json0["paramList"] = json1;
+
+    QString strCmd = QString::fromStdString(json0.dump());
+    QString strRet;
+    int ret = m_httpApi->DBQDoInterface(enDBOperatorType::EDBOT_Update, strCmd, strRet);
+    if(ret < 0)
+    {
+        SPDLOG_LOGGER_WARN(m_logger,"更新报警结束时间失败:{}, 错误信息:{}",ret,m_httpApi->DoGetLastError(&ret).toStdString());
+        return false;
+    }
+
+    return true;
+}
+
+/* 获取报警规则表 */
+bool ToEQMDataBase::getAlarmRuleInfo(std::vector<AlarmRuleInfo>& vecInfo)
+{
+    nJson json0;
+    json0["opName"] = "SPSS_SelectFromAlarmRule";
+    QString strCmd = QString::fromStdString(json0.dump());
+    QString strRet;
+    auto ret = m_httpApi->DBQDoInterface(enDBOperatorType::EDBOT_Select, strCmd, strRet);
+    if(ret < 0)
+    {
+        SPDLOG_LOGGER_DEBUG(m_logger,"获取AlarmRule失败:{}, 错误信息:{}",ret,m_httpApi->DoGetLastError(&ret).toStdString());
+        return false;
+    }
+    try 
+    {
+        nJson json1 = nJson::parse(strRet.toStdString());
+        int retCode = json1["code"].get<int>();
+        if(retCode != 0)
+        {
+            SPDLOG_LOGGER_ERROR(m_logger,"获取tAlarmRule失败");
+            return false;
+        }
+        nJson result = json1["result"];
+        if(result.empty())
+        {
+            return false;
+        }
+        for(const auto& it : result)
+        {
+            AlarmRuleInfo info;
+            info.RuleName = it["ruleName"].get<std::string>();
+            info.LiveMinEnable = it["liveMinEnable"].get<bool>();
+            info.LiveMaxEnable = it["liveMaxEnable"].get<bool>();
+            info.DicMinEnable = it["dicMinEnable"].get<bool>();
+            info.DicMaxEnable = it["dicMaxEnable"].get<bool>();
+            info.LiveDicMinEnable = it["liveDicMinEnable"].get<bool>();
+            info.LiveDicMaxEnable = it["liveDicMaxEnable"].get<bool>();
+            info.LiveMin = it["liveMin"].get<int>();
+            info.LiveMax = it["liveMax"].get<int>();
+            info.DicMin = it["dicMin"].get<int>();
+            info.DicMax = it["dicMax"].get<int>();
+            info.LiveDicMin = it["liveDicMin"].get<int>();
+            info.LiveDicMax = it["liveDicMax"].get<int>();
+            vecInfo.push_back(info);
+        }
+    } 
+    catch (const nJson::parse_error& e) {
+        SPDLOG_LOGGER_ERROR(m_logger,"解析AlarmRule数据失败:{}, 错误ID:{}",e.what(), e.id);
+        return false;
+    }
+    catch(const nJson::type_error& e)
+    {
+        SPDLOG_LOGGER_ERROR(m_logger,"解析AlarmRule数据失败:{}, 错误ID:{}",e.what(), e.id);
+        return false;
+    }
+
+    return true;
+}
+

+ 5 - 0
SecurePlayAuxServer/communication/ToEQMDataBase.h

@@ -47,6 +47,11 @@ public:
 
     /* 写入报警信息 */
     bool insertAlarmInfo(const AlarmInfo& alarmInfo);
+    /* 更新报警结束时间 */
+    bool updateAlarmEndTime(const AlarmInfo& alarmInfo);
+
+    /* 获取报警规则表 */
+    bool getAlarmRuleInfo(std::vector<AlarmRuleInfo>& vecInfo);
     
 
 private:

+ 33 - 7
安播辅助服务程序说明.md

@@ -107,7 +107,8 @@
 1. `tAction`是算法信息表,从超脑获取到的算法信息写入这个表格
 2. `tWorkOnInfo`是人员在岗信息表,也包含人脸信息库
 3. `tActionCamer`摄像机ID,和算法ID关联
-4. `tFaceUser`用户信息表
+4. `tFaceUser`用户信息表,读取到超脑返回的用户数据,写入该表
+5. `tRuleInfo`报警规则表,区域人员检测(人员计数)会使用到
 
 ## 启动程序,可能需要从EQM获取的数据
 1. `tCamerinfo`获取摄像机信息
@@ -120,16 +121,41 @@
 ## 应用各算法的检测逻辑
 1. 逻辑详情见[安播辅助提示系统需求文档](https://alidocs.dingtalk.com/i/nodes/20eMKjyp81pbx3nQUAEOAlnGJxAZB1Gv?utm_scene=person_space)
 2. 这里不在是一个摄像机一个线程了,而是根据任务分配线程,如违禁物品每个摄像机对应的Action都可以检测,那么每个Redis Key就是一个线程;检测人员在岗需要房间内的多个摄像机联合起来判断,那么这个房间内的整个摄像机都是对应的Action都在一个线程中。
+3. 这里以任务分配线程,如人员计数报警
 
 ## 报警算法逻辑
-### 写入EQM数据库报警信息
+### 单个算法-单个摄像机写入EQM数据库报警信息
 1. 出现报警时会创建一条报警信息,直接写入到数据库,只有开始时间
 2. 根据设置的规定时间继续检测(如违禁品出现10秒才算报警,防止误报警),在规定时间内报警消失,就删除这次报警信息
 3. 超过了规定时间,报警消失,更新开始创建的那条信息的结束时间
 
+#### 违禁品检测
+1. 违禁品检测一个摄像机一个算法
+2. 检测违禁品一般是一个算法只检测一个物品,如果有多个物品,报警只有一条,所有违禁品都消失了,才会结束报警
 
-### 人员在岗信息
-1. 人员在岗主要是给客户端实时信息
-2. 创建一个最新的记录在`tWorkOnInfo`表中,不断地更新这条数据的人数和人员信息
-3. 如果所有人都离岗了,这条信息的人数就清零
-4. 最新的在岗信息清零后,人员回来,就重新创建一条新的数据
+
+
+### 算法需要多个摄像机配合
+1. 区域人员计数是一个算法ID,人脸检测是一个算法ID,这两个结合起来,组合成了区域人员检测(人员计数、在岗离岗、非法入侵等)
+
+#### 区域人员检测(区域人员计数)
+1. 区域人员计数有几种情况
+    - 直播间、导播间各有最小和最大人数
+    - 直播间+导播间有最小和最大人数
+
+#### 区域非法入侵
+1. 区域非法入侵需要两个算法配合,区域人员检测和人脸识别
+2. 区域人员非法入侵有几种情况,这个算法需要
+    - 未知人员入侵
+    - 人员个数和人脸可以识别的个数不相等
+3. 区域人员检测需要判断是否在检测时间段内
+4. 一个频率只有一个直播间和一个导播间?
+
+#### 人员在岗识别
+1. 需要人脸识别算法
+2. 人员在岗主要是给客户端实时信息
+3. 创建一个最新的记录在`tWorkOnInfo`表中,不断地更新这条数据的人数和人员信息
+4. 如果所有人都离岗了,这条信息的人数就清零
+5. 最新的在岗信息清零后,人员回来,就重新创建一条新的数据
+
+#### 直播间-离岗在岗报警