#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); // std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now(); while(m_isRunning) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); /* 判断是否还在噪音检测时间段内,如果还在噪音报警中, 则继续保存数据 */ if(m_isInDetectPeriod.load() == false) { m_currentIsNoise = false; if(m_currentAlarmInfo.isAlarm) { saveResultOnlyOneItem(); }else { m_isNoise.store(false); m_isNoiseWarning.store(false); } continue; } /*------------------------------------------------------------------------ * 获取最新的左右声道数据 *------------------------------------------------------------------------*/ if(!m_pThreadWav->getLatestLeftRightData(m_leftRightData)) { continue; } // SPDLOG_LOGGER_ERROR(m_logger, "{} 获取最新的左右声道数据成功,开始调用动态库计算噪音------------------------------------------ ", m_logBase); /*------------------------------------------------------------------------ * 计算数据 *------------------------------------------------------------------------*/ if(!detectNoise()) { continue; } /* 这里模拟报警 */ // auto now = std::chrono::system_clock::now(); // if(now - startTime > std::chrono::seconds(10) && now - startTime < std::chrono::seconds(20)) // { // SPDLOG_LOGGER_WARN(m_logger, "{} 模拟噪音开始", m_logBase); // m_currentIsNoise = true; // 模拟噪音检测到 // }else // { // m_currentIsNoise = false; // } /*------------------------------------------------------------------------ * 处理结果,写噪音报警信息到数据库(这里不写数据库,只计算结果返回给对比项) * 上面那个函数已经把结果保存了 *------------------------------------------------------------------------*/ // saveResult(); saveResultOnlyOneItem(); } 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() { /* 判断是否还在报警中,如果是,则结束报警 */ endAlarm(); } /* 调用动态库检测噪音 */ 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点数 */ false /* 是否输出调试信息, 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["silence_state"].dump(4)); // 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.nNoiseDetectContinueCount; // /* 根据噪音所占的百分比判断是否是噪音 */ // if(percentNoise >= m_noiseDetectParam.nNoiseContinueCountPercent) // { // m_isNoise.store(true); // }else { // m_isNoise.store(false); // } // // SPDLOG_LOGGER_INFO(m_logger, "{} 当前噪音检测结果: {}, 噪音预警: {}, 连续噪音个数: {}, 噪音所占百分比: {:.2f}%", // // m_logBase, m_currentIsNoise, m_isNoiseWarning.load(), numCountinueNoise, percentNoise); // /* 噪音报警信息 */ // 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 * 1; // 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(), // startTime.toString("yyyy-MM-dd hh:mm:ss").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(); // } // } // } // } // m_isNoiseLast = m_isNoise.load(); // 更新这一次的噪音检测结果 // } /* 新的保存结果函数,只有一个对比项信息 */ void NoiseDetectThread::saveResultOnlyOneItem() { /* 将当前噪音检测结果保存到环形队列中 */ 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.nNoiseDetectContinueCount; /* 根据噪音所占的百分比判断是否是噪音 */ if(percentNoise >= m_noiseDetectParam.nNoiseContinueCountPercent) { m_isNoise.store(true); }else { m_isNoise.store(false); } // SPDLOG_LOGGER_INFO(m_logger, "{} 当前噪音检测结果: {}, 噪音预警: {}, 连续噪音个数: {}, 噪音所占百分比: {:.2f}%", // m_logBase, m_currentIsNoise, m_isNoiseWarning.load(), numCountinueNoise, percentNoise); /* 噪音报警信息 */ if(m_isNoise.load()) { /* 判断上次是否是噪音 */ if(!m_isNoiseLast) { /* 开始噪音报警 */ m_isNoiseWarning = true; /* 通知对比项线程,开始噪音报警 */ if(!m_currentAlarmInfo.isAlarm) // 如果没有报警 { m_currentAlarmInfo.isAlarm = true; // 设置为报警状态 m_currentAlarmInfo.CompareItemID = m_threadInfo.compareItemInfo.nID; m_currentAlarmInfo.strCompareItemName = m_threadInfo.compareItemInfo.strName.toStdString(); m_currentAlarmInfo.RoadInfo = m_threadInfo.compareItemInfo.mapRoad.first(); m_currentAlarmInfo.AlarmType = EAlarmType::EAT_Noise; /* 向前推算噪音开始时间,开始时间需要向前推 n次噪音检测的时间 x 单次噪音检测需要的时间,单位秒 */ int nNoiseDetectTime = m_noiseDetectParam.nNoiseDetectContinueCount * 1; QDateTime startTime = m_leftRightData.startTime; startTime = startTime.addSecs(-nNoiseDetectTime); m_currentAlarmInfo.StartTime = startTime; SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 开始噪音报警, 报警开始时间: {}", m_logBase, m_currentAlarmInfo.strCompareItemName, m_currentAlarmInfo.RoadInfo.strCompareRoadName.toStdString(), startTime.toString("yyyy-MM-dd hh:mm:ss").toStdString()); /* 这里可以添加通知录制报警文件的线程开始录音 */ if(m_pThreadCreateAlarm != nullptr) { m_pThreadCreateAlarm->startRecordAlarmFile(m_currentAlarmInfo); } } } }else { /* 没有噪音,判断是否结束报警 */ if(m_isNoiseLast) { /* 停止噪音报警 */ m_isNoiseWarning = false; /* 通知对比项线程,结束噪音报警 */ std::lock_guard lock(m_mutexAlarm); if(m_currentAlarmInfo.isAlarm) // 如果正在报警 { // 设置为非报警状态 m_currentAlarmInfo.isAlarm = false; /* 设置结束时间 */ m_currentAlarmInfo.EndTime = m_leftRightData.endTime; SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 结束噪音报警", m_logBase, m_currentAlarmInfo.strCompareItemName, m_currentAlarmInfo.RoadInfo.strCompareRoadName.toStdString()); /* 这里可以添加通知录制报警文件的线程停止录音 */ if(m_pThreadCreateAlarm != nullptr) { m_pThreadCreateAlarm->stopRecordAlarmFile(m_currentAlarmInfo); } /* 写入数据库,并清空时间 */ AlarmManager.addAlarmInfo(m_currentAlarmInfo); m_currentAlarmInfo = AlarmInfo_t(); // 清空当前报警信息 } } } m_isNoiseLast = m_isNoise.load(); // 更新这一次的噪音检测结果 } /* 结束报警 */ void NoiseDetectThread::endAlarm() { if(m_currentAlarmInfo.isAlarm) // 如果正在报警 { // 设置为非报警状态 m_currentAlarmInfo.isAlarm = false; /* 设置结束时间 */ m_currentAlarmInfo.EndTime = m_leftRightData.endTime; SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 结束噪音报警", m_logBase, m_currentAlarmInfo.strCompareItemName, m_currentAlarmInfo.RoadInfo.strCompareRoadName.toStdString()); /* 这里可以添加通知录制报警文件的线程停止录音 */ if(m_pThreadCreateAlarm != nullptr) { m_pThreadCreateAlarm->stopRecordAlarmFile(m_currentAlarmInfo); } /* 写入数据库,并清空时间 */ AlarmManager.addAlarmInfo(m_currentAlarmInfo); m_currentAlarmInfo = AlarmInfo_t(); // 清空当前报警信息 } m_isNoiseWarning.store(false); m_isNoise.store(false); m_isNoiseLast = false; m_currentIsNoise = false; }