#include "NoiseDetectThread.h" #include #include "CreateWAVThread.h" #include "CreateLongFileThread.h" #include "ThreadAlarmManager.h" #include "ThreadManager.h" #include "commonDefine.h" #include "GlobalInfo.h" #include "signalstats.h" #include "spdlog.h" NoiseDetectThread::NoiseDetectThread(CalculateThreadInfo_t& threadInfo) : BaseCalculateThread(threadInfo), m_leftRightData(0) { m_logger = spdlog::get("ACAServer"); if(m_logger == nullptr) { fmt::print("NoiseDetectThread: ACAServer Logger not found.\n"); return; } } NoiseDetectThread::~NoiseDetectThread() { } /* 开启对比项通道的噪音报警功能 */ void NoiseDetectThread::startCompareItemNoiseAlarm(const int itemID, const QString strName, const CompareItemRoadInfo_t& compareItemRoadInfo) { std::lock_guard lock(m_mutexAlarm); /* 如果已经存在这个对比项的报警信息,就不需要重复添加了 */ if(m_mapAlarmInfo.find(itemID) != m_mapAlarmInfo.end()) { SPDLOG_LOGGER_INFO(m_logger, "{} 对比项 {} 的噪音报警功能已经开启", m_logBase, strName.toStdString()); return; } /* 添加对比项的噪音报警信息 */ AlarmInfo_t alarmInfo; alarmInfo.CompareItemID = itemID; alarmInfo.strCompareItemName = strName.toStdString(); alarmInfo.isAlarm = false; // 初始状态没有报警 alarmInfo.RoadInfo = compareItemRoadInfo; alarmInfo.AlarmType = EAlarmType::EAT_Noise; m_mapAlarmInfo[itemID] = alarmInfo; } /* 关闭对比项通道的噪音报警功能 */ void NoiseDetectThread::stopCompareItemNoiseAlarm(const int itemID, const QString strName, const CompareItemRoadInfo_t& compareItemRoadInfo) { std::lock_guard lock(m_mutexAlarm); auto it = m_mapAlarmInfo.find(itemID); if(it != m_mapAlarmInfo.end()) { SPDLOG_LOGGER_INFO(m_logger, "{} 对比项 {} 的噪音报警功能已关闭", m_logBase, strName.toStdString()); m_mapAlarmInfo.erase(it); // 移除对比项的噪音报警信息 }else { SPDLOG_LOGGER_WARN(m_logger, "{} 对比项 {} 的噪音报警功能未开启,无法关闭", m_logBase, strName.toStdString()); } } /* 线程功能函数 */ void NoiseDetectThread::task() { /* 初始化数据 */ if(!initData()) { SPDLOG_LOGGER_ERROR(m_logger, "{} 初始化数据失败", m_logBase); return; } SPDLOG_LOGGER_INFO(m_logger, " ★ {} 噪音检测线程开始运行 ", m_logBase); while(m_isRunning) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); /*------------------------------------------------------------------------ * 获取最新的左右声道数据 *------------------------------------------------------------------------*/ if(!m_pThreadWav->getLatestLeftRightData(m_leftRightData)) { continue; } // SPDLOG_LOGGER_ERROR(m_logger, "{} 获取最新的左右声道数据成功,开始调用动态库计算噪音------------------------------------------ ", m_logBase); /*------------------------------------------------------------------------ * 计算数据 *------------------------------------------------------------------------*/ if(!detectNoise()) { continue; } /*------------------------------------------------------------------------ * 处理结果,写噪音报警信息到数据库(这里不写数据库,只计算结果返回给对比项) * 上面那个函数已经把结果保存了 *------------------------------------------------------------------------*/ saveResult(); } clearData(); // 清理数据 SPDLOG_LOGGER_INFO(m_logger, " ★ {} 噪音检测线程结束 ", m_logBase); } /* 初始化数据 */ bool NoiseDetectThread::initData() { auto begin = m_threadInfo.compareItemInfo.mapRoad.begin(); // 获取第一个通道的信息 m_roadInfo = begin->scRoadInfo; // 录音通道信息 m_roadName = fmt::format("{}:{}", m_roadInfo.strSoundCardID.toStdString(), m_roadInfo.roadInfo.nRoadNum); m_logBase = fmt::format("噪音检测通道 {}:", m_roadName); /* 获取线程 */ auto now = std::chrono::steady_clock::now(); while(true) { if(m_pThreadWav == nullptr) { m_pThreadWav = ThreadMan.getCreateWAVThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum); } if(m_pThreadCreateAlarm == nullptr) { m_pThreadCreateAlarm = ThreadMan.getCreateLongFileThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum); } if(m_pThreadWav != nullptr && m_pThreadCreateAlarm != nullptr) { break; // 获取到线程了 } if(std::chrono::steady_clock::now() - now > std::chrono::seconds(10)) { SPDLOG_LOGGER_ERROR(m_logger, "{} 获取数据线程超时", m_logBase); return false; // 超时了,获取线程失败 } } auto sampleRate = GInfo.sampleRate(); m_sample_rate = static_cast(sampleRate); /* 获取噪音检测参数 */ m_noiseDetectParam = SysConfig.getNoiseDetectParam(); /* 设置噪音检测参数 */ m_ringQueueIsNoise.setQueueCapacity(m_noiseDetectParam.nNoiseDetectContinueCount); return true; } /* 清理数据 */ void NoiseDetectThread::clearData() { /* 释放资源 */ // signalstats_wrapper::finalize(); } /* 调用动态库检测噪音 */ bool NoiseDetectThread::detectNoise() { // SPDLOG_LOGGER_TRACE(m_logger, "{} 开始调用动态库计算噪音", m_logBase); SPDLOG_LOGGER_INFO(m_logger, "{} 左声道数据大小: {}, 右声道数据大小: {}", m_logBase, m_leftRightData.vecLeftData.size(), m_leftRightData.vecRightData.size()); auto startTime = std::chrono::steady_clock::now(); bool isNoiseLeft = false; /* 是否检测到左声道噪音 */ bool isNoiseRight = false; /* 是否检测到右声道噪音 */ try { /*-------------------------- 先检测左声道 --------------------------*/ nJson jsonOutput; auto ret = signalstats::detect_signal( jsonOutput, /* 返回结果,和jsonResult是一样的 */ m_leftRightData.vecLeftData, /* 左声道数据 */ m_sample_rate, /* 采样率(HZ) */ m_silence_threshold, /* 静音阈值 */ m_db_threshold, /* 分贝阈值 */ m_cv_threshold, /* 变异系数阈值 */ m_window_params, /* 窗函数参数 */ m_nperseg, /* 每段样本数 */ m_noverlap, /* 重叠样本数 */ m_nfft, /* FFT点数 */ true /* 是否输出调试信息, true表示输出调试信息, false表示不输出调试信息 */ ); isNoiseLeft = jsonOutput["noise"].is_null() ? false : jsonOutput["noise"].get(); std::chrono::duration duration = std::chrono::steady_clock::now() - startTime; std::chrono::milliseconds ms = std::chrono::duration_cast(duration); SPDLOG_LOGGER_DEBUG(m_logger, "{} 计算左声道噪音耗时: {}ms", m_logBase, ms.count()); startTime = std::chrono::steady_clock::now(); /* 重置开始时间 */ /*-------------------------- 再检测右声道 --------------------------*/ jsonOutput.clear(); /* 清空输出结果 */ signalstats::detect_signal( jsonOutput, /* 返回结果,和jsonResult是一样的 */ m_leftRightData.vecRightData, /* 右声道数据 */ m_sample_rate, /* 采样率(HZ) */ m_silence_threshold, /* 静音阈值 */ m_db_threshold, /* 分贝阈值 */ m_cv_threshold, /* 变异系数阈值 */ m_window_params, /* 窗函数参数 */ m_nperseg, /* 每段样本数 */ m_noverlap, /* 重叠样本数 */ m_nfft, /* FFT点数 */ false /* 是否输出调试信息, true表示输出调试信息, false表示不输出调试信息 */ ); isNoiseRight = jsonOutput["noise"].is_null() ? false : jsonOutput["noise"].get(); // SPDLOG_LOGGER_DEBUG(m_logger, "{} 右声道噪音检测结果: {}", m_logBase, jsonOutput.dump(4)); } catch (const std::exception& e) { SPDLOG_LOGGER_ERROR(m_logger, "{} 调用动态库检测噪音失败: {}", m_logBase, e.what()); return false; } std::chrono::duration duration = std::chrono::steady_clock::now() - startTime; std::chrono::milliseconds ms = std::chrono::duration_cast(duration); SPDLOG_LOGGER_DEBUG(m_logger, "{} 计算右声道噪音耗时: {}ms", m_logBase, ms.count()); SPDLOG_LOGGER_DEBUG(m_logger, "{} 左声道噪音检测结果: {}, 右声道噪音检测结果: {}", m_logBase, isNoiseLeft, isNoiseRight); /* -------------------------- 和以往的结果对比 --------------------------*/ m_currentIsNoise = (isNoiseLeft || isNoiseRight); /* 是否检测到噪音 */ return true; } /* 保存结果 */ void NoiseDetectThread::saveResult() { /* 将当前噪音检测结果保存到环形队列中 */ m_ringQueueIsNoise.push(m_currentIsNoise); /* 计算出噪音个数和噪音所占百分比 */ const int size = m_ringQueueIsNoise.QueueSize(); int numNoise = 0; // 噪音的个数 int numCountinueNoise = 0; // 连续噪音的个数 double percentNoise = 0.0; // 噪音所占百分比 for(int i = 0; i < size; ++i) { if(m_ringQueueIsNoise.at(i)) { numNoise++; numCountinueNoise++; }else { numCountinueNoise = 0; // 如果不是噪音,连续噪音计数清零 } } /* 判断是否是噪音预警 */ if(numCountinueNoise >= m_noiseDetectParam.nNoiseContinueCountIsWarn) { m_isNoiseWarning.store(true); } percentNoise = numNoise * 100.0 / m_noiseDetectParam.nNoiseContinueCountPercent; /* 根据噪音所占的百分比判断是否是噪音 */ if(percentNoise >= m_noiseDetectParam.nNoiseContinueCountPercent) { m_isNoise.store(true); }else { m_isNoise.store(false); } if(m_isNoise.load()) { /* 判断上次是否是噪音 */ if(!m_isNoiseLast) { /* 开始噪音报警 */ m_isNoiseWarning = true; /* 通知对比项线程,开始噪音报警 */ std::lock_guard lock(m_mutexAlarm); for(auto& pair : m_mapAlarmInfo) { AlarmInfo_t& alarmInfo = pair.second; if(!alarmInfo.isAlarm) // 如果没有报警 { alarmInfo.isAlarm = true; // 设置为报警状态 /* 向前推算噪音开始时间,开始时间需要向前推 n次噪音检测的时间 x 单次噪音检测需要的时间,单位秒 */ int nNoiseDetectTime = m_noiseDetectParam.nNoiseDetectContinueCount * m_noiseDetectParam.nNoiseDetectDuration; QDateTime startTime = m_leftRightData.startTime; startTime = startTime.addSecs(-nNoiseDetectTime); alarmInfo.StartTime = startTime; SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 开始噪音报警", m_logBase, alarmInfo.strCompareItemName, alarmInfo.RoadInfo.strCompareRoadName.toStdString()); /* 这里可以添加通知录制报警文件的线程开始录音 */ if(m_pThreadCreateAlarm != nullptr) { m_pThreadCreateAlarm->startRecordAlarmFile(alarmInfo); } } } } }else { /* 没有噪音,判断是否结束报警 */ if(m_isNoiseLast) { /* 停止噪音报警 */ m_isNoiseWarning = false; /* 通知对比项线程,结束噪音报警 */ std::lock_guard lock(m_mutexAlarm); for(auto& pair : m_mapAlarmInfo) { AlarmInfo_t& alarmInfo = pair.second; if(alarmInfo.isAlarm) // 如果正在报警 { // 设置为非报警状态 alarmInfo.isAlarm = false; /* 设置结束时间 */ alarmInfo.EndTime = m_leftRightData.endTime; SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 结束噪音报警", m_logBase, alarmInfo.strCompareItemName, alarmInfo.RoadInfo.strCompareRoadName.toStdString()); /* 这里可以添加通知录制报警文件的线程停止录音 */ if(m_pThreadCreateAlarm != nullptr) { m_pThreadCreateAlarm->stopRecordAlarmFile(alarmInfo); } /* 写入数据库,并清空时间 */ AlarmManager.addAlarmInfo(alarmInfo); alarmInfo.StartTime = QDateTime(); alarmInfo.EndTime = QDateTime(); } } } } }