#include "CompareDoubleThread.h" #include "GlobalInfo.h" #include "GlobalVariable.h" #include "GlobalInfo.h" #include "ThreadManager.h" #include "ConsistencyCompareThread.h" CompareDoubleThread::CompareDoubleThread(CalculateThreadInfo_t& compareItemInfo) : BaseCalculateThread(compareItemInfo) { m_logger = spdlog::get("ACAServer"); if(m_logger == nullptr) { fmt::print("CompareDouble: ACAServer Logger not found.\n"); return; } m_threadInfo = compareItemInfo; } CompareDoubleThread::~CompareDoubleThread() { } /* 获取计算结果 */ OneRoadVolume_t CompareDoubleThread::getVolumeInfo() { std::lock_guard lock(m_mutexVolumeInfo); return m_roadResult; } /** * @brief 获取最新的计算结果,这里目前只有一致性检测的结果,没有噪音检测的结果 * 1、这里目前没有进行时间判断,直接返回最新的结果 * * @param volumeInfo * @return true * @return false */ bool CompareDoubleThread::getlastVolumeInfo(OneRoadVolume_t& volumeInfo) { std::lock_guard lock(m_mutexVolumeInfo); volumeInfo.isConsistency = m_roadResult.isConsistency; volumeInfo.isNotConsistencyWarning = m_roadResult.isNotConsistencyWarning; return true; } /* 线程功能函数 */ void CompareDoubleThread::task() { /* 初始化数据 */ if(!initData()) { SPDLOG_LOGGER_ERROR(m_logger, "{} 初始化数据失败", m_logBase); return; } while(m_isRunning) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); /* 更新数据 */ if(!updateData()) { continue; // 数据不足,继续等待 } /* 计算音量和反相数据 */ calculateData(); /* 保存结果 */ } /* 清理数据 */ clearData(); SPDLOG_LOGGER_INFO(m_logger, "{} 计算双通道对比的线程结束", m_logBase); m_threadInfo.threadState = EThreadState::State_Stopped; // 更新线程状态 } /* 初始化数据 */ bool CompareDoubleThread::initData() { if(m_threadInfo.compareItemInfo.mapRoad.size() < 2) { SPDLOG_LOGGER_WARN(m_logger, "{} 对比项信息错误,录音通道数量小于2", m_threadInfo.compareItemInfo.strName.toStdString()); return false; } auto begin = m_threadInfo.compareItemInfo.mapRoad.begin(); m_roadInfo1 = begin->scRoadInfo; begin++; // 移动到下一个元素 m_roadInfo2 = begin->scRoadInfo; // 录音通道2信息 m_logBase = fmt::format("{} 对比项: {}:{} - {}:{}", m_threadInfo.compareItemInfo.strName.toStdString(), m_roadInfo1.strSoundCardName.toStdString(), m_roadInfo1.roadInfo.nRoadNum, m_roadInfo2.strSoundCardName.toStdString(), m_roadInfo2.roadInfo.nRoadNum); m_offsetMSeconds = GInfo.offsetMSeconds(); // 获取偏移量 m_calculateSeconds = GInfo.calculateDataSeconds(); // 获取计算需要的秒数 m_avgDBCalculateSeconds = GInfo.avgDBCalculateSeconds(); // 获取计算平均音量所需要的时长 m_silentThreshold = GInfo.silentThreshold(); // 获取静音阈值 m_sinSeconds = GInfo.sinSeconds(); // 获取正弦波计算所需要的时长 m_isAINotConsistencyAlone = GInfo.isAINotConsistencyAlone(); // 获取杭州台是否按上面的逻辑来 m_nIsSameBothMinDBWaitNum = GInfo.nIsSameBothMinDBWaitNum(); // 获取是否需要等待静音状态 /* 获取生成音量的线程 */ m_threadCreateDBPhase1 = ThreadMan.getCreateDBPhaseThread(m_roadInfo1.nSoundCardNum, m_roadInfo1.roadInfo.nRoadNum); m_threadCreateDBPhase2 = ThreadMan.getCreateDBPhaseThread(m_roadInfo2.nSoundCardNum, m_roadInfo2.roadInfo.nRoadNum); if(m_threadCreateDBPhase1 == nullptr || m_threadCreateDBPhase2 == nullptr) { SPDLOG_LOGGER_ERROR(m_logger, "{} 获取生成音量的线程失败", m_logBase); return false; } /* 获取一致性比较的线程 */ m_pConsistencyCompareThread = ThreadMan.getConsistencyCompareThread(m_roadInfo1, m_roadInfo2); return true; } /* 清理数据 */ void CompareDoubleThread::clearData() { } /* 更新数据 */ bool CompareDoubleThread::updateData() { /* 获取一致性线程计算的结果 */ m_consistencyResult = m_pConsistencyCompareThread->getConsistencyResult(); /* 获取原始的音量值 */ if(m_isUpdated1 == false) { if(m_threadCreateDBPhase1->getLatestResult(m_localData1.ringQueue) == false) { SPDLOG_LOGGER_DEBUG(m_logger, "{} 获取通道1最新数据失败", m_logBase); return false; // 没有获取到最新数据,继续等待 } m_isUpdated1 = true; } if(m_isUpdated2 == false) { if(m_threadCreateDBPhase2->getLatestResult(m_localData2.ringQueue) == false) { SPDLOG_LOGGER_DEBUG(m_logger, "{} 获取通道2最新数据失败", m_logBase); return false; // 没有获取到最新数据,继续等待 } m_isUpdated2 = true; } if(m_localData1.isEmpty() || m_localData2.isEmpty()) { SPDLOG_LOGGER_DEBUG(m_logger, "{} 队列数据不足,等待数据更新", m_logBase); return false;; } return true; } /** * 再次判断噪音和不一致 * */ void CompareDoubleThread::calculateData() { // 2、用最新数据计算音频状态:静音、过载、反相 bool isAvgDBLessMin = false; /* 平均音量是否最小 */ bool isSin1 = false; /* 通道1是否正弦波 */ bool isSin2 = false; /* 通道2是否正弦波 */ bool isSinDBEqual = false; /* 通道1和通道2的正弦波音量是否相等 */ bool isOffsetSilence1 = false; /* 通道1偏移之后是否静音 */ bool isOffsetSilence2 = false; /* 通道2偏移之后是否静音 */ OneRoadVolume_t oneRoadVolume; /* 一个录音通道的音量信息,临时结果 */ /* 保存通道2的时间 */ oneRoadVolume.dateTime = m_localData2.ringQueue.back()->startTime; /* --------------------------------------------------------------- * 这一段计算音量偏移后的静音和正弦波(正弦波也是一种噪音,防止动态库检测不出来) * (正弦波结果貌似并没有用到) * ---------------------------------------------------------------*/ { /* 获取偏移值 */ long chn1Offset = 0; long chn2Offset = 0; calculateOffset(chn1Offset, chn2Offset); // 设置音频偏移 m_localData1.setOffset(chn1Offset); m_localData2.setOffset(chn2Offset); // 计算偏移之后的静音 int nSilentEndPos = 0, nSilentEndPos2 = 0; int nSilentStartPos = 0, nSilentStartPos2 = 0; isOffsetSilence1 = m_localData1.calculateSilent(m_volumeParam, m_avgDBCalculateSeconds, nSilentStartPos, nSilentEndPos); isOffsetSilence2 = m_localData2.calculateSilent(m_volumeParam, m_avgDBCalculateSeconds, nSilentStartPos2, nSilentEndPos2); isAvgDBLessMin = m_localData1.isAvgDBLessThan(m_avgDBCalculateSeconds, m_silentThreshold) && m_localData2.isAvgDBLessThan(m_avgDBCalculateSeconds, m_silentThreshold); // 音量小时不用判定正弦波 if (!isAvgDBLessMin) { // 正弦波处理:正弦波就是一个固定的音量值,这时不做判定,直接比较音量值 // (主次通道的音量值相同,并且是一个固定的音量值时,直接判定为一致) int nLeftDB1, nRightDB1; isSin1 = m_localData1.isSinDB(m_sinSeconds, m_avgDBCalculateSeconds, nLeftDB1, nRightDB1); int nLeftDB2, nRightDB2; isSin2 = m_localData2.isSinDB(m_sinSeconds, m_avgDBCalculateSeconds, nLeftDB2, nRightDB2); if (nLeftDB1 == nLeftDB2 && nRightDB1 == nRightDB2) { isSinDBEqual = true; } } /* 恢复音频偏移,下面也没有用到这两个环形队列的地方了,所以提到前面恢复 */ m_localData1.setOffset(0); m_localData2.setOffset(0); /* 这里貌似也没用到 */ // m_data1 = m_localData1; // m_data2 = m_localData2; } /* --------------------------------------------------------------- * 和动态库检测的一致性结果进行对比 * ---------------------------------------------------------------*/ { getLatestConsistencyStatus(oneRoadVolume); } if (isOffsetSilence1 && isOffsetSilence2) { m_bBothMinDB = ESW_BothMute_TRUE; } else { if (isAvgDBLessMin) { m_bBothMinDB = ESW_BothMinDB_TRUE; } else { if (isOffsetSilence1 || isOffsetSilence2) { m_bBothMinDB = ESW_OneMinDB_FALSE; } else { m_bBothMinDB = ESW_ALL_NOT; } } m_isSin1 = isSin1; m_isSin2 = isSin2; m_isSinDBEqual = isSinDBEqual; } /* 保存计算信息,目前只保存了一致性结果和报警 */ { std::lock_guard lock(m_mutexVolumeInfo); m_roadResult.isConsistency = oneRoadVolume.isConsistency; m_roadResult.isNotConsistencyWarning = oneRoadVolume.isNotConsistencyWarning; m_roadResult.dateTime = oneRoadVolume.dateTime; } } /* 计算偏移值 */ void CompareDoubleThread::calculateOffset(long chn1Offset, long chn2Offset) { chn1Offset = 0; chn2Offset = 0; if(m_offsetMSeconds > 0) { chn1Offset = m_offsetMSeconds; } else if(m_offsetMSeconds < 0) { chn2Offset = std::abs(m_offsetMSeconds); } } /** * @brief 获取最新的一致性状态 * 1、先使用音量进行静音比对,都不是静音时,使用AI对比 * 2、是否使用AI对比由外部传入的配置决定 * * @param sendInfo * @return true * @return false */ bool CompareDoubleThread::getLatestConsistencyStatus(OneRoadVolume_t& oneRoad) { // 非对比时间时,默认为一致 if (!m_isCompareTime) { initMiniArray(); SPDLOG_LOGGER_INFO(m_logger, "{}: 非对比时间时,默认为一致", m_chnName); oneRoad.isConsistency = true; return true; } /* 使用Python对比的结果,这里没有这个功能 */ /* 如果没有开启比较线程,默认为一致 */ if (!m_isRunCompareThread) { oneRoad.isConsistency = true; SPDLOG_LOGGER_INFO(m_logger, "{}: 没有开启比较线程,默认为一致", m_chnName); return true; } /* 不做一致性检测,返回一致性 */ if (!m_consistencyParam.GetConsistencySwitch()) { SPDLOG_LOGGER_INFO(m_logger, "{}: 一致性检测关闭,不做一致性检测,返回一致性", m_chnName); oneRoad.isConsistency = true; return true; } /* 判断是否开启计算这两个通道音量值的线程 * 如果两个线程都在不在运行,无法判断,则设置成一致,一个在运行一个不在运行,则不一致 * */ if(!m_isRunCalDB1Thread && !m_isRunCalDB2Thread) { oneRoad.isConsistency = true; SPDLOG_LOGGER_INFO(m_logger, "{}: 通道指针为空,默认为一致", m_chnName); return true; } // 一个为空,一个不空判定为不一致 if (m_isRunCalDB1Thread != m_isRunCalDB2Thread) { oneRoad.isConsistency = false; SPDLOG_LOGGER_INFO(m_logger, "{}: 一个通道正在运行计算音量的线程,一个通道未在运行,判定为不一致", m_chnName); return false; } /* 上一次的一致性标记 */ m_mutexVolumeInfo.lock(); const bool lastConsistency = m_roadResult.isConsistency; m_mutexVolumeInfo.unlock(); bool bConsistency = lastConsistency; if (!m_isRunCompareThread) { /* 没有开启比对线程 */ bConsistency = false; if(m_recordDev1ID == m_recordDev2ID) { // 同一个通道肯定是一致的 bConsistency = true; SPDLOG_LOGGER_INFO(m_logger, "同一个通道肯定是一致的"); } } else if(!m_isRunCalDB1Thread && !m_isRunCalDB2Thread) { // 都没有开启,表示两者是静音 bConsistency = true; SPDLOG_LOGGER_INFO(m_logger, "都没有开启,表示两者是静音"); } else if(m_isRunCalDB1Thread != m_isRunCalDB2Thread) { // 一个开启了,一个没有开启,表示不一致 bConsistency = false; SPDLOG_LOGGER_INFO(m_logger, "一个开启了,一个没有开启,表示不一致"); }else { /* 都开启了计算音量的线程 */ /* 获取上一次的低音量信息 */ int bBothMinDB = m_bBothMinDB; addBothMiniDB(bBothMinDB); /************************************************************* * 金海林加入 * 时间: 2023年8月30日 16:09:25 * 描述: 杭州台:在其它报警时,不一致不报警且后台不进行不一致比对,在其它报警恢复后,不一致重新计时比对 if (ESW_ALL_NOT != bBothMinDB) *************************************************************/ /* m_isAINotConsistencyAlone 是外部的配置选项 */ if (ESW_ALL_NOT != bBothMinDB && !m_isAINotConsistencyAlone) /**********金海林加入结束*************/ { if (ESW_BothMinDB_TRUE == bBothMinDB) { SPDLOG_LOGGER_INFO(m_logger, "{}: 平均音量比较小时,维持当前状态不变!", m_chnName); } else if (ESW_BothMute_TRUE == bBothMinDB) { /************************************************************* * 金海林加入 * 时间: 2022年4月6日 10:58:19 * 描述: 静音判断可能存在时间偏移,所有要多等待一次对比 *************************************************************/ if (isSameBothMinDB(ESW_BothMute_TRUE)) { SPDLOG_LOGGER_INFO(m_logger, "{}: 两个都是静音,判定为一致!", m_chnName); bConsistency = true; } else { SPDLOG_LOGGER_INFO(m_logger, "{}: 两个都是静音,前3次维持当前状态不变!", m_chnName); //bConsistency = FALSE; } /**********金海林加入结束*************/ } else { /* 一个静音,一个非静音 */ /************************************************************* * 金海林修改 * 时间: 2018年11月2日 14:31:34 * 描述: 一个静音一个非静音,第一次维持不一致状态不变;第二次直接判定位不一致 *************************************************************/ if (isSameBothMinDB(ESW_OneMinDB_FALSE)) { SPDLOG_LOGGER_INFO(m_logger, "{}: 一个静音一个非静音,上次也是这样,直接判定为不一致!", m_chnName); bConsistency = false; } else { SPDLOG_LOGGER_INFO(m_logger, "{}: 一个静音一个非静音,前3次维持当前状态不变!", m_chnName); //bConsistency = false; } /**********金海林修改结束*************/ } } else { /* 两个都是静音,进一步进行一致性比较 */ /************************************************************* * 金海林加入 * 时间: 2022年3月31日 16:04:30 * 描述: 开启基础(静音正弦波判断)和AI对比 *************************************************************/ compareConsistencyBaseOnAI(bConsistency, lastConsistency); /**********金海林加入结束*************/ std::string strMsgLog; if (bConsistency) { if (oneRoad.isNotConsistencyWarning == true) { strMsgLog = fmt::format("{}:预警【一致】:{}", m_logBase, m_chnName); } else { strMsgLog = fmt::format("{}:是一致:{}", m_logBase, m_chnName); } } else { strMsgLog = fmt::format("{}:不一致:{}", m_logBase, m_chnName); } SPDLOG_LOGGER_DEBUG(m_logger, strMsgLog); } } return true; } /* 基础静音判断和AI(这里是调用的动态库)判断进行对比 */ bool CompareDoubleThread::compareConsistencyBaseOnAI(bool& bConsistency, const bool lastConsistency) { /************************************************************* * 金海林加入 * 时间: 2023年8月30日 16:09:25 * 描述: 杭州台:在其它报警时,不一致不报警且后台不进行不一致比对,在其它报警恢复后,不一致重新计时比对 *************************************************************/ if (m_isHasAlarmExecpt) { bConsistency = true; return false; } /**********金海林加入结束*************/ /* 更新动态库的一致性比对结果 */ std::string strAIValue, strAIValueNot; bool bAICon = m_consistencyResult.IsAIConsistency(GInfo.AICMPThresholdNum(), GInfo.AICMPThreshold(), strAIValue); bool bAINot = m_consistencyResult.IsAINotConsistency(GInfo.AICMPThresholdNum(), GInfo.AICMPThresholdNot(), strAIValueNot); if (false == bAICon || false == bAINot) { bConsistency = true; return false; } if (bAICon) { bConsistency = true; if (!lastConsistency) { SPDLOG_LOGGER_INFO(m_logger, "当前AI判定为一致,上次是不一致,确认为一致(第{}次),{}", ++m_arryAIChangeRetNum[1], strAIValue); } return true; } if (bAINot) { bConsistency = false; if (lastConsistency) { SPDLOG_LOGGER_INFO(m_logger, "当前AI判定为不一致,上次是一致,确认为不一致(第{}次),{}", ++m_arryAIChangeRetNum[3], strAIValueNot); } return true; } bConsistency = lastConsistency; // 如果都不是一致和不一致,则使用上一次的一致性结果 return true; } /* 初始化低音量数组 */ void CompareDoubleThread::initMiniArray() { for(auto& data : m_arrayMiniDB) { data = -1; } } /* 向低音量数组添加数字 */ int CompareDoubleThread::addBothMiniDB(int db) { for(int i = 8; i >= 0; --i) { m_arrayMiniDB[i+1] = m_arrayMiniDB[i]; } m_arrayMiniDB[0] = db; if (ESW_ALL_NOT == db) { } if (ESW_BothMinDB_TRUE == db) { SPDLOG_LOGGER_DEBUG(m_logger, "{}: 两个都是声音小,连续{}秒平均音量小于[{}]", m_chnName, m_avgDBCalculateSeconds, m_silentThreshold); SPDLOG_LOGGER_DEBUG(m_logger, "{}: 记录当前状态:两个都是声音小,连续{}秒平均音量小于[{}]", m_chnName, m_avgDBCalculateSeconds, m_silentThreshold); } if (ESW_BothMute_TRUE == db) { SPDLOG_LOGGER_DEBUG(m_logger, "{}: 两个静音({})", m_chnName, db); } if (ESW_OneMinDB_FALSE == db) { SPDLOG_LOGGER_DEBUG(m_logger, "{}: 一个静音一个非静音({})", m_chnName, db); } return 10; } /** * @brief 两个低音量是否相似 * * @param db * @return true * @return false */ bool CompareDoubleThread::isSameBothMinDB(int db) { bool bSame = true; int nThresholdNum = m_nIsSameBothMinDBWaitNum; //是否静音要等待3秒,因为音频有延时 if (nThresholdNum < 0) nThresholdNum = 1; if (nThresholdNum > 10) nThresholdNum = 10; for(int i = 0; i < nThresholdNum; ++i) { if (db != m_arrayMiniDB[i]) { bSame = false; break; } } return bSame; }