Pārlūkot izejas kodu

V0.3.3
1、完成了噪音检测线程所需数据的修改,报警录音,写日志库的验证

Apple 1 nedēļu atpakaļ
vecāks
revīzija
886f9b52f7

+ 3 - 0
RTPServer/RtpOneRoadThread.cpp

@@ -259,6 +259,9 @@ void RTPOneRoadThread::do_timerSendData()
     {
         return;
     }
+    /* 先初始化UDP */
+
+    /* 发送数据 */
     sendData();
 }
 

+ 78 - 13
RTPServer/RtpServer.cpp

@@ -1,19 +1,13 @@
 #include "RtpServer.h"
 
 #include <QTcpSocket>
-
+#include "ThreadManager.h"
 
 
 RTPServer::RTPServer(QObject *parent)
-    : QObject(parent), m_tcpServer(nullptr), m_port(8088)
+    : QObject(parent), m_tcpServer(nullptr), m_port(8808)
 {
-    m_logger = spdlog::get("RTPServer");
-    if (!m_logger) 
-    {
-        fmt::print("RTPServer: Failed to get logger instance.\n");
-        return;
-    }
-    SPDLOG_LOGGER_INFO(m_logger, "RTPServer initialized on port {}", m_port);
+    
 }
 
 RTPServer::~RTPServer()
@@ -31,8 +25,17 @@ RTPServer::~RTPServer()
  * @param port 监听端口
  * @return 成功返回true,失败返回false
  */
-bool RTPServer::startServer(int port)
+bool RTPServer::thread_task(int port)
 {
+    m_logger = spdlog::get("RTPServer");
+    if (!m_logger) 
+    {
+        fmt::print("RTPServer: Failed to get logger instance.\n");
+        return false;
+    }
+
+    SPDLOG_LOGGER_INFO(m_logger, "♫ 开启 RTPServer 服务线程");
+
     if(m_tcpServer == nullptr)
     {
         m_tcpServer = new QTcpServer(this);
@@ -42,12 +45,34 @@ bool RTPServer::startServer(int port)
     if(!m_tcpServer->listen(QHostAddress::Any, m_port))
     {
         SPDLOG_LOGGER_ERROR(m_logger, "Failed to start RTP server on port {}", m_port);
+        SPDLOG_LOGGER_ERROR(m_logger, "错误信息: {}", m_tcpServer->errorString().toStdString());
         return false;
     }
 
+    /* 开启事件循环 */
+    connect(m_tcpServer, &QTcpServer::newConnection, this, &RTPServer::do_newConnection);
+    connect(m_tcpServer, &QTcpServer::acceptError, this, &RTPServer::do_error);
+
+    m_isStoped.store(false);
+    m_eventLoop.exec();
+    m_isStoped.store(true);
+    SPDLOG_LOGGER_INFO(m_logger, "♫ RTPServer 线程结束运行");
     return true;
 }
 
+/* 停止线程 */
+void RTPServer::stopThreadBlock()
+{
+    if(m_eventLoop.isRunning())
+    {
+        m_eventLoop.quit();
+    }
+    while(!m_isStoped.load())
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+}
+
 /* 处理新连接 */
 void RTPServer::do_newConnection()
 {
@@ -64,6 +89,12 @@ void RTPServer::do_newConnection()
     m_listClients.append(clientSocket);  // 添加到客户端列表中
 }
 
+/* 监听服务错误 */
+void RTPServer::do_error(QAbstractSocket::SocketError socketError)
+{
+    SPDLOG_LOGGER_ERROR(m_logger, "RTP Server error: {}, {}", static_cast<int>(socketError), m_tcpServer->errorString().toStdString());
+}
+
 /* 断开连接 */
 void RTPServer::do_disconnect()
 {
@@ -96,11 +127,42 @@ void RTPServer::do_receiveMessage()
         return;
     }
 
+    SPDLOG_LOGGER_TRACE(m_logger, "RTPServer从: {}:{} 接收到原始数据: {}, 大小: {}", 
+                        clientSocket->peerAddress().toString().toStdString(), clientSocket->peerPort(), 
+                        data.toHex().toStdString(), data.size());
+    // SPDLOG_LOGGER_TRACE(m_logger, "RTPServer从: {}:{} 接收到数据: {}", 
+    //                     clientSocket->peerAddress().toString().toStdString(), clientSocket->peerPort(), 
+    //                     QString(data).toStdString());
+    // return;
+
+    if(data.size() < sizeof(RtpRecvClientInfo_t)) 
+    {
+        SPDLOG_LOGGER_WARN(m_logger, "从客户端接收到的数据大小: {} 低于应有的大小 {}", 
+                            clientSocket->peerAddress().toString().toStdString(), sizeof(RtpRecvClientInfo_t));
+        return;
+    }
+
+
     /* 处理接收到的数据 */
     RtpSendClientInfo_t sendInfo;
-    sendInfo.clientIP = clientSocket->peerAddress().toString();
-    sendInfo.clientPort = clientSocket->peerPort();
+    QString clientIP = clientSocket->peerAddress().toString();
+    quint16 clientPort = clientSocket->peerPort();
     RtpRecvClientInfo_t* recvInfo = reinterpret_cast<RtpRecvClientInfo_t*>(data.data());
+
+    SPDLOG_LOGGER_DEBUG(m_logger, "客户端信息: ");
+    SPDLOG_LOGGER_DEBUG(m_logger, "    客户端IP: {}", recvInfo->clientIP);
+    SPDLOG_LOGGER_DEBUG(m_logger, "    客户端端口: {}", recvInfo->clientPort);
+    SPDLOG_LOGGER_DEBUG(m_logger, "    对比项ID: {}", recvInfo->compareItemID);
+    SPDLOG_LOGGER_DEBUG(m_logger, "    对比项通道号: {}", recvInfo->compareItemRoadNum);
+    SPDLOG_LOGGER_DEBUG(m_logger, "    客户端名称: {}", recvInfo->sessionName);
+    SPDLOG_LOGGER_DEBUG(m_logger, "    包类型: {}", recvInfo->type);
+
+    /* 解析客户端发送的信息 */
+    sendInfo.clientIP = clientIP;
+    sendInfo.clientPort = clientPort;
+    /* 查找本地可用的UDP端口,这里先使用10010测试 */
+    sendInfo.localPort = 10010;
+
     switch(recvInfo->type)
     {
         case 0: // 登录请求
@@ -121,7 +183,8 @@ void RTPServer::do_receiveMessage()
 /* 处理登录请求 */
 void RTPServer::handleLogin(RtpSendClientInfo_t& clientInfo)
 {
-
+    /* 获取RTP发送线程 */
+    
 }
 
 /* 处理心跳请求 */
@@ -144,3 +207,5 @@ bool RTPServer::getSendUdpSocketPtr(SoundCardRoadInfo_t roadID)
     return true;
 }
 
+
+

+ 21 - 8
RTPServer/RtpServer.h

@@ -3,6 +3,7 @@
 
 #include <QTcpServer>
 #include <QList>
+#include <QEventLoop>
 
 #include "spdlog/spdlog.h"
 #include "Rtpcommon.h"
@@ -12,12 +13,16 @@
 
 
 /**
- * RTP监听服务,监听客户端发起的数据接受请求
- *      功能:
- *          1、监听客户端的连接请求,获取客户端发送过来的UDP端口
- *          2、创建一个RTP数据对象,负责发送数据
- *      接收的数据:
- *          0是登录,1是心跳,2是注销
+    RTP监听服务,监听客户端发起的数据接受请求
+        功能:
+            1、监听客户端的连接请求,获取客户端发送过来的UDP端口
+            2、从ThreadManager中获取RTP发送线程实例,将需要发送的对比项通道信息发送给
+                RTP发送线程,RTP发送线程会将数据发送到客户端的UDP端口
+            3、RTP发送线程使用的UDP端口从这里确定,与客户端信息一同发送过去
+        接收的数据:
+            0是登录,1是心跳,2是注销
+        登录:
+            客户端在连接成功后,发送过来的登录信息是 RtpRecvClientInfo_t 结构体中的内容。
  */
 class RTPServer : public QObject
 {
@@ -31,14 +36,19 @@ public:
      * @param port 监听端口
      * @return 成功返回true,失败返回false
      */
-    bool startServer(int port);
+    bool thread_task(int port);
+    /* 停止线程 */
+    void stopThreadBlock();
+
 
 private slots:
     /* 处理新连接 */
     void do_newConnection();
+    /* 监听服务错误 */
+    void do_error(QAbstractSocket::SocketError socketError);
+
     /* 断开连接 */
     void do_disconnect();
-
     /* 接收消息 */
     void do_receiveMessage();
 
@@ -53,8 +63,11 @@ private:
     /* 获取发送UDP数据的指针 */
     bool getSendUdpSocketPtr(SoundCardRoadInfo_t roadInfo);
 
+
 private:
     std::shared_ptr<spdlog::logger> m_logger = nullptr;     /* 日志记录器 */
+    QEventLoop m_eventLoop;                 /* 事件循环,用于处理异步操作 */
+    std::atomic_bool m_isStoped = true;     /* 线程是否已经停止标志位 */
     QTcpServer *m_tcpServer;                /* TCP服务器对象 */
     int m_port;                             /* 监听端口 */
 

+ 6 - 4
RTPServer/Rtpcommon.cpp

@@ -6,7 +6,8 @@ RtpRecvClientInfo_t::RtpRecvClientInfo_t()
 {
     memset(clientIP, 0, 32);
     clientPort = 0;
-    sessionID = 0;
+    compareItemID = 0;
+    compareItemRoadNum = 0;
     memset(sessionName, 0, 32);
     type = 0; // 默认类型为0
 }
@@ -23,7 +24,8 @@ RtpRecvClientInfo_t& RtpRecvClientInfo_t::operator=(const RtpRecvClientInfo_t& o
         strncpy(clientIP, other.clientIP, sizeof(clientIP) - 1);
         clientIP[sizeof(clientIP) - 1] = '\0';
         clientPort = other.clientPort;
-        sessionID = other.sessionID;
+        compareItemID = other.compareItemID;
+        compareItemRoadNum = other.compareItemRoadNum;
         strncpy(sessionName, other.sessionName, sizeof(sessionName) - 1);
         sessionName[sizeof(sessionName) - 1] = '\0';
         type = other.type;
@@ -49,8 +51,8 @@ RtpSendClientInfo_t& RtpSendClientInfo_t::operator=(const RtpSendClientInfo_t& o
         localPort = other.localPort;
         clientIP = other.clientIP;
         clientPort = other.clientPort;
-        sessionID = other.sessionID;
-        sessionName = other.sessionName;
+        compareItemID = other.compareItemID;
+        compareItemRoadNum = other.compareItemRoadNum;
         // udpSocket = other.udpSocket;     /* 不拷贝udp套接字 */ 
     }
     return *this;

+ 19 - 6
RTPServer/Rtpcommon.h

@@ -5,14 +5,18 @@
 #include <QUdpSocket>
 
 /**
- * @brief 接收到的客户端结构体
- * 
+    接收到的客户端结构体
+    这里的sessionID应该换成对比项ID加需要数据的对比项使用的通道信息,通过对比项ID加通道编号
+    找到对应的声卡通道
+
  */
 struct RtpRecvClientInfo_t
 {
     char clientIP[32];      /* 客户端IP */
     int clientPort;         /* 客户端接收数据的UDP端口 */
-    int sessionID;          /* 连接的SessionID */
+    // int sessionID;          /* 连接的SessionID */
+    int compareItemID;      /* 对比项ID */
+    int compareItemRoadNum; /* 对比项的通道号 */
     char sessionName[32];   /* 客户端名称 */
     int type;               /* 包类型,0:Login,1:Heart,2:Logout */
 
@@ -21,10 +25,17 @@ struct RtpRecvClientInfo_t
     RtpRecvClientInfo_t& operator=(const RtpRecvClientInfo_t& other);
 };
 
+/**
+    回复给客户端的信息,主要是服务发送RTP数据的端口
+ */
+struct RtpReplyClientInfo_t
+{
+    int localPort;          /* 本地发送数据的UDP端口 */
+};
 
 
 /**
- * @brief RTP通道包含带有UDP信息的客户端信息
+ * @brief RTP通道包含带有UDP信息的客户端信息,这个是传递给RTP发送线程的
  * 
  */
 struct RtpSendClientInfo_t
@@ -32,8 +43,10 @@ struct RtpSendClientInfo_t
     int localPort;                      /* 本地发送数据的UDP端口 */
     QString clientIP;                   /* 客户端IP */
     int clientPort;                     /* 客户端接收数据的UDP端口 */
-    int sessionID;                      /* 连接的SessionID */
-    QString sessionName;                /* 客户端名称 */
+    // int sessionID;                      /* 连接的SessionID */
+    // QString sessionName;                /* 客户端名称 */
+    int compareItemID;                  /* 对比项ID */
+    int compareItemRoadNum;             /* 对比项通道号 */
     QUdpSocket* udpSocket = nullptr;    /* 用于发送数据的UDP套接字 */
 
     RtpSendClientInfo_t();

+ 5 - 6
SQL/ACAServer.sql

@@ -21,17 +21,16 @@ SELECT *
 FROM tACADetectPeriod;
 
 
-
-#记录文件
+#报警信息
 SELECT *
-FROM tACARecordFile;
+FROM tACAAlarmInfo;
+
 
 
 
-#通道信息
-SELECT *
-FROM tACARoad;
 
+#清空报警信息内容
+DELETE FROM tACAAlarmInfo;
 
 
 

+ 34 - 4
Server/ACAServer.cpp

@@ -8,6 +8,7 @@
 #include "ThreadPool.h"
 #include "ThreadManager.h"
 #include "ThreadAlarmManager.h"
+#include "RtpServer.h"
 
 #include <QFile>
 #include <QString>
@@ -31,10 +32,12 @@ ACAServer::ACAServer()
 
 ACAServer::~ACAServer()
 {
-    // if(m_threadCompareItemManager != nullptr) {
-    //     delete m_threadCompareItemManager;
-    //     m_threadCompareItemManager = nullptr;
-    // }
+    if(m_rtpServer != nullptr)
+    {
+        m_rtpServer->stopThreadBlock();
+        delete m_rtpServer;
+        m_rtpServer = nullptr;
+    }
 }
 
 
@@ -89,6 +92,15 @@ bool ACAServer::initGlobalInfo()
         SPDLOG_LOGGER_WARN(m_logger, "更新声卡信息到WebAPI失败");
     }
 
+    /* 更新SysConfig,对比项管理线程中类中有这个函数,这里复用一下 */
+    QMap<std::string, std::string> mapSysConfig;
+    if(!m_fromWebAPI.getSystemConfig(mapSysConfig))
+    {
+        SPDLOG_LOGGER_ERROR(m_logger, "获取系统配置失败");
+        return false;
+    }
+    SysConfig.parseConfigFromDatabase(mapSysConfig);
+
     /*------------------------------------------------------------------------------------*/
     /* 初始化全局参数信息 */
     GInfo.initGlobalInfo();
@@ -101,6 +113,7 @@ bool ACAServer::initGlobalInfo()
     SPDLOG_LOGGER_INFO(m_logger, "录音参数: ");
     SPDLOG_LOGGER_INFO(m_logger, "      采样率: {}, 通道数: {}, 位深: {}", 
                         GInfo.m_sampleRate, GInfo.m_numChannels, GInfo.m_bitsPerSample);
+    SPDLOG_LOGGER_INFO(m_logger, "RTP服务TCP监听端口: {}", SysConfig.getDatabaseConfig().nListenPort);
     SPDLOG_LOGGER_INFO(m_logger, "=========================================================================================");
 
     return true;
@@ -122,6 +135,8 @@ bool ACAServer::startService()
     CPPTP.add_task(&ACAServer::thread_deleteRecordThread, this);
     /* 开启处理报警信息的线程 */
     CPPTP.add_task(&ThreadAlarmManager::thread_task, &AlarmManager);
+    /* 开启RTP监听服务线程 */
+    CPPTP.add_task(&ACAServer::thread_RTPServer, this);
 
     return true;
 }
@@ -190,6 +205,21 @@ void ACAServer::thread_deleteRecordThread()
     }
 }
 
+/* 线程函数,开启RTP监听服务,一个壳 */
+void ACAServer::thread_RTPServer()
+{
+    m_rtpServer = new RTPServer();
+    const auto dbConfig = SysConfig.getDatabaseConfig();
+    int port = 8808;
+    if(dbConfig.nListenPort != 0)
+    {
+        port = dbConfig.nListenPort;
+    }
+    /* 线程任务,会一直阻塞到这里 */
+    m_rtpServer->thread_task(port);
+
+}
+
 
 /* 处理MQTT消息 */
 void ACAServer::do_receiveMQTTMessage(const QMQTT::Message& message)

+ 7 - 0
Server/ACAServer.h

@@ -29,6 +29,9 @@
 #include "ThreadCompareItemManager.h"
 
 
+class RTPServer;
+
+
 class ACAServer : public QObject
 {
     Q_OBJECT
@@ -51,6 +54,8 @@ private:
 private:
     /* 线程函数,定时删除录音线程的实例 */
     void thread_deleteRecordThread();
+    /* 线程函数,开启RTP监听服务,一个壳 */
+    void thread_RTPServer();
 
 private slots:
     /* 处理MQTT消息 */
@@ -72,6 +77,8 @@ private:
     std::atomic_bool m_isGetWebInfoSuccess = false; /* 是否获取WebAPI信息成功 */
 
     // ThreadCompareItemManager* m_threadCompareItemManager = nullptr; /* 对比项管理线程 */
+    /* --------------------------------- RTP服务 --------------------------------- */
+    RTPServer* m_rtpServer = nullptr; /* RTP服务对象 */
 
 };
 

+ 2 - 0
Server/ThreadCalculate/CompareItemThread.cpp

@@ -442,6 +442,8 @@ bool CompareItemThread::getNoiseDetectThreads()
             SPDLOG_LOGGER_ERROR(m_logger, "{} 获取噪音检测线程失败", m_logBase);
             return false; // 获取线程失败
         }
+        /* 向噪音检测线程写入对比项通道信息 */
+        pThread->startCompareItemNoiseAlarm(m_threadInfo.compareItemInfo.nID, m_threadInfo.compareItemInfo.strName, road);
         m_mapNoiseDetectThreads.insert({road.nCompareRoadNum, pThread});
     }
 

+ 32 - 14
Server/ThreadCalculate/NoiseDetectThread.cpp

@@ -76,6 +76,7 @@ void NoiseDetectThread::task()
     }
 
     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));
@@ -95,6 +96,18 @@ void NoiseDetectThread::task()
         {
             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;
+        // }
+
 
         /*------------------------------------------------------------------------
          * 处理结果,写噪音报警信息到数据库(这里不写数据库,只计算结果返回给对比项)
@@ -161,8 +174,8 @@ void NoiseDetectThread::clearData()
 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());
+    // 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; /* 是否检测到左声道噪音 */
@@ -182,14 +195,14 @@ bool NoiseDetectThread::detectNoise()
             m_nperseg,              /* 每段样本数 */
             m_noverlap,             /* 重叠样本数 */
             m_nfft,                 /* FFT点数 */
-            true                   /* 是否输出调试信息, true表示输出调试信息, false表示不输出调试信息 */
+            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(); /* 重置开始时间 */
+        // 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(); /* 清空输出结果 */
@@ -208,8 +221,10 @@ bool NoiseDetectThread::detectNoise()
         );
         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)
     {
@@ -219,7 +234,7 @@ bool NoiseDetectThread::detectNoise()
 
     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, "{} 计算噪音耗时: {}ms", m_logBase, ms.count());
 
     SPDLOG_LOGGER_DEBUG(m_logger, "{} 左声道噪音检测结果: {}, 右声道噪音检测结果: {}", m_logBase, isNoiseLeft, isNoiseRight);
 
@@ -255,7 +270,7 @@ void NoiseDetectThread::saveResult()
     {
         m_isNoiseWarning.store(true);
     }
-    percentNoise = numNoise * 100.0 / m_noiseDetectParam.nNoiseContinueCountPercent;
+    percentNoise = numNoise * 100.0 / m_noiseDetectParam.nNoiseDetectContinueCount;
     /* 根据噪音所占的百分比判断是否是噪音 */
     if(percentNoise >= m_noiseDetectParam.nNoiseContinueCountPercent)
     {
@@ -263,8 +278,9 @@ void NoiseDetectThread::saveResult()
     }else {
         m_isNoise.store(false);
     }
-
-
+    // SPDLOG_LOGGER_INFO(m_logger, "{} 当前噪音检测结果: {}, 噪音预警: {}, 连续噪音个数: {}, 噪音所占百分比: {:.2f}%", 
+    //     m_logBase, m_currentIsNoise, m_isNoiseWarning.load(), numCountinueNoise, percentNoise);
+    /* 噪音报警信息 */
     if(m_isNoise.load())
     {
         /* 判断上次是否是噪音 */
@@ -281,12 +297,13 @@ void NoiseDetectThread::saveResult()
                 {
                     alarmInfo.isAlarm = true; // 设置为报警状态
                     /* 向前推算噪音开始时间,开始时间需要向前推 n次噪音检测的时间 x 单次噪音检测需要的时间,单位秒 */
-                    int nNoiseDetectTime = m_noiseDetectParam.nNoiseDetectContinueCount * m_noiseDetectParam.nNoiseDetectDuration;
+                    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());
+                    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)
                     {
@@ -329,5 +346,6 @@ void NoiseDetectThread::saveResult()
         }
         
     }
+    m_isNoiseLast = m_isNoise.load(); // 更新这一次的噪音检测结果
 }
 

+ 2 - 0
Server/ThreadManager/ThreadAlarmManager.cpp

@@ -93,6 +93,8 @@ void ThreadAlarmManager::task()
             {
                 SPDLOG_LOGGER_ERROR(m_logger, "写入报警信息失败");
             }
+            /* 清空报警列表 */
+            m_listAlarm.clear();
         }
 
     }

+ 1 - 43
Server/ThreadManager/ThreadCompareItemManager.cpp

@@ -132,49 +132,7 @@ bool ThreadCompareItemManager::updateBaseSettings()
         return false;
     }
     /* 将获取到的配置转换成结构体 */
-    for(auto it = baseSettings.begin(); it != baseSettings.end(); ++it)
-    {
-
-        if(Config_Base == it.key())
-        {
-            if(!SysConfig.getBaseConfigFromJson(it.value()))
-            {
-                SPDLOG_ERROR("获取基础配置失败");
-            }
-        }
-        else if(Config_CompareAI == it.key())
-        {
-            if(!SysConfig.getAICompareConfigFromJson(it.value()))
-            {
-                SPDLOG_ERROR("获取AI对比配置失败");
-            }
-        }
-        else if(Config_NoiseBase == it.key())
-        {
-            if(!SysConfig.getNoiseDetectBaseConfigFromJson(it.value()))
-            {
-                SPDLOG_ERROR("获取噪音检测基础配置失败");
-            }
-        }
-        else if(Config_NoiseParam == it.key())
-        {
-            if(!SysConfig.getNoiseDetectParamFromJson(it.value()))
-            {
-                SPDLOG_ERROR("获取噪音检测参数失败");
-            }
-        }
-        else if(Config_Database == it.key())
-        {
-            if(!SysConfig.getDatabaseConfigFromJson(it.value()))
-            {
-                SPDLOG_ERROR("获取数据库设置失败");
-            }
-        }
-        else
-        {
-            SPDLOG_DEBUG("未知的系统配置项: {}", it.key());
-        }
-    }
+    SysConfig.parseConfigFromDatabase(baseSettings);
 
     /* 检测时段单独获取 */
     QMap<int, DetectPeriodConfig_t> mapDetectConfig;

+ 2 - 1
Server/ThreadManager/ThreadCompareItemManager.h

@@ -33,9 +33,10 @@ public:
         在函数中将对比项实例插入到线程管理器中 */
     static void thread_compareItem(CalculateThreadInfo_t threadInfo);
 
-private:
     /* 更新基础设置信息,如数据库设置,噪音参数等 */
     bool updateBaseSettings();
+    
+private:
     /* 处理对比项信息,新获取的和已有的对比 */
     void processCompareItemInfo(QList<CompareItemInfo_t>& createList,
                                 QList<CompareItemInfo_t>& updateList,

+ 10 - 10
Server/ThreadManager/ThreadManager.cpp

@@ -123,16 +123,16 @@ bool ThreadManager::createRecordThread(const SoundCardRoadInfo_t& roadInfo, int
     threadInfo.threadType = EThreadType::Type_RtpSend;
     CPPTP.add_task(&ThreadManager::thread_RTPSend, threadInfo);
 
-    RTPOneRoadThread* pRtpSendThread = new RTPOneRoadThread(threadInfo);
-    if(pRtpSendThread == nullptr) 
-    {
-        SPDLOG_LOGGER_ERROR(m_logger, "{}:{} 创建发送RTP数据线程失败", roadInfo.strSoundCardName.toStdString(), roadInfo.roadInfo.nRoadNum);
-    }else 
-    {
-        CPPTP.add_task(&RTPOneRoadThread::threadTask, pRtpSendThread);
-        std::lock_guard<std::mutex> lock(m_mutexRtpSendThreads);
-        m_rtpSendThreads.push_back(pRtpSendThread);
-    }
+    // RTPOneRoadThread* pRtpSendThread = new RTPOneRoadThread(threadInfo);
+    // if(pRtpSendThread == nullptr) 
+    // {
+    //     SPDLOG_LOGGER_ERROR(m_logger, "{}:{} 创建发送RTP数据线程失败", roadInfo.strSoundCardName.toStdString(), roadInfo.roadInfo.nRoadNum);
+    // }else 
+    // {
+    //     CPPTP.add_task(&RTPOneRoadThread::threadTask, pRtpSendThread);
+    //     std::lock_guard<std::mutex> lock(m_mutexRtpSendThreads);
+    //     m_rtpSendThreads.push_back(pRtpSendThread);
+    // }
 
     /* 创建录音线程 */
     threadInfo.threadType = EThreadType::Type_RecordSrc;

+ 83 - 50
Server/ThreadRecord/CreateLongFileThread.cpp

@@ -337,7 +337,7 @@ bool CreateLongFileThread::setTodayPath(bool isNewFile)
     }
     m_todayDateRecord = today;
     /* 先检查现在的日期文件夹是否存在,因为其他线程可能已经创建了 */
-    QString todayDirName = QString("%1/%2").arg(GInfo.longWavPath()).arg(today.toString("yyyy-MM-dd"));
+    QString todayDirName = QString("%1/%2").arg(GInfo.longWavPath()).arg(m_todayDateRecord.toString("yyyy-MM-dd"));
     m_todayDir.setPath(todayDirName);
     if(!m_todayDir.exists())
     {
@@ -358,7 +358,8 @@ bool CreateLongFileThread::setTodayPath(bool isNewFile)
     m_todayDir.setPath(roadDirName);
     if(!m_todayDir.exists())
     {
-        if(!m_todayDir.mkpath(todayDirName))
+        SPDLOG_LOGGER_WARN(m_logger, "{} 记录文件目录不存在: {}", m_logBase, roadDirName.toStdString());
+        if(!m_todayDir.mkpath(roadDirName))
         {
             SPDLOG_LOGGER_ERROR(m_logger, "{} 创建目录失败: {}", m_logBase, roadDirName.toStdString());
             return false;
@@ -468,17 +469,18 @@ QString CreateLongFileThread::generateAlarmFileName(const AlarmValue_t& value, b
         setTodayAlarmPath();
 
         /* 检查这个对比项的报警文件夹是否存在 */
+        // QString itemDirName = m_todayDirAlarm.filePath(QString("CompareItemID_%1").arg(QString::number(value.numConpareItemID)));
         QString itemDirName = QString("CompareItemID_%1").arg(QString::number(value.numConpareItemID));
-        QDir itemDir = m_todayDirAlarm;
-        itemDir.cd(itemDirName);
+        QDir itemDir(m_todayDirAlarm.filePath(itemDirName));
         if(!itemDir.exists())
         {
-            if(!itemDir.mkpath(itemDirName))
+            SPDLOG_LOGGER_WARN(m_logger, "{} 对比项报警文件夹不存在: {}", m_logBase, itemDir.path().toStdString());
+            if(!itemDir.mkpath("."))
             {
-                SPDLOG_LOGGER_ERROR(m_logger, "{} 创建报警文件夹失败: {}", m_logBase, itemDirName.toStdString());
+                SPDLOG_LOGGER_ERROR(m_logger, "{} 创建对比项报警文件夹失败: {}", m_logBase, itemDir.path().toStdString());
                 return QString(); // 创建目录失败
             } else {
-                SPDLOG_LOGGER_INFO(m_logger, "{} 创建报警文件夹成功: {}", m_logBase, itemDirName.toStdString());
+                SPDLOG_LOGGER_INFO(m_logger, "{} 创建对比项报警文件夹成功: {}", m_logBase, itemDir.path().toStdString());
             }
         }
         /* 生成文件名, 格式: Alarm_RoadNum_AlarmType_yyyyMMdd_hhmmss-yyyyMMdd_hhmmss.wav
@@ -510,7 +512,7 @@ void CreateLongFileThread::writeAlarmFile()
 {
     const int writeSeconds = 2;
     const int newAlarmSeconds = 10;
-    /* 新文件,从报警时间往前推1秒写入 */
+    /* 新文件,从报警时间往前推10秒写入 */
     std::list<AudioSrcData*> newDataList;
     /* 给已经写入的问价使用 */
     std::list<AudioSrcData*> dataList;
@@ -559,48 +561,8 @@ void CreateLongFileThread::writeAlarmFile()
         /* 判断是新否是新录音的文件 */
         if(value.state == eRecordState::eAlarm_Init)
         {
-            /* 新的报警录音,生成文件名 */
-            value.fileName = generateAlarmFileName(value, true);
-            value.state = eRecordState::eAlarm_Recording; // 设置为录音状态
-            SPDLOG_LOGGER_DEBUG(m_logger, "{} 开始写入报警文件: {}", m_logBase, value.fileName.toStdString());
-            
-            /* 计算出报警开始位置在报警文件中的偏移,这个偏移值在设置结束的时候取出 */
-            for(const auto& audioData : newDataList)
-            {
-                if(audioData == nullptr || audioData->pData == nullptr || audioData->dataSize == 0)
-                {
-                    continue; // 跳过空数据
-                }
-                if(audioData->startTime <= value.startTime)
-                {
-                    /* 计算报警开始位置 */
-                    value.alarmStartPos = audioData->startTime.secsTo(value.startTime);
-                }
-            }
-            
-            /* 打开文件 */
-            QFile wavFile(value.fileName);
-            if(!wavFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
-            {
-                SPDLOG_LOGGER_ERROR(m_logger, "{} 打开报警文件失败: {}", m_logBase, value.fileName.toStdString());
-                SPDLOG_LOGGER_ERROR(m_logger, "错误原因: {}", wavFile.errorString().toStdString());
-                continue;
-            }
-            /* 写入音频数据到文件 */
-            for(auto& audioData : newDataList)
-            {
-                if(audioData == nullptr || audioData->pData == nullptr || audioData->dataSize == 0)
-                {
-                    continue; // 跳过空数据
-                }
-                auto writeSize = wavFile.write(reinterpret_cast<const char*>(audioData->pData), audioData->dataSize);
-                if(writeSize < 0)
-                {
-                    SPDLOG_LOGGER_ERROR(m_logger, "{} 写入报警文件失败: {}", m_logBase, value.fileName.toStdString());
-                    SPDLOG_LOGGER_ERROR(m_logger, "错误原因: {}", wavFile.errorString().toStdString());
-                }
-            }
-            wavFile.close();
+            /* 创建新的文件,并写入wav头文件 */
+            createNewAlarmFile(value, newDataList);
         } else
         {
             /* 已经存在的文件,直接写入新录制的文件 */
@@ -625,6 +587,9 @@ void CreateLongFileThread::writeAlarmFile()
                     SPDLOG_LOGGER_ERROR(m_logger, "{} 写入报警文件失败: {}", m_logBase, value.fileName.toStdString());
                     SPDLOG_LOGGER_ERROR(m_logger, "错误原因: {}", wavFile.errorString().toStdString());
                     break; // 写入失败,跳出循环
+                }else 
+                {
+                    value.writtenSize += writeSize; // 累加写入大小
                 }
             }
             wavFile.close();
@@ -638,6 +603,10 @@ void CreateLongFileThread::writeAlarmFile()
         AlarmValue_t& value = it->second;
         if(value.state == eRecordState::eAlarm_Stopped)
         {
+            /* 修改wav头文件,修改吸入的数据大小 */
+            value.wavHeader.setDataSize(value.writtenSize);
+            value.wavHeader.calculateDerivedFields();
+            modifyWavFileHeader(value.fileName, value.wavHeader);
             /* 已经结束的报警,修改文件名 */
             modifyFileName(value.fileName, value.fileNameEnd);
             /* 移除这个报警 */
@@ -646,7 +615,71 @@ void CreateLongFileThread::writeAlarmFile()
             ++it; // 继续下一个
         }
     }
+
+
+    /* 清空标志位 */
+    m_numNewAlarmSeconds = 0;
+    
+}
+
+
+/* 创建新的文件 */
+void CreateLongFileThread::createNewAlarmFile(AlarmValue_t& value, const std::list<AudioSrcData*>& dataList)
+{
+    /* 新的报警录音,生成文件名 */
+    value.fileName = generateAlarmFileName(value, true);
+    value.state = eRecordState::eAlarm_Recording; // 设置为录音状态
+    SPDLOG_LOGGER_DEBUG(m_logger, "{} 开始写入报警文件: {}", m_logBase, value.fileName.toStdString());
+    
+    /* 计算出报警开始位置在报警文件中的偏移,这个偏移值在设置结束的时候取出 */
+    for(const auto& audioData : dataList)
+    {
+        if(audioData == nullptr || audioData->pData == nullptr || audioData->dataSize == 0)
+        {
+            continue; // 跳过空数据
+        }
+        if(audioData->startTime <= value.startTime)
+        {
+            /* 计算报警开始位置 */
+            value.alarmStartPos = audioData->startTime.secsTo(value.startTime);
+        }
+    }
     
+    /* 打开文件 */
+    QFile wavFile(value.fileName);
+    if(!wavFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+    {
+        SPDLOG_LOGGER_ERROR(m_logger, "{} 打开报警文件失败: {}", m_logBase, value.fileName.toStdString());
+        SPDLOG_LOGGER_ERROR(m_logger, "错误原因: {}", wavFile.errorString().toStdString());
+        return;
+    }
+    /* 先写入wav头文件 */
+    value.wavHeader.setSampleRate(m_sampleRate);
+    value.wavHeader.setNumChannels(m_numChannels);
+    value.wavHeader.setBitsPerSample(m_bitsPerSample);
+    value.wavHeader.setDataSize(m_writtenSize);
+    value.wavHeader.calculateDerivedFields();
+
+    wavFile.write(reinterpret_cast<const char*>(&value.wavHeader), sizeof(WavHeader));
+
+    /* 写入音频数据到文件 */
+    for(auto& audioData : dataList)
+    {
+        if(audioData == nullptr || audioData->pData == nullptr || audioData->dataSize == 0)
+        {
+            continue; // 跳过空数据
+        }
+        auto writeSize = wavFile.write(reinterpret_cast<const char*>(audioData->pData), audioData->dataSize);
+        if(writeSize < 0)
+        {
+            SPDLOG_LOGGER_ERROR(m_logger, "{} 写入报警文件失败: {}", m_logBase, value.fileName.toStdString());
+            SPDLOG_LOGGER_ERROR(m_logger, "错误原因: {}", wavFile.errorString().toStdString());
+        }else 
+        {
+            value.writtenSize += writeSize; // 累加写入大小
+        }
+    }
+    wavFile.close();
 }
 
 

+ 6 - 0
Server/ThreadRecord/CreateLongFileThread.h

@@ -70,6 +70,10 @@ struct AlarmValue_t
     QDateTime startTime;        /* 录音开始时间 */
     QDateTime endTime;          /* 录音结束时间 */
     int alarmStartPos = 0;      /* 录音开始位置,单位:秒,报警问价的开始位置不一定是报警的开始位置 */
+
+    /* wav文件信息 */
+    WavHeader wavHeader;        /* wav文件头信息 */
+    int writtenSize = 0;        /* 已经写入的大小,单位:字节 */
     
     /*  对比项和通道信息*/
     EAlarmType alarmType;       /* 报警类型 */ 
@@ -145,6 +149,8 @@ private:
     /*===============================================================================*/
     /* 写入报警文件 */
     void writeAlarmFile();
+    /* 创建新的文件 */
+    void createNewAlarmFile(AlarmValue_t& value, const std::list<AudioSrcData*>& dataList);
     /* 生成报警文件名 */
     inline QString generateAlarmFileName(const AlarmValue_t& value, bool isNewFile);
     /* 设置今日报警文件夹 */

+ 4 - 5
Server/ThreadRecord/CreateWAVThread.cpp

@@ -389,7 +389,7 @@ bool CreateWAVThread::splitLeftRightChannel()
     }
 
     /* 分离左右声道 / 2,转换成short / 2 */
-    bool isStart = false;
+    bool isStart = true;
     AudioLeftRightData* pLeftRightData = new AudioLeftRightData(m_noiseElementDuration * m_oneSecondSize / 4);
     for(int32_t i = 0; i < m_noiseElementDuration; ++i)
     {
@@ -411,7 +411,7 @@ bool CreateWAVThread::splitLeftRightChannel()
         for(int j = 0; j < oneChannelSize; ++j)
         {
             int16_t sample = *reinterpret_cast<int16_t*>(data->pData + j * 2);
-            double sampleDouble = static_cast<double>(sample);
+            double sampleDouble = sample / 32768.f; // 转换成double类型,范围[-1.0, 1.0]
             if(j % 2 == 0) // 左声道
             {
                 pLeftRightData->vecLeftData.push_back(sampleDouble);
@@ -419,10 +419,9 @@ bool CreateWAVThread::splitLeftRightChannel()
             {
                 pLeftRightData->vecRightData.push_back(sampleDouble);
             }
-            // if(j < 20) // 只打印前10个数据
+            // if(j < 30)
             // {
             //     fmt::print(" |{} {}, short: {}, double: {:.2f}\n", static_cast<uint8_t>(*(data->pData + j)), static_cast<uint8_t>(*(data->pData + j * 2)), sample, sampleDouble);
-
             // }
             // fmt::print(" |{} {}, short: {}, double: {:.2f}", static_cast<uint8_t>(*(data->pData + j)), static_cast<uint8_t>(*(data->pData + 1001)), sample, sampleDouble);
         }
@@ -454,7 +453,7 @@ bool CreateWAVThread::splitLeftRightChannel()
     m_queueLeftRightData.mutex.unlock();
     if(tmp != nullptr)
     {
-        SPDLOG_LOGGER_INFO(m_logger, "{} 左右声道环形队列已满,出队一个元素", m_logBase);
+        // SPDLOG_LOGGER_TRACE(m_logger, "{} 左右声道环形队列已满,出队一个元素", m_logBase);
         delete tmp; // 删除出队的元素
         tmp = nullptr;
     }

+ 6 - 6
Server/ThreadRecord/RecordThread.cpp

@@ -91,12 +91,12 @@ void RecordThread::task()
         {
             m_assignThread->setSrcData(m_pRecordBuffer, m_recordBufferSize, currentTime);
         }
-        for(int i = 0; i < 30; ++i)
-        {
-            /* 这里可以添加对音频数据的处理,比如噪音检测等 */
-            fmt::print(" |{} {}, short: {}\n", static_cast<uint8_t>(*(m_pRecordBuffer + i*2)), static_cast<uint8_t>(*(m_pRecordBuffer + i*2+1)), 
-            static_cast<int16_t>(*(m_pRecordBuffer + i * 2)));
-        }
+        // for(int i = 0; i < 30; ++i)
+        // {
+        //     /* 这里可以添加对音频数据的处理,比如噪音检测等 */
+        //     fmt::print(" |{} {}, short: {}\n", static_cast<uint8_t>(*(m_pRecordBuffer + i*2)), static_cast<uint8_t>(*(m_pRecordBuffer + i*2+1)), 
+        //     static_cast<int16_t>(*(m_pRecordBuffer + i * 2)));
+        // }
         memset(m_pRecordBuffer, 0, m_recordBufferSize);  /* 清空缓存,准备下一次录音 */
         // SPDLOG_LOGGER_DEBUG(m_logger, "{} 录音数据已分派给AssignSrcDataThread线程", m_logBase);
     }

+ 58 - 0
common/DataManager/SystemConfig.cpp

@@ -4,6 +4,64 @@
 #include "commonDefine.h"
 #include <string>
 
+
+
+
+
+
+/* 解析设置从数据库读取到的配置信息 */
+void SystemConfigInfo::parseConfigFromDatabase(const QMap<std::string, std::string>& baseSettings)
+{
+    /* 将获取到的配置转换成结构体 */
+    for(auto it = baseSettings.begin(); it != baseSettings.end(); ++it)
+    {
+
+        if(Config_Base == it.key())
+        {
+            if(!SysConfig.getBaseConfigFromJson(it.value()))
+            {
+                SPDLOG_ERROR("获取基础配置失败");
+            }
+        }
+        else if(Config_CompareAI == it.key())
+        {
+            if(!SysConfig.getAICompareConfigFromJson(it.value()))
+            {
+                SPDLOG_ERROR("获取AI对比配置失败");
+            }
+        }
+        else if(Config_NoiseBase == it.key())
+        {
+            if(!SysConfig.getNoiseDetectBaseConfigFromJson(it.value()))
+            {
+                SPDLOG_ERROR("获取噪音检测基础配置失败");
+            }
+        }
+        else if(Config_NoiseParam == it.key())
+        {
+            if(!SysConfig.getNoiseDetectParamFromJson(it.value()))
+            {
+                SPDLOG_ERROR("获取噪音检测参数失败");
+            }
+        }
+        else if(Config_Database == it.key())
+        {
+            if(!SysConfig.getDatabaseConfigFromJson(it.value()))
+            {
+                SPDLOG_ERROR("获取数据库设置失败");
+            }
+        }
+        else
+        {
+            SPDLOG_DEBUG("未知的系统配置项: {}", it.key());
+        }
+    }
+
+}
+
+
+
+
 /* 设置基础配置 */
 void SystemConfigInfo::setBaseConfig(const BaseConfig_t& config)
 {

+ 3 - 0
common/DataManager/SystemConfig.h

@@ -84,6 +84,9 @@ public:
         {eSystemConfigType::eSCT_SoundCardInfo, "系统的声卡信息"}
     };
 
+    /* 解析设置从数据库读取到的配置信息 */
+    void parseConfigFromDatabase(const QMap<std::string, std::string>& baseSettings);
+
     /*-------------------------------------------------------------------------------------*/
     /* 获取未被修改的基础配置 */
     const BaseConfig_t& getBaseConfigSrc() const { return m_baseConfigSrc; }

+ 1 - 0
show3/CMakeLists.txt

@@ -39,6 +39,7 @@ target_include_directories(${this_exe} PRIVATE
     ${CMAKE_SOURCE_DIR}/External/common
     ${CMAKE_SOURCE_DIR}/External/module
     ${CMAKE_SOURCE_DIR}/External/module/ThreadPool
+    ${CMAKE_SOURCE_DIR}/External/module/RingQueue
     ${CMAKE_SOURCE_DIR}/ThreeLib/signalstats/include
     
     ${spdlog_INCLUDE_DIR}

+ 20 - 4
show3/main.cpp

@@ -4,24 +4,27 @@
 #include <nlohmann/json.hpp>
 #include "signalstats.h"
 #include "ThreadPool.h"
+#include "RingQueueManualMutex.hpp"
+#include "spdlog/spdlog.h"
 
 
 void test1();
 void test2();
+void test3();
 
 int main() {
 
     // 初始化Python解释器
     // signalstats_wrapper::initialize();
 
-    CPPTP.add_task(test1);
+    // CPPTP.add_task(test1);
     // std::this_thread::sleep_for(std::chrono::seconds(1));
     // CPPTP.add_task(test2);
     // std::this_thread::sleep_for(std::chrono::seconds(5));
     
     
-
-    std::this_thread::sleep_for(std::chrono::seconds(10)); // 等待任务执行
+    test3();
+    std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待任务执行
 
     // signalstats_wrapper::finalize();
     
@@ -142,4 +145,17 @@ void test2()
     }
 
     // signalstats_wrapper::finalize();
-}
+}
+
+
+/* 测试环形队列 */
+void test3()
+{
+    RingQueueManualMutex<int> queue(10);
+    for(int i = 1; i <= 20; ++i)
+    {
+        int pop = queue.push(i);
+        SPDLOG_INFO("Push: {}, pop:{}, Queue Size: {}", i, pop, queue.QueueSize());
+    }
+}
+