123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- #include "NoiseDetectThread.h"
- #include <random>
- #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<std::mutex> 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<std::mutex> 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<double>(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<bool>();
- // std::chrono::duration<double> duration = std::chrono::steady_clock::now() - startTime;
- // std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(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<bool>();
- // 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<double> duration = std::chrono::steady_clock::now() - startTime;
- std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(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<std::mutex> 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<std::mutex> 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<std::mutex> 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;
- }
|