Prechádzať zdrojové kódy

V0.4.2
1、分离了普通检测功能

Apple 1 mesiac pred
rodič
commit
bbe110dfe0

+ 22 - 0
SecurePlayAuxServer/Application/FuncBase.cpp

@@ -0,0 +1,22 @@
+#include "FuncBase.h"
+
+#include "spdlog/spdlog.h"
+#include "FromRedis.h"
+#include "ToEQMDataBase.h"
+
+FuncBase::FuncBase()
+{
+    m_fromRedis = std::make_shared<FromRedis>();
+    m_toEQMDataBase = std::make_shared<ToEQMDataBase>();
+}
+
+FuncBase::~FuncBase()
+{
+
+}
+
+/* 设置功能信息 */
+void FuncBase::setFuncActionInfo(FuncActionInfo* pFuncAct)
+{
+    m_pFuncAct = pFuncAct;
+}

+ 37 - 0
SecurePlayAuxServer/Application/FuncBase.h

@@ -0,0 +1,37 @@
+#ifndef FUNCBASE_H
+#define FUNCBASE_H
+
+#include <memory>
+
+class FromRedis;
+class ToEQMDataBase;
+struct FuncActionInfo;
+
+/**
+ * @brief 功能基础类,包含读取Redis数据和写入EQM数据库的功能
+ * 
+ */
+class FuncBase
+{
+
+public:
+    FuncBase();
+    ~FuncBase();
+
+    /* 任务线程 */
+    virtual void thread_task() = 0;
+
+    /* 设置功能信息 */
+    void setFuncActionInfo(FuncActionInfo* pFuncAct);
+
+protected:
+    /* 读取Redis的实例 */
+    std::shared_ptr<FromRedis> m_fromRedis = nullptr;
+    /* 写入EQM数据库的实例 */
+    std::shared_ptr<ToEQMDataBase> m_toEQMDataBase = nullptr;
+    /* 线程信息 */
+    FuncActionInfo* m_pFuncAct = nullptr;
+};
+
+
+#endif // FUNCBASE_H

+ 149 - 0
SecurePlayAuxServer/Application/FuncOrdinary.cpp

@@ -0,0 +1,149 @@
+#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;
+    }
+    /* 给变量分配内存 */
+    m_listAlarm = new ListAlarmInfo();
+}
+
+FuncOrdinary::~FuncOrdinary()
+{
+    if(m_listAlarm != nullptr)
+    {
+        delete m_listAlarm;
+        m_listAlarm = nullptr;
+    }
+}
+
+/* 任务线程 */
+void FuncOrdinary::thread_task()
+{
+    if(m_pFuncAct == nullptr)
+    {
+        SPDLOG_LOGGER_ERROR(m_logger, "未设置线程功能信息");
+        return;
+    }
+    SPDLOG_LOGGER_INFO(m_logger, "开启 {} 线程, ChannelID:{}",m_pFuncAct->strFunctionName, m_pFuncAct->ChannelID);
+
+    while (GThreadInfo.getRunning())
+    {
+        /* 更新算法关联的摄像机ID */
+        // updateFuncInfo(pFuncAct);
+        /* 如果线程的运行状态为 */
+        if(m_pFuncAct->RunState == RunTimeState::RUN_STATE_STOP)
+        {
+            /* 设置为NONE,就会被管理线程回收释放内存 */
+            m_pFuncAct->appFunction = AppFunction::APP_NONE;
+            break;
+        }
+        /* 读取Redis信息,处理数据 */
+        for(const auto& roomInfo : m_pFuncAct->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(100));
+                    continue;
+                }
+                /* 解析数据 */
+                AlarmInfo alarmInfo;
+                parseRedisBaseData(strRetValue, alarmInfo);
+                parseRedisBBoxesData(strRetValue, alarmInfo);
+                /* 判断事件的时效性,超过多少秒不更新就可能是超脑挂了 */
+                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;
+                }
+                /* 找出数组中与当前报警ID相同的的报警信息 */
+                auto pAlarmInfo = m_listAlarm->findAlarmInfo(alarmInfo);
+                if(pAlarmInfo == nullptr)
+                {
+                    /* 没有找到报警记录,就新建一个 */
+                    AlarmInfo newAlarmInfo = alarmInfo;
+                    m_listAlarm->addAlarmInfo(newAlarmInfo);
+                    /* 重新查找该报警信息 */
+                    pAlarmInfo = m_listAlarm->findAlarmInfo(alarmInfo);
+                    if(pAlarmInfo == nullptr)
+                    {
+                        SPDLOG_LOGGER_ERROR(m_logger, "查找报警信息失败, Key:{}", strKey);
+                        continue;
+                    }
+                }
+                /* 判断有无报警记录,新的报警就写入到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());
+                            m_toEQMDataBase->updateAlarmEndTime(*pAlarmInfo);
+                        }else {
+                            /* 不够报警时间,目前不做任何处理,不删除EQM报警记录 */
+                        }
+
+                        /* 清空报警信息 */
+                        pAlarmInfo->reInit();
+                    }
+                }
+                else
+                {
+                    /* 是新的报警 */
+                    if(alarmInfo.Is_Alarm)
+                    {
+                        /* 图片不能是空,如果是空的,就不写入数据库 */
+                        if(!alarmInfo.ImageInfo.empty())
+                        {
+                            /* 违禁品检测,开始时间是事件时间 */
+                            alarmInfo.StartTime = alarmInfo.EventTime;
+                            alarmInfo.EndTime = "";
+                            if(m_toEQMDataBase->insertAlarmInfo(alarmInfo))
+                            {
+                                /* 保存新的报警记录 */
+                                *pAlarmInfo = alarmInfo;
+                            }else {
+                                SPDLOG_LOGGER_ERROR(m_logger, "写入tAlarmInfo报警数据失败, Key: {}", strKey);
+                            }
+                        }else {
+                            SPDLOG_LOGGER_ERROR(m_logger, "频道:{}, 房间:{}, 摄像机:{}, 算法:{}, 有报警区域, 但是没有图片信息", pFuncAct->ChannelID, roomInfo.RoomID, it.first, it.second);
+                        }
+                    }
+                }
+
+            }
+        }
+        
+        /* 休眠设置的时间 */
+        std::this_thread::sleep_for(std::chrono::seconds(g_config.ThreadSleepMS));
+    }
+    /* 设置线程退出的状态 */
+    setThreadState(m_pFuncAct, RunTimeState::RUN_STATE_STOP);
+    SPDLOG_LOGGER_INFO(m_logger, "{} 线程退出,Channel:{}", m_pFuncAct->strFunctionName, m_pFuncAct->ChannelID);
+}

+ 24 - 0
SecurePlayAuxServer/Application/FuncOrdinary.h

@@ -0,0 +1,24 @@
+#ifndef FuncOrdinary_H
+#define FuncOrdinary_H
+
+#include "FuncBase.h"
+#include "spdlog/spdlog.h"
+
+struct ListAlarmInfo;
+
+class FuncOrdinary : public FuncBase
+{
+public:
+    FuncOrdinary();
+    ~FuncOrdinary();
+
+    /* 任务线程 */
+    void thread_task() override;
+
+private:
+    std::shared_ptr<spdlog::logger> m_logger = nullptr;
+
+    ListAlarmInfo* m_listAlarm = nullptr;               /* 报警列表 */
+};
+
+#endif // FuncOrdinary_H

+ 92 - 0
SecurePlayAuxServer/GlobalInfo/GlobalConfig.cpp

@@ -0,0 +1,92 @@
+#include "GlobalConfig.h"
+#include <QSettings>
+
+#include "spdlog/spdlog.h"
+
+
+/* ====================================================================================
+ * **************************    GlobalConfig成员函数    ******************************
+ * ====================================================================================*/
+
+/* 创建实例 */
+GlobalConfig g_config;
+
+GlobalConfig::GlobalConfig()
+{
+    ThreadSleepMS = 300;
+}
+
+
+/* 读取配置文件 */
+bool GlobalConfig::readConfig(const QString& strConfigFile)
+{
+    if(strConfigFile.isEmpty())
+    {
+        SPDLOG_ERROR("读取配置文件失败! 配置文件名为空");
+        return false;
+    }
+    SPDLOG_DEBUG("读取配置文件: {}", strConfigFile.toStdString());
+    QSettings settings(strConfigFile, QSettings::IniFormat);
+    settings.setIniCodec("UTF-8");
+
+    settings.beginGroup("System");
+
+    AppUpdateOnWorkTimeInterval = settings.value("WorkOnInfoSecond", 600).toInt();  /* 更新在岗信息的时间间隔 */
+    AppPeopleOnWork = settings.value("APPPEPOLEONWORK", 300).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();             /* 人员聚集时间 */
+    AppPlayPhone = settings.value("APPPLAYPHONE", 50).toInt();               /* 玩手机识别 */
+    AppMouse = settings.value("APPMOUSE", 50).toInt();                       /* 手势识别 */
+    AppMask = settings.value("APPMASK", 5).toInt();                         /* 戴口罩识别 */
+
+    CheckSet = settings.value("CHECKSET", 300).toInt();                       /* 服务端多久检测一次配置 */
+    EventTimeValid = settings.value("EventTimeValid", 300).toInt();           /* 事件时间有效期 */
+
+    Key = settings.value("Key").toString().toStdString();                           /* Key */
+    Secret = settings.value("Secret").toString().toStdString();                     /* Secret */
+
+    settings.endGroup();
+
+    if(Key.empty() || Secret.empty())
+    {
+        SPDLOG_ERROR("读取配置文件失败! Key或Secret为空");
+        return false;
+    }
+
+    return true;
+}
+
+
+/* 打印读取到的值 */
+void GlobalConfig::printValue()
+{
+    SPDLOG_INFO("APPPEPOLEONWORK:   {}", AppPeopleOnWork);
+    SPDLOG_INFO("APPBADTHING:       {}", Contraband);
+    SPDLOG_INFO("APPBADMAN:         {}", AppBadMan);
+    SPDLOG_INFO("APPTIRED:          {}", AppTired);
+    SPDLOG_INFO("APPPEOPLECONT:     {}", AppPeopleCont);
+    SPDLOG_INFO("APPPLAYPHONE:      {}", AppPlayPhone);
+    SPDLOG_INFO("APPMOUSE:          {}", AppMouse);
+    SPDLOG_INFO("APPMASK:           {}", AppMask);
+    SPDLOG_INFO("CHECKSET:          {}", CheckSet);
+    SPDLOG_INFO("EventTimeValid:    {}", EventTimeValid);
+}
+
+
+/* ====================================================================================
+ * ************************    GlobalThreadInfo成员函数    *****************************
+ * ====================================================================================*/
+
+/* 获取线程运行标志位 */
+bool GlobalThreadInfo::getRunning() const
+{
+    return m_bRunning;
+}
+
+/* 设置线程运行标志位 */
+void GlobalThreadInfo::setRunning(bool bRunning)
+{
+    m_bRunning = bRunning;
+}

+ 74 - 0
SecurePlayAuxServer/GlobalInfo/GlobalConfig.h

@@ -0,0 +1,74 @@
+#ifndef GLOBALCONFIG_H
+#define GLOBALCONFIG_H
+
+#include <QString>
+
+
+/* ====================================================================================
+ * **************************    GlobalConfig成员函数    ******************************
+ * ====================================================================================*/
+
+/**
+ * @brief 读取配置文件
+ * 
+ */
+ class GlobalConfig
+ {
+public:
+    GlobalConfig();
+    ~GlobalConfig() = default;
+
+    /* 读取配置文件 */
+    bool readConfig(const QString& strConfigFile);
+    /* 打印读取到的值 */
+    void printValue();
+    
+    int AppUpdateOnWorkTimeInterval;/* 更新在岗信息的时间间隔 */
+    int AppPeopleOnWork;        /* 离岗时间 */
+    int Contraband;             /* 违禁物品出现的时间 */
+    int AppBadMan;              /* 非法入侵 */
+    int AppTired;               /* 疲劳检测时间 */
+    int AppPeopleCont;          /* 区域人员统计 */
+    int AppPlayPhone;           /* 玩手机识别 */
+    int AppMouse;               /* 老鼠识别 */
+    int AppMask;                /* 戴口罩识别 */
+
+    int CheckSet;               /* 服务端多久检测一次配置 */
+    int EventTimeValid;         /* 事件时间有效期 */
+
+    int ThreadSleepMS;          /* 任务线程休眠时间,单位是ms */
+
+    std::string Key;            /* Key */
+    std::string Secret;         /* Secret */
+ };
+
+extern GlobalConfig g_config;
+
+
+/* ====================================================================================
+ * ************************    GlobalThreadInfo成员函数    *****************************
+ * ====================================================================================*/
+
+#define GThreadInfo GlobalThreadInfo::getInstance()
+class GlobalThreadInfo
+{
+    GlobalThreadInfo();
+    ~GlobalThreadInfo() {}
+    GlobalThreadInfo(const GlobalThreadInfo& other) = delete;
+    GlobalThreadInfo& operator=(const GlobalThreadInfo& other) = delete;
+public:
+    static GlobalThreadInfo& getInstance()
+    {
+        static GlobalThreadInfo instance;
+        return instance;
+    }
+    /* 获取线程运行标志位 */
+    inline bool getRunning() const;
+    /* 设置线程运行标志位 */
+    inline void setRunning(bool bRunning);
+
+private:
+    std::atomic_bool m_bRunning;    /* 线程运行状态 */
+};
+
+#endif /* GLOBALCONFIG_H */

+ 1 - 72
SecurePlayAuxServer/GlobalInfo/GlobalInfo.cpp → SecurePlayAuxServer/GlobalInfo/GlobalVariable.cpp

@@ -1,6 +1,4 @@
-#include "GlobalInfo.h"
-
-#include <QSettings>
+#include "GlobalVariable.h"
 
 #include "spdlog/spdlog.h"
 
@@ -1231,72 +1229,3 @@ FuncActionInfo* ListFuncActInfo::findAppFunction(const FuncActionInfo& func)
     return nullptr;
 }
 
-
-/* ====================================================================================
- * **************************    GlobalConfig成员函数    ******************************
- * ====================================================================================*/
-
-/* 创建实例 */
-GlobalConfig g_config;
-
-GlobalConfig::GlobalConfig()
-{
-    ThreadSleepMS = 300;
-}
-
-/* 读取配置文件 */
-bool GlobalConfig::readConfig(const QString& strConfigFile)
-{
-    if(strConfigFile.isEmpty())
-    {
-        SPDLOG_ERROR("读取配置文件失败! 配置文件名为空");
-        return false;
-    }
-    SPDLOG_DEBUG("读取配置文件: {}", strConfigFile.toStdString());
-    QSettings settings(strConfigFile, QSettings::IniFormat);
-    settings.setIniCodec("UTF-8");
-
-    settings.beginGroup("System");
-
-    AppUpdateOnWorkTimeInterval = settings.value("WorkOnInfoSecond", 600).toInt();  /* 更新在岗信息的时间间隔 */
-    AppPeopleOnWork = settings.value("APPPEPOLEONWORK", 300).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();             /* 人员聚集时间 */
-    AppPlayPhone = settings.value("APPPLAYPHONE", 50).toInt();               /* 玩手机识别 */
-    AppMouse = settings.value("APPMOUSE", 50).toInt();                       /* 手势识别 */
-    AppMask = settings.value("APPMASK", 5).toInt();                         /* 戴口罩识别 */
-
-    CheckSet = settings.value("CHECKSET", 300).toInt();                       /* 服务端多久检测一次配置 */
-    EventTimeValid = settings.value("EventTimeValid", 300).toInt();           /* 事件时间有效期 */
-
-    Key = settings.value("Key").toString().toStdString();                           /* Key */
-    Secret = settings.value("Secret").toString().toStdString();                     /* Secret */
-
-    settings.endGroup();
-
-    if(Key.empty() || Secret.empty())
-    {
-        SPDLOG_ERROR("读取配置文件失败! Key或Secret为空");
-        return false;
-    }
-
-    return true;
-}
-
-
-/* 打印读取到的值 */
-void GlobalConfig::printValue()
-{
-    SPDLOG_INFO("APPPEPOLEONWORK:   {}", AppPeopleOnWork);
-    SPDLOG_INFO("APPBADTHING:       {}", Contraband);
-    SPDLOG_INFO("APPBADMAN:         {}", AppBadMan);
-    SPDLOG_INFO("APPTIRED:          {}", AppTired);
-    SPDLOG_INFO("APPPEOPLECONT:     {}", AppPeopleCont);
-    SPDLOG_INFO("APPPLAYPHONE:      {}", AppPlayPhone);
-    SPDLOG_INFO("APPMOUSE:          {}", AppMouse);
-    SPDLOG_INFO("APPMASK:           {}", AppMask);
-    SPDLOG_INFO("CHECKSET:          {}", CheckSet);
-    SPDLOG_INFO("EventTimeValid:    {}", EventTimeValid);
-}

+ 3 - 39
SecurePlayAuxServer/GlobalInfo/GlobalInfo.h → SecurePlayAuxServer/GlobalInfo/GlobalVariable.h

@@ -1,5 +1,5 @@
-#ifndef GLOBALINFO_H
-#define GLOBALINFO_H
+#ifndef GLOBALVARIABLE_H
+#define GLOBALVARIABLE_H
 
 #include <QString>
 #include <vector>
@@ -687,43 +687,7 @@ public:
  };
 
 
-/**
- * @brief 读取配置文件
- * 
- */
- class GlobalConfig
- {
-public:
-    GlobalConfig();
-    ~GlobalConfig() = default;
-
-    /* 读取配置文件 */
-    bool readConfig(const QString& strConfigFile);
-    /* 打印读取到的值 */
-    void printValue();
-    
-    int AppUpdateOnWorkTimeInterval;/* 更新在岗信息的时间间隔 */
-    int AppPeopleOnWork;        /* 离岗时间 */
-    int Contraband;             /* 违禁物品出现的时间 */
-    int AppBadMan;              /* 非法入侵 */
-    int AppTired;               /* 疲劳检测时间 */
-    int AppPeopleCont;          /* 区域人员统计 */
-    int AppPlayPhone;           /* 玩手机识别 */
-    int AppMouse;               /* 老鼠识别 */
-    int AppMask;                /* 戴口罩识别 */
-
-    int CheckSet;               /* 服务端多久检测一次配置 */
-    int EventTimeValid;         /* 事件时间有效期 */
-
-    int ThreadSleepMS;          /* 任务线程休眠时间,单位是ms */
-
-    std::string Key;            /* Key */
-    std::string Secret;         /* Secret */
- };
-
-extern GlobalConfig g_config;
-
 
 
 
-#endif /* GLOBALINFO_H */
+#endif /* GLOBALVARIABLE_H */

+ 223 - 0
SecurePlayAuxServer/GlobalInfo/UniversalFunc.cpp

@@ -0,0 +1,223 @@
+#include "UniversalFunc.h"
+
+#include "spdlog/spdlog.h"
+
+/**
+ * @brief 解析Redis的基础通用数据,不包含bBoxes数组数据
+ * 
+ * @param strData Redis返回的源数据,JSON格式
+ * @param alarmInfo 解析出来的数据
+ */
+ void 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_ERROR("解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
+         return;
+     }
+     catch (const nJson::type_error& e)
+     {
+         SPDLOG_ERROR("解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
+         return;
+     }
+ }
+
+ /**
+ * @brief 解析Redis的bBoxes数据,这个内容可能根据算法ID不同,内容不同
+ * 
+ * @param strData 
+ * @param alarmInfo 
+ */
+void parseRedisBBoxesData(const std::string& strData, AlarmInfo& alarmInfo)
+{
+    try
+    {
+        nJson json0;
+        json0 = nJson::parse(strData);
+        /* 判断bBoxes有无数据,有数据就解析,没数据就直接返回了 */
+        nJson json1 = json0["bBoxes"];
+        
+        std::string labelList;              /* 记录违禁品 */
+        std::list<std::string> listBbox;    /* 记录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 == g_actionList.ActContraband)
+                {
+                    /* 解析报警,取出报警类型 */
+                    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();
+                }
+                listBbox.push_back(strBbox);
+            }
+            /* 去掉最后一个“|” */
+            if(!labelList.empty())
+            {
+                labelList.pop_back();
+            }
+            SPDLOG_DEBUG("违禁品列表:{}", labelList);
+            
+        }
+        /* 如果有报警的Box,解析出报警的说明 */
+        if(alarmInfo.Is_Alarm)
+        {
+            /* 添加报警信息的提示信息 */
+            alarmInfo.listBbox = listBbox;
+            /* 违禁品报警信息,违禁品列表不是空的,就添加补充的文字 */
+            if( (alarmInfo.ActionID == g_actionList.ActContraband) && !labelList.empty() )
+            {
+                alarmInfo.ActionDes = fmt::format("出现违禁品[{}]告警", labelList);
+                SPDLOG_INFO("{}", alarmInfo.ActionDes);
+            }else {
+                /* 其他报警信息,直接获取 */
+                alarmInfo.ActionDes = json0["actionDes"].get<std::string>();
+            }
+            /* 判断有没有报警数据 */
+            if(alarmInfo.ImageInfo.empty())
+            {
+                SPDLOG_WARN("有报警区域,但是没有图片信息");
+                return;
+            }
+            /* 如果是人员报警,就存储人员报警信息 */
+            if(alarmInfo.ActionID == g_actionList.ActFace)
+            {
+                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_ERROR("解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
+        return;
+    }
+    catch (const nJson::type_error& e)
+    {
+        SPDLOG_ERROR("解析Redis数据失败:{}, 错误ID:{}", e.what(), e.id);
+        return;
+    }
+
+}
+
+
+/**
+ * @brief 判断时间是否长时间没有更新,默认的是600秒,超过这个时间Redis还未更新,可能是超脑挂了
+ * 
+ * @param strTime 
+ * @return true 
+ * @return false 
+ */
+ bool isEventTimeVaild(const std::string& strTime)
+ {
+     /* 获取当前时间 */
+     std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+     /* 字符串转成时间 */
+     std::istringstream iss(strTime);
+     std::tm tmEvent = {};
+     iss >> std::get_time(&tmEvent, "%Y-%m-%d %H:%M:%S");
+     /* 时间差 */
+     std::chrono::system_clock::time_point eventTime = std::chrono::system_clock::from_time_t(std::mktime(&tmEvent));
+     std::chrono::duration<double> diff = now - eventTime;
+     // SPDLOG_LOGGER_DEBUG(m_logger, "now:{} eventTime: {} 时间差:{}秒",now, eventTime, diff);
+     if(diff.count() > 600)
+     {
+         // SPDLOG_LOGGER_ERROR(m_logger, "Redis数据长时间没有更新,EventTime:{}", strTime);
+         return false;
+     }
+     return true;
+ }
+ 
+
+ /* 计算与当前时间的时间差,返回秒 */
+int 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 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 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);
+}
+
+
+
+

+ 27 - 0
SecurePlayAuxServer/GlobalInfo/UniversalFunc.h

@@ -0,0 +1,27 @@
+#ifndef UNIVERSALFUNC_H
+#define UNIVERSALFUNC_H
+
+#include <string>
+#include "GlobalVariable.h"
+
+/* ==========================================================
+ * 通用的功能函数
+ * ========================================================== */
+
+
+/* 解析Redis的基础通用数据,不包含bBoxes数组数据 */
+void parseRedisBaseData(const std::string& strData, AlarmInfo& alarmInfo);
+/* 解析Redis的bBoxes数据, */
+void parseRedisBBoxesData(const std::string& strData, AlarmInfo& alarmInfo);
+
+/* 判断时间是否长时间没有更新 */
+bool isEventTimeVaild(const std::string& strTime);
+/* 计算与当前时间的时间差,返回秒 */
+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);
+
+
+#endif // UNIVERSALFUNC_H

+ 2 - 2
SecurePlayAuxServer/SPAServer.cpp

@@ -1,10 +1,10 @@
 #include "SPAServer.h"
 
 #include "spdlog/spdlog.h"
-// #include "CurlHttp.h"
 #include <filesystem>
-// #include "CurlFtp.h"
 #include "ThreadPool/ThreadPool.h"
+#include "GlobalInfo/GlobalVariable.h"
+#include "GlobalInfo/GlobalConfig.h"
 
 #include <QApplication>
 #include <QVector>

+ 1 - 0
SecurePlayAuxServer/common/LHLog/LHLogInit.cpp

@@ -71,6 +71,7 @@ void initLog(QString ModuleName, CLHQLogApi& lhQLog)
         /* 创建一个ToEQMDataBase logger */
         auto logger_ToEQMDataBase = std::make_shared<spdlog::logger>("ToEQMDataBase",begin(sinks),end(sinks));
 
+
         /* 注册到注册表 */
         spdlog::register_logger(logger_main);
         spdlog::register_logger(logger_FromSuperBrain);

+ 1 - 1
SecurePlayAuxServer/communication/ToEQMDataBase.h

@@ -3,7 +3,7 @@
 
 #include "spdlog/spdlog.h"
 #include "lhhttpapi.h"
-#include "GlobalInfo.h"
+#include "GlobalVariable.h"
 
 class ToEQMDataBase
 {

+ 8 - 2
安播辅助服务程序说明.md

@@ -146,8 +146,14 @@
 
 ## 应用各算法的检测逻辑
 1. 逻辑详情见[安播辅助提示系统需求文档](https://alidocs.dingtalk.com/i/nodes/20eMKjyp81pbx3nQUAEOAlnGJxAZB1Gv?utm_scene=person_space)
-2. 这里不在是一个摄像机一个线程了,而是根据任务分配线程,如违禁物品每个摄像机对应的Action都可以检测,那么每个Redis Key就是一个线程;检测人员在岗需要房间内的多个摄像机联合起来判断,那么这个房间内的整个摄像机都是对应的Action都在一个线程中。
-3. 这里以任务分配线程,如人员计数报警
+2. 这里不再是一个摄像机一个线程了,而是根据任务分配线程,一个频率的一个检测功能就是一个线程,一个检测功能可能需要多个Action(算法),一个算法可能需要多个摄像机配合
+3. 这里的对应关系如下:
+    - `频率`包含几个`房间`
+    - 一个`房间`包含几种`功能`(APP)
+    - `功能`包含使用到的`算法`(Action)
+    - `算法`包含需要的`设备`(摄像机)
+
+4. 也有可能一种`功能`需要多个`房间`联合判断
 
 ## 报警算法逻辑
 ### 单个算法-单个摄像机写入EQM数据库报警信息