#include "SPAServer.h" #include "spdlog/spdlog.h" #include "CurlHttp.h" #include #include "CurlFtp.h" #include "ThreadPool/ThreadPool.h" #include SPAServer::SPAServer() { m_logger = spdlog::get("SPAServer"); if(m_logger == nullptr) { SPDLOG_ERROR("APAServer logger is nullptr"); return; } m_threadRunning = true; /* 初始化WebAPI */ m_toEQMDataBase.initWebApi("http://192.1.3.133:31000/v6/", "", "4c2f9fc91c22dd98331e47af2e2964f4"); /* 模拟违禁品算法ID,后续需要动态调整 */ m_contrabandKey = "OD210_026_005246_001-IZRTKyEx"; } SPAServer::~SPAServer() { } /* 启动服务 */ void SPAServer::startServer() { /* 添加获取基础信息的线程 */ // CPPTP.add_task(&SPAServer::threadFromSuperBrain, this); /* 测试Redis读取并解析数据线程 */ CameraThreadInfo info; info.RedisIP = "172.16.36.80"; info.RedisPort = 32222; info.RedisPWD = "Ff1z@TOFr^iwd%Ra"; info.DeviceID = 117; info.vecAction.push_back("OD210_026_005246_001-IZRTKyEx"); CPPTP.add_task(&SPAServer::threadFromRedis, this, info); } /** * @brief 从基础平台获取算法信息和设备信息的线程函数 * */ void SPAServer::threadFromSuperBrain() { SPDLOG_LOGGER_INFO(m_logger, "开启 fromSuperBrainThread 线程"); /* 创建变量 */ std::vector vecAlgNewInfo; std::vector vecDevNewInfo; /* 获取一次token,后续失效了再获取 */ m_fromSuperBrain.getToken(); while (m_threadRunning) { SPDLOG_LOGGER_INFO(m_logger, "刷新算法和设备信息"); /* 先更新数据库的信息,防止从其他地方更改了数据库,这里没有刷新本地缓存 */ m_toEQMDataBase.getAlgorithmInfo(m_vecEqmAlgInfo); m_toEQMDataBase.getDeviceInfo(m_vecEqmDevInfo); m_toEQMDataBase.getDeviceAlgorithmInfo(m_vecEqmDevInfo, m_listDevIDDelete); /* 从超脑获取基础信息 */ m_fromSuperBrain.getTaskTypeList(vecAlgNewInfo); m_fromSuperBrain.getDeviceList(vecDevNewInfo); /* 处理算法信息 */ bool algIsUpdate = processAlgorithmInfo(vecAlgNewInfo); /* 处理设备信息 */ bool devIsUpdate = processDeviceInfo(vecDevNewInfo); vecAlgNewInfo.clear(); vecDevNewInfo.clear(); std::this_thread::sleep_for(std::chrono::seconds(10)); } } /* 处理算法信息,返回值为true,说明有改变,需要重新读取 */ bool SPAServer::processAlgorithmInfo(std::vector vecNewAlgInfo) { std::vector vecAlgUpdate; std::vector vecAlgDelete; /* 对比数据库表格信息,这里只有插入和删除,没有更新 */ compareAlgorithmInfo(vecNewAlgInfo, vecAlgUpdate, vecAlgDelete); /* 更新数据库,先删除,再写入刷新 */ bool isUpdate = false; if(vecAlgDelete.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "删除算法信息"); m_toEQMDataBase.deleteAlgorithmInfo(vecAlgDelete); isUpdate = true; } if(vecAlgUpdate.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "写入算法信息"); m_toEQMDataBase.writeAlgorithmInfo(vecAlgUpdate); isUpdate = true; } return isUpdate; } /** * @brief 处理设备信息 * * @param vecNewDevInfo 传入新获取到的值 * @return true 需要重新读取数据库,获取新的数据 * @return false 无需读取数据库 */ bool SPAServer::processDeviceInfo(std::vector vecNewDevInfo) { std::vector vecDevInsert; std::vector vecDevUpdate; std::vector vecDevDelete; /*------------------------------------------------------------------------- ****** 这里只对比设备信息,不对比设备的算法信息,算法信息在下面单独对比 ******* *------------------------------------------------------------------------*/ /* 如果本地缓存没有数据,那么就全部插入 */ if(m_vecEqmDevInfo.size() > 0) { for(const auto& it : vecNewDevInfo) { bool isExist = false; for(const auto& it0 : m_vecEqmDevInfo) { if(it.DeviceID == it0.DeviceID) { isExist = true; /* 对比其他项是否相等,不相等就更新 */ if(it == it0) { continue; }else { vecDevUpdate.push_back(it); } break; } } if(!isExist) { vecDevInsert.push_back(it); } } }else { vecDevInsert = vecNewDevInfo; } /* 获取删除列表 */ if(vecNewDevInfo.size() > 0) { bool isExist = false; for(const auto& it : m_vecEqmDevInfo) { isExist = false; for(const auto& it0 : vecNewDevInfo) { if(it.DeviceID == it0.DeviceID) { isExist = true; break; } } if(!isExist) { vecDevDelete.push_back(it); } } }else { vecDevDelete = m_vecEqmDevInfo; } bool isUpdate = false; /* 先删除多余的数据 */ if(vecDevDelete.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "删除设备信息"); m_toEQMDataBase.deleteDeviceInfo(vecDevDelete); isUpdate = true; } /* 更新数据 */ if(vecDevUpdate.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "更新设备信息"); m_toEQMDataBase.updateDeviceInfo(vecDevUpdate); isUpdate = true; } /* 插入数据 */ if(vecDevInsert.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "插入设备信息"); m_toEQMDataBase.insertDeviceInfo(vecDevInsert); isUpdate = true; } /*------------------------------------------------------------------------- ************* 处理设备和算子关联的表格,单独对比设备的算法信息 ************* *------------------------------------------------------------------------*/ /* 插入新的设备信息 */ if(vecDevInsert.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "插入设备和算法关联表"); m_toEQMDataBase.insertDeviceAlgorithmInfo(vecDevInsert); isUpdate = true; } vecDevUpdate.clear(); /* 对比现有的设备是否需要更新算法 */ compareDeviceAlgorithmInfo(vecNewDevInfo, vecDevUpdate); if(vecDevUpdate.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "更新设备和算法关联表, 更新设备数目:{}", vecDevUpdate.size()); m_toEQMDataBase.updateDeviceAlgorithmInfo(vecDevUpdate); } /* 删除tActionCamer表中消失的设备信息 */ if(m_listDevIDDelete.size() > 0) { SPDLOG_LOGGER_DEBUG(m_logger, "删除消失的设备关联的算法"); m_toEQMDataBase.deleteDeviceAlgorithmInfo(m_listDevIDDelete); isUpdate = true; } return isUpdate; } /* 对比现有的数据和新获取到的数据,取出要删除和添加的数据 */ void SPAServer::compareAlgorithmInfo(const std::vector& vecNewInfo, std::vector& vecAlgUpdate, std::vector& vecAlgDelete) { /* 取出要添加的,如果本地缓存是0,那么全部都要添加 */ if(m_vecEqmAlgInfo.size() > 0) { for(const auto& it : vecNewInfo) { bool isExist = false; for(const auto& it0 : m_vecEqmAlgInfo) { /* 如果存在就退出循环 */ if(it.ActionID == it0.ActionID) { isExist = true; break; } } if(!isExist) { vecAlgUpdate.push_back(it); } } }else { vecAlgUpdate = vecNewInfo; } /* 取出要删除的,如果新的数据是0,那么全部都要删除 */ if(vecNewInfo.size() > 0) { bool isExist = false; for(const auto& it : m_vecEqmAlgInfo) { isExist = false; for(const auto& it0 : vecNewInfo) { if(it.ActionID == it0.ActionID) { isExist = true; break; } } if(!isExist) { vecAlgDelete.push_back(it); } } }else { vecAlgDelete = m_vecEqmAlgInfo; } } /** * @brief 对比设备和算法关联表是否需要更新 * 对比规则: * 1、这里只对比已有的设备ID,需要删除的ID在获取到tActionCamer表是就已经取出来了 * 2、如果设备ID相等,那么进一步对比算法信息是否相等 * 3、如果设备ID相等,但是算法信息数目不相等,那么直接加入更新列表 * 4、如果设备ID相等,算法信息数目相等,进一步对比算法信息 * * @param vecNewInfo * @param vecDevUpdate */ void SPAServer::compareDeviceAlgorithmInfo(const std::vector& vecNewInfo, std::vector& vecDevUpdate) { vecDevUpdate.clear(); for(const auto& it0 : vecNewInfo) { for(const auto& it1 : m_vecEqmDevInfo) { if(it0.DeviceID == it1.DeviceID) { /* 设备的算法信息数目不相等,直接加入更新列表 */ if(it0.vecAlgorithmInfo.size() != it1.vecAlgorithmInfo.size()) { vecDevUpdate.push_back(it0); break; } /* 设备的算法信息数目相等,进一步对比算法信息 */ bool isEquality = true; for(const auto& it2 : it0.vecAlgorithmInfo) { bool isEq2 = false; for(const auto& it3 : it1.vecAlgorithmInfo) { /* 这里只对比算法ID */ if(it2.ActionID != it3.ActionID) { continue; }else { isEq2 = true; break; } } if(!isEq2) { isEquality = false; break; } } if(!isEquality) { vecDevUpdate.push_back(it0); break; } } } } } /** * @brief 从Redis获取数据线程函数,这个是摄像机线程 * 一个设备一个线程,这个线程的相关变量只在这个线程中使用 * * @param info */ void SPAServer::threadFromRedis(const CameraThreadInfo& info) { SPDLOG_LOGGER_INFO(m_logger, "开启 fromRedisThread 线程,设备ID:{}", info.DeviceID); FromRedis fromRedis; fromRedis.setRedisIPAndPort(info.RedisIP, info.RedisPort); fromRedis.setRedisPassword(info.RedisPWD); if(fromRedis.connectRedis()) { SPDLOG_LOGGER_INFO(m_logger, "连接Redis成功"); }else { SPDLOG_LOGGER_ERROR(m_logger, "连接Redis失败"); return; } // std::string strKey = "117:OD210_026_005246_001-IZRTKyEx"; /* 取出该设备的Key */ std::vector vecKey; for(const auto& it : info.vecAction) { std::string strKey = std::to_string(info.DeviceID) + ":" + it; vecKey.push_back(strKey); } std::string strRetValue; while (m_threadRunning) { /* 循环读取这个设备关联的算法信息 */ for(const auto& it : vecKey) { SPDLOG_LOGGER_INFO(m_logger, "读取Redis信息, Key: {}", it); fromRedis.getRedisString(it, strRetValue); SPDLOG_LOGGER_TRACE(m_logger, "Redis Value:\n{}", strRetValue); /* 解析数据 */ AlarmInfo alarmInfo; alarmInfo.ActionID = it; parseRedisData(strRetValue, alarmInfo); } std::this_thread::sleep_for(std::chrono::seconds(1)); } return; } /* 解析Redis基础数据 */ void SPAServer::parseRedisData(const std::string& strData, AlarmInfo& alarmInfo) { try { nJson json0; json0 = nJson::parse(strData); alarmInfo.AlarmID = json0["alarmId"].get(); alarmInfo.ActionDes = json0["actionDes"].get(); alarmInfo.ChannelID = json0["channel"].get(); alarmInfo.PicUrl = json0["picUrl"].get(); /* 解析时间,需要将时间中的“T”换成空格 */ alarmInfo.StartTime = json0["beginTime"].get(); std::replace(alarmInfo.StartTime.begin(), alarmInfo.StartTime.end(), 'T', ' '); alarmInfo.EndTime = json0["endTime"].get(); std::replace(alarmInfo.EndTime.begin(), alarmInfo.EndTime.end(), 'T', ' '); alarmInfo.EventTime = json0["eventTime"].get(); std::replace(alarmInfo.EventTime.begin(), alarmInfo.EventTime.end(), 'T', ' '); /* 判断bBoxes有无数据,有数据就解析,没数据就直接返回了 */ nJson json1 = json0["bBoxes"]; if(!json1.empty()) { for(auto& it0 : json1) { /* 如果status是true,就不是报警,直接跳过 */ bool status = it0["status"].get(); if(alarmInfo.State) { continue; } /* 判断这个是不是违禁品检测的算法ID */ if(alarmInfo.ActionID == m_contrabandKey) { /* 解析报警 */ } } } alarmInfo.ActionDes = json0["actionDes"].get(); if(alarmInfo.ActionDes.empty()) { alarmInfo.ActionDes = "出现违禁品"; } } 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; } }