Преглед изворни кода

V0.5.1
1、添加了RTP心跳功能
2、应用了一些动态库数据信息

Apple пре 1 дан
родитељ
комит
197c09c802

+ 1 - 0
CMakeLists.txt

@@ -143,6 +143,7 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS
     Core
     # SerialPort
     Network
+    Multimedia
     REQUIRED
 )
 

+ 5 - 6
RTPServer/RtpOneRoadThread.cpp

@@ -86,16 +86,15 @@ bool RTPOneRoadThread::addUdpSession(const RtpSendClientInfo_t& udpSession)
     SPDLOG_LOGGER_INFO(m_logger, "{} 添加UDP会话: {}:{}", m_logBase, udpSession.clientIP.toStdString(), udpSession.clientPort);
 
     /* 绑定UDP本地端口 */
-    if(m_localPort < 0)
+    if(m_udpState == eUDPState::eUDP_None)
     {
         m_udpState = eUDPState::eUDP_Init;
         m_localIP = udpSession.localIP;
-        m_localPort = udpSession.localPort;
+        // m_localPort = udpSession.localPort;
     }
     
-    m_lockClients.lock();
+    std::lock_guard<std::mutex> lock(m_lockClients);
     m_listClients.append(udpSession);
-    m_lockClients.unlock();
 
     return true;
 }
@@ -248,9 +247,9 @@ bool RTPOneRoadThread::processUdpState()
         /* 清空连接信息 */
         m_listClients.clear();
         m_localIP.clear();
-        m_localPort = -1;
+        // m_localPort = -1;
         emit signal_udpClosed(m_threadInfo.cardRoadInfo.nSoundCardNum, 
-                              m_threadInfo.cardRoadInfo.roadInfo.nRoadNum, m_localPort);
+                              m_threadInfo.cardRoadInfo.roadInfo.nRoadNum);
         /* 清空环形队列中的数据 */
         while(m_ringQueue.QueueSize() > 0)
         {

+ 4 - 5
RTPServer/RtpOneRoadThread.h

@@ -9,8 +9,7 @@
 #include <QEventLoop>
 #include <QUdpSocket>
 #include <QTimer>
-#include <qobject.h>
-#include <qobjectdefs.h>
+#include <QObject>
 #include <mutex>
 
 
@@ -26,6 +25,7 @@
         3、这个线程在打开一个录音通道后就会创建,在后台运行,直到RTP服务设置进来一个UDP会话
         4、这个线程类会被 RTPServer 线程和 AssignSrcData 线程调用
         5、这个线程添加会话后会占用一个本地的UDP端口,所有会话移除掉后会释放这个端口
+            注:UDP端口是随机分配的,目前不需要指定
  
  */
 class RTPOneRoadThread : public QObject, public BaseRecordThread
@@ -48,7 +48,7 @@ public:
 
 signals:
     /* 一个UDP关闭了,通知RTP服务,释放掉了一个本地端口 */
-    void signal_udpClosed(int soundCardNum, int roadNum, int localPort);
+    void signal_udpClosed(int soundCardNum, int roadNum);
 
 protected:
     /* 任务函数 */
@@ -60,7 +60,6 @@ protected:
 
     /* 处理UDP状态 */
     bool processUdpState();
-    /*  */
     /* 发送数据 */
     bool sendData();
 
@@ -78,7 +77,7 @@ private:
 
     QUdpSocket* m_udpSocket = nullptr;              /* 用于发送数据的UDP套接字 */
     QString m_localIP;                              /* 本地IP */
-    int m_localPort = -1;                           /* 本地发送数据的UDP端口 */
+    // int m_localPort = -1;                           /* 本地发送数据的UDP端口 */
     eUDPState m_udpState = eUDPState::eUDP_None;    /* UDP状态 */
     /* UDP会话列表 */
     std::atomic_bool m_isRecvData = false;          /* 是否接收数据的标志 */

+ 88 - 36
RTPServer/RtpServer.cpp

@@ -58,13 +58,20 @@ bool RTPServer::thread_task(int port)
     m_localIP = SysConfig.getBaseConfigSrc().strServerIP;
     if(m_localIP.isEmpty())
     {
-        SPDLOG_LOGGER_ERROR(m_logger, "未设置服务器出口IP,无法监听客户端连接");
+        SPDLOG_LOGGER_ERROR(m_logger, "未设置服务器监听IP,无法监听客户端连接");
     }
-
-    /* 开启事件循环 */
     connect(m_tcpServer, &QTcpServer::newConnection, this, &RTPServer::do_newConnection);
     connect(m_tcpServer, &QTcpServer::acceptError, this, &RTPServer::do_error);
 
+    /* 开启心跳定时器 */
+    m_timerHeartbeat.setTimerType(Qt::PreciseTimer);
+    m_timerHeartbeat.setSingleShot(false);
+    m_timerHeartbeat.setInterval(1000);
+    connect(&m_timerHeartbeat, &QTimer::timeout, this, &RTPServer::do_heartbeatTimeout);
+
+    m_timerHeartbeat.start();
+
+    /* 开启事件循环 */
     m_isStoped.store(false);
     m_eventLoop.exec();
     m_isStoped.store(true);
@@ -148,10 +155,9 @@ void RTPServer::do_receiveMessage()
     //                     QString(data).toStdString());
     // return;
 
-    if(static_cast<unsigned long>(data.size()) < sizeof(RtpRecvClientInfo_t)) 
+    if(static_cast<unsigned long>(data.size()) != sizeof(RtpRecvClientInfo_t)) 
     {
-        SPDLOG_LOGGER_WARN(m_logger, "从客户端接收到的数据大小: {} 低于应有的大小 {}", 
-                            data.size(), sizeof(RtpRecvClientInfo_t));
+        SPDLOG_LOGGER_WARN(m_logger, "从客户端接收到的数据大小: {} 不等于应有的大小 {}", data.size(), sizeof(RtpRecvClientInfo_t));
         return;
     }
 
@@ -165,20 +171,20 @@ void RTPServer::do_receiveMessage()
     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 = recvInfo->clientIP;
     sendInfo.clientPort = recvInfo->clientPort;
-    sendInfo.compareItemID = recvInfo->compareItemID;
-    sendInfo.compareItemRoadNum = recvInfo->compareItemRoadNum;
+    // sendInfo.compareItemID = recvInfo->compareItemID;
+    // sendInfo.compareItemRoadNum = recvInfo->compareItemRoadNum;
     /* 根据对比项ID和通道编号查找到使用到的声卡通道编号 */
-    SoundCardRoadInfo_t roadInfo = CompareItemManager.getSoundCardRoadInfo(sendInfo.compareItemID, sendInfo.compareItemRoadNum);
+    SoundCardRoadInfo_t roadInfo = CompareItemManager.getSoundCardRoadInfo(recvInfo->compareItemID, recvInfo->compareItemRoadNum);
     if(roadInfo.nSoundCardNum < 0 || roadInfo.roadInfo.nRoadNum < 0)
     {
         SPDLOG_LOGGER_ERROR(m_logger, "无法获取对比项 {} 通道 {} 的声卡信息", 
-                            sendInfo.compareItemID, sendInfo.compareItemRoadNum);
+                            recvInfo->compareItemID, recvInfo->compareItemRoadNum);
         return;
     }
     sendInfo.SoundCardNum = roadInfo.nSoundCardNum;
@@ -201,23 +207,51 @@ void RTPServer::do_receiveMessage()
     }
 }
 
-/* 接收到释放端口的信号 */
-void RTPServer::do_udpClosed(int soundCardNum, int roadNum, int localPort)
+/* 一个通道UDP关闭的信号 */
+void RTPServer::do_udpClosed(int soundCardNum, int roadNum)
 {
-    SPDLOG_LOGGER_DEBUG(m_logger, "接收到释放本地端口的信号: 声卡编号: {}, 通道编号: {}, 本地端口: {}", 
-                        soundCardNum, roadNum, localPort);
+    SPDLOG_LOGGER_DEBUG(m_logger, "接收到释放本地端口的信号: 声卡编号: {}, 通道编号: {}", 
+                        soundCardNum, roadNum);
+    
+    // for(auto it = m_mapSoundCardRoadPorts.begin(); it != m_mapSoundCardRoadPorts.end(); ++it)
+    // {
+    //     if(it.key().nSoundCardNum == soundCardNum && it.key().nRoadNum == roadNum)
+    //     {
+    //         // SPDLOG_LOGGER_DEBUG(m_logger, "从声卡通道列表中删除: 声卡编号: {}, 通道编号: {}, 本地端口: {}", 
+    //         //                     soundCardNum, roadNum, localPort);
+    //         m_mapSoundCardRoadPorts.remove(it.key());
+    //         break;
+    //     }
+    // }
     
-    for(auto it = m_mapSoundCardRoadPorts.begin(); it != m_mapSoundCardRoadPorts.end(); ++it)
+}
+
+/* 处理心跳超时 */
+void RTPServer::do_heartbeatTimeout()
+{
+    const QDateTime currentTime = QDateTime::currentDateTime();
+    QList<RtpClientKey_t> keysToRemove;
+
+    for (auto it = m_mapClientHeartbeats.begin(); it != m_mapClientHeartbeats.end(); ++it) 
     {
-        if(it.key().nSoundCardNum == soundCardNum && it.key().nRoadNum == roadNum)
+        if (it.value().secsTo(currentTime) > 20) // 超过30秒未收到心跳
         {
-            // SPDLOG_LOGGER_DEBUG(m_logger, "从声卡通道列表中删除: 声卡编号: {}, 通道编号: {}, 本地端口: {}", 
-            //                     soundCardNum, roadNum, localPort);
-            m_mapSoundCardRoadPorts.remove(it.key());
-            break;
+            SPDLOG_LOGGER_WARN(m_logger, "客户端 {}:{} 心跳超时,准备移除", 
+                               it.key().clientIP.toStdString(), it.key().clientPort);
+            keysToRemove.append(it.key());
         }
     }
-    
+
+    /* 注销超时的会话 */
+    for (const auto& key : keysToRemove) 
+    {
+        RtpSendClientInfo_t clientInfo;
+        clientInfo.clientIP = key.clientIP;
+        clientInfo.clientPort = key.clientPort;
+        clientInfo.SoundCardNum = key.SoundCardNum;
+        clientInfo.SoundCardRoadNum = key.SoundCardRoadNum;
+        handleLogout(clientInfo);
+    }
 }
 
 /* 处理登录请求 */
@@ -225,7 +259,7 @@ void RTPServer::handleLogin(RtpSendClientInfo_t& clientInfo)
 {
     SPDLOG_LOGGER_TRACE(m_logger, "处理登录请求: {}:{}", clientInfo.clientIP.toStdString(), clientInfo.clientPort);
     /* 查找本地可用的UDP端口,这里先使用10010测试 */
-    clientInfo.localPort = 10010;
+    // clientInfo.localPort = 10010;
     clientInfo.localIP = m_localIP;
     /* 查找RTP发送线程 */
     auto pThread = ThreadMan.getRtpSendThread(clientInfo.SoundCardNum, clientInfo.SoundCardRoadNum);
@@ -241,25 +275,30 @@ void RTPServer::handleLogin(RtpSendClientInfo_t& clientInfo)
         return;
     }
     connect(pThread, &RTPOneRoadThread::signal_udpClosed, this, &RTPServer::do_udpClosed);
-    /* 将本地端口添加到声卡通道列表中 */
-    for(auto it = m_mapSoundCardRoadPorts.begin(); it != m_mapSoundCardRoadPorts.end(); ++it)
-    {
-        if(it.key().nSoundCardNum == clientInfo.SoundCardNum && it.key().nRoadNum == clientInfo.SoundCardRoadNum)
-        {
-            return;
-        }
-    }
-    SoundCardRoadKey_t roadInfo;
-    roadInfo.nSoundCardNum = clientInfo.SoundCardNum;
-    roadInfo.nRoadNum = clientInfo.SoundCardRoadNum;
-    m_mapSoundCardRoadPorts.insert(roadInfo, clientInfo.localPort);
+    /* 将本地端口添加到心跳列表中 */
+    RtpClientKey_t clientKey;
+    clientKey.clientIP = clientInfo.clientIP;
+    clientKey.clientPort = clientInfo.clientPort;
+    clientKey.SoundCardNum = clientInfo.SoundCardNum;
+    clientKey.SoundCardRoadNum = clientInfo.SoundCardRoadNum;
+    m_mapClientHeartbeats[clientKey] = QDateTime::currentDateTime();
 }
 
 /* 处理心跳请求 */
 void RTPServer::handleHeartbeat(RtpSendClientInfo_t& clientInfo)
 {
     SPDLOG_LOGGER_TRACE(m_logger, "处理心跳请求: {}:{}", clientInfo.clientIP.toStdString(), clientInfo.clientPort);
-    
+    RtpClientKey_t clientKey;
+    clientKey.clientIP = clientInfo.clientIP;
+    clientKey.clientPort = clientInfo.clientPort;
+    clientKey.SoundCardNum = clientInfo.SoundCardNum;
+    clientKey.SoundCardRoadNum = clientInfo.SoundCardRoadNum;
+    /* 更新心跳时间 */
+    if(m_mapClientHeartbeats.contains(clientKey))
+    {
+        m_mapClientHeartbeats[clientKey] = QDateTime::currentDateTime();
+        SPDLOG_LOGGER_TRACE(m_logger, "更新心跳时间: {}:{}", clientInfo.clientIP.toStdString(), clientInfo.clientPort);
+    }
 }
 
 /* 处理注销请求 */
@@ -281,6 +320,19 @@ void RTPServer::handleLogout(RtpSendClientInfo_t& clientInfo)
         SPDLOG_LOGGER_ERROR(m_logger, "删除UDP会话失败: {}:{}", clientInfo.clientIP.toStdString(), clientInfo.clientPort);
         return;
     }
+
+    /* 将客户端信息从心跳列表中移除 */
+    RtpClientKey_t clientKey;
+    clientKey.clientIP = clientInfo.clientIP;
+    clientKey.clientPort = clientInfo.clientPort;
+    clientKey.SoundCardNum = clientInfo.SoundCardNum;
+    clientKey.SoundCardRoadNum = clientInfo.SoundCardRoadNum;
+    if(m_mapClientHeartbeats.contains(clientKey))
+    {
+        m_mapClientHeartbeats.remove(clientKey);
+        SPDLOG_LOGGER_DEBUG(m_logger, "从心跳列表中移除客户端: {}:{}", 
+                            clientInfo.clientIP.toStdString(), clientInfo.clientPort);
+    }
 }
 
 

+ 8 - 3
RTPServer/RtpServer.h

@@ -4,6 +4,7 @@
 #include <QTcpServer>
 #include <QList>
 #include <QEventLoop>
+#include <QTimer>
 
 #include "spdlog/spdlog.h"
 #include "Rtpcommon.h"
@@ -53,7 +54,10 @@ private slots:
     void do_receiveMessage();
 
     /* 接收到释放端口的信号 */
-    void do_udpClosed(int soundCardNum, int roadNum, int localPort);
+    void do_udpClosed(int soundCardNum, int roadNum);
+
+    /* 处理心跳超时 */
+    void do_heartbeatTimeout();
 
 private:
     /* 处理登录请求 */
@@ -76,8 +80,9 @@ private:
     QString m_localIP;                      /* 本地IP地址,给UDP使用,指定本地出口IP,防止自动连接到 0.0.0.0 上 */
     QList<QTcpSocket*> m_listClients;       /* 客户端连接列表,用于管理多个客户端连接 */
 
-    /* 录音通道线程列表,键为录音通道ID,值为通道使用到的本地端口 */
-    QMap<SoundCardRoadKey_t, int> m_mapSoundCardRoadPorts;
+    /* 录音通道线程列表,键为录音通道ID,值为上次的心跳时间 */
+    QMap<RtpClientKey_t, QDateTime> m_mapClientHeartbeats;
+    QTimer m_timerHeartbeat;                /* 心跳定时器,用于检测客户端连接状态 */
 };
 
 

+ 31 - 5
RTPServer/Rtpcommon.cpp

@@ -8,7 +8,7 @@ RtpRecvClientInfo_t::RtpRecvClientInfo_t()
     clientPort = 0;
     compareItemID = 0;
     compareItemRoadNum = 0;
-    memset(sessionName, 0, 32);
+    // memset(sessionName, 0, 32);
     type = 0; // 默认类型为0
 }
 
@@ -26,8 +26,8 @@ RtpRecvClientInfo_t& RtpRecvClientInfo_t::operator=(const RtpRecvClientInfo_t& o
         clientPort = other.clientPort;
         compareItemID = other.compareItemID;
         compareItemRoadNum = other.compareItemRoadNum;
-        strncpy(sessionName, other.sessionName, sizeof(sessionName) - 1);
-        sessionName[sizeof(sessionName) - 1] = '\0';
+        // strncpy(sessionName, other.sessionName, sizeof(sessionName) - 1);
+        // sessionName[sizeof(sessionName) - 1] = '\0';
         type = other.type;
     }
     return *this;
@@ -52,11 +52,37 @@ RtpSendClientInfo_t& RtpSendClientInfo_t::operator=(const RtpSendClientInfo_t& o
         localPort = other.localPort;
         clientIP = other.clientIP;
         clientPort = other.clientPort;
-        compareItemID = other.compareItemID;
-        compareItemRoadNum = other.compareItemRoadNum;
+        // compareItemID = other.compareItemID;
+        // compareItemRoadNum = other.compareItemRoadNum;
         SoundCardNum = other.SoundCardNum;
         SoundCardRoadNum = other.SoundCardRoadNum;
         // udpSocket = other.udpSocket;     /* 不拷贝udp套接字 */ 
     }
     return *this;
 }
+
+
+bool RtpClientKey_t::operator==(const RtpClientKey_t& other) const
+{
+    return (clientIP == other.clientIP && clientPort == other.clientPort &&
+            SoundCardNum == other.SoundCardNum && SoundCardRoadNum == other.SoundCardRoadNum);
+}
+
+
+bool RtpClientKey_t::operator<(const RtpClientKey_t& other) const
+{
+    if (clientPort < other.clientPort)
+        return true;
+    if (clientPort > other.clientPort)
+        return false;
+    if (SoundCardNum < other.SoundCardNum)
+        return true;
+    if (SoundCardNum > other.SoundCardNum)
+        return false;
+    if (SoundCardRoadNum < other.SoundCardRoadNum)
+        return true;
+    if (SoundCardRoadNum > other.SoundCardRoadNum)
+        return false;
+    return clientIP < other.clientIP; // 最后比较IP地址
+}
+

+ 21 - 13
RTPServer/Rtpcommon.h

@@ -28,10 +28,8 @@ struct RtpRecvClientInfo_t
 {
     char clientIP[32];      /* 客户端IP */
     int clientPort;         /* 客户端接收数据的UDP端口 */
-    // int sessionID;          /* 连接的SessionID */
     int compareItemID;      /* 对比项ID */
     int compareItemRoadNum; /* 对比项的通道号 */
-    char sessionName[32];   /* 客户端名称 */
     int type;               /* 包类型,0:Login,1:Heart,2:Logout */
 
     RtpRecvClientInfo_t();
@@ -39,13 +37,6 @@ struct RtpRecvClientInfo_t
     RtpRecvClientInfo_t& operator=(const RtpRecvClientInfo_t& other);
 };
 
-/**
-    回复给客户端的信息,主要是服务发送RTP数据的端口
- */
-struct RtpReplyClientInfo_t
-{
-    int localPort;          /* 本地发送数据的UDP端口 */
-};
 
 
 /**
@@ -55,12 +46,12 @@ struct RtpReplyClientInfo_t
 struct RtpSendClientInfo_t
 {
     QString localIP;                    /* 本地IP */
-    int localPort;                      /* 本地发送数据的UDP端口 */
+    int localPort = -1;                 /* 本地发送数据的UDP端口 */
     QString clientIP;                   /* 客户端IP */
-    int clientPort;                     /* 客户端接收数据的UDP端口 */
+    int clientPort = -1;                /* 客户端接收数据的UDP端口 */
     
-    int compareItemID;                  /* 对比项ID */
-    int compareItemRoadNum;             /* 对比项通道号 */
+    // int compareItemID;                  /* 对比项ID */
+    // int compareItemRoadNum;             /* 对比项通道号 */
     int SoundCardNum;                   /* 声卡编号 */
     int SoundCardRoadNum;               /* 声卡通道编号 */
 
@@ -71,6 +62,23 @@ struct RtpSendClientInfo_t
 };
 
 
+/**
+ * @brief 客户端信息结构体,充当Rtp服务中客户端列表的键
+ * 
+ */
+struct RtpClientKey_t
+{
+    QString clientIP;           /* 客户端IP */
+    int clientPort;             /* 客户端接收数据的UDP端口 */
+
+    int SoundCardNum = -1;      /* 声卡编号 */
+    int SoundCardRoadNum = -1;  /* 声卡通道编号 */
+
+    RtpClientKey_t() = default;
+    
+    bool operator==(const RtpClientKey_t& other) const;
+    bool operator<(const RtpClientKey_t& other) const;
+};
 
 
 

+ 30 - 1
Server/ACAServer.cpp

@@ -117,6 +117,11 @@ bool ACAServer::initGlobalInfo()
     /* 初始化全局数据信息 */
     GInfo.initGlobalInfo();
 
+    /*------------------------------------------------------------------------------------*/
+    /* 发送服务信息到MQTT中,设置为保留信息 */
+    sendServerInfoToMQTT();
+
+
     /*------------------------------------------------------------------------------------*/
     printACAServerConfig();
 
@@ -196,6 +201,25 @@ bool ACAServer::waitForWebAPIReply()
     return true;
 }
 
+/* 发送服务信息到MQTT中 */
+bool ACAServer::sendServerInfoToMQTT()
+{
+    const QString& mqttPubTopicServerInfo = GInfo.mqttPubTopicServerInfo();
+    nJson serverInfo;
+    serverInfo["server_ip"] = SysConfig.getBaseConfigSrc().strServerIP.toStdString();
+    serverInfo["server_rtp_port"] = SysConfig.getDatabaseConfig().nListenPort;
+
+    QByteArray payload = QString::fromStdString(serverInfo.dump()).toUtf8();
+
+    if(!m_fromMQTT.sendMessage(mqttPubTopicServerInfo, payload, 0, true))
+    {
+        SPDLOG_LOGGER_ERROR(m_logger, "发送服务信息到MQTT失败");
+        return false;
+    }
+
+    return true;
+}
+
 
 
 /* 线程函数,定时删除录音线程的实例 */
@@ -227,14 +251,19 @@ void ACAServer::thread_RTPServer()
 /* 打印ACA服务一些基础配置参数 */
 void ACAServer::printACAServerConfig()
 {
+    const auto& baseConfig = SysConfig.getBaseConfigSrc();
+    const auto& dbConfig = SysConfig.getDatabaseConfig();
     SPDLOG_LOGGER_INFO(m_logger, "=========================================================================================");
     SPDLOG_LOGGER_INFO(m_logger," ");
     /* 打印一些基础参数 */
     SPDLOG_LOGGER_INFO(m_logger, "MQTT服务器地址: {}, 端口: {}", m_mqttIP.toStdString(), m_mqttPort);
     SPDLOG_LOGGER_INFO(m_logger, "WebAPI地址: {}, ID: {}", m_webAPIUrl.toStdString(), m_webAPIID.toStdString());
+    SPDLOG_LOGGER_INFO(m_logger, "ACA服务地址: {}, RTP服务TCP监听端口: {}", baseConfig.strServerIP.toStdString(), dbConfig.nListenPort);
+    SPDLOG_LOGGER_INFO(m_logger, "MQTT订阅: ");
+    SPDLOG_LOGGER_INFO(m_logger, "      服务信息订阅: {}", GInfo.mqttPubTopicServerInfo().toStdString());
+    SPDLOG_LOGGER_INFO(m_logger, "      对比项信息订阅: {}", GInfo.mqttPubTopicCompareItem().toStdString());
     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, "录音文件路径: {}", SysConfig.getDatabaseConfig().strRecordFilePath.toStdString());
 
     // SPDLOG_LOGGER_INFO(m_logger, "-----------------------------------------------------------------------------------------");

+ 5 - 1
Server/ACAServer.h

@@ -50,6 +50,8 @@ private:
     bool readConfigFile();
     /* 等待webapi回复信息 */
     bool waitForWebAPIReply();
+    /* 发送服务信息到MQTT中 */
+    bool sendServerInfoToMQTT();
 
 private:
     /* 线程函数,定时删除录音线程的实例 */
@@ -67,13 +69,15 @@ private slots:
 
 private:
     std::shared_ptr<spdlog::logger> m_logger = nullptr;
-    FromMQTT m_fromMQTT;
     std::atomic_bool m_isRunning = false;   /* 线程运行标志位 */
 
+    /* --------------------------------- MQTT --------------------------------- */
+    FromMQTT m_fromMQTT;
     const QString m_webapiTopic = "LH_WEBINFO";
     QString m_mqttIP;
     int m_mqttPort = 1883;              /* MQTT服务器端口 */
 
+    /* --------------------------------- WebAPI --------------------------------- */
     FromWebAPI m_fromWebAPI;
     QString m_webAPIUrl;                /* WebAPI地址 */
     QString m_webAPIID;                 /* WebAPI ID */

+ 9 - 7
Server/GlobalInfo/GlobalInfo.h

@@ -52,6 +52,7 @@ public:
     void setMqttInfo(const QString& ip, int port);
     const QString& mqttPubTopicDB() const {return m_pubTopicDB;}
     const QString& mqttPubTopicCompareItem() const {return m_pubTopicCompareItem;}
+    const QString& mqttPubTopicServerInfo() const {return m_pubTopicServerInfo;}
     
     /* 获取WebAPI信息 */
     QString webAPIUrl() const {return m_webAPIUrl;}
@@ -140,14 +141,15 @@ private:
 private:
     /******************** 系统基础设置 *********************/
     /* MQTT信息 */
-    QString m_mqttIP;                  /* MQTT服务器IP */
-    int m_mqttPort = 1883;                 /* MQTT服务器端口 */
+    QString m_mqttIP;                           /* MQTT服务器IP */
+    int m_mqttPort = 1883;                      /* MQTT服务器端口 */
     /* WebAPI地址 */
-    QString m_webAPIUrl;               /* WebAPI地址 */
-    QString m_webAPIID;                /* WebAPI ID */
-    const QString m_webAPIAppType = "ACAWatch"; /* 应用类型 */
-    QString m_pubTopicDB = "LH_ACAServer/DB"; /* 音量包发布主题 */
-    QString m_pubTopicCompareItem = "LH_ACAServer/CompareItems"; /* 对比项发布主题 */
+    QString m_webAPIUrl;                        /* WebAPI地址 */
+    QString m_webAPIID;                         /* WebAPI ID */
+    const QString m_webAPIAppType = "ACAWatch";                     /* 应用类型 */
+    QString m_pubTopicDB = "LH_ACAServer/DB";                       /* 音量包发布主题 */
+    QString m_pubTopicCompareItem = "LH_ACAServer/CompareItems";    /* 对比项发布主题 */
+    QString m_pubTopicServerInfo = "LH_ACAServer/ServerInfo";       /* 服务信息发布主题 */
 
     /******************** 音频基础数据 *********************/
     /* 环形队列中每个文件的大小,也是生成的wav文件的大小

+ 1 - 1
Server/ThreadCalculate/CalculateDBThread.cpp

@@ -147,7 +147,7 @@ bool CalculateDBThread::initData()
         }
         if(m_threadCreateAlarmFile == nullptr)
         {
-            m_threadCreateAlarmFile = ThreadMan.getCreateLongFileThread(m_roadInfo.scRoadInfo.nSoundCardNum, m_roadInfo.scRoadInfo.roadInfo.nRoadNum);
+            m_threadCreateAlarmFile = ThreadMan.getCreateRecordFileThread(m_roadInfo.scRoadInfo.nSoundCardNum, m_roadInfo.scRoadInfo.roadInfo.nRoadNum);
         }
         
         if(m_threadCreateDB != nullptr && m_threadCreateAlarmFile != nullptr)

+ 2 - 2
Server/ThreadCalculate/CalculateDBThread.h

@@ -4,7 +4,7 @@
 #include "AudioData.h"
 #include "BaseCalculateThread.h"
 #include "CreateDBThread.h"
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 #include "SendStruct.h"
 #include "CalculateAudio.h"
 #include "CompareResult.h"
@@ -89,7 +89,7 @@ private:
     StVolumeParam m_volumeParam;                            /* 音量计算的参数 */
 
     /* 录制报警文件的线程 */
-    CreateLongFileThread* m_threadCreateAlarmFile = nullptr;
+    CreateRecordFileThread* m_threadCreateAlarmFile = nullptr;
 
     int32_t m_numQueueSeconds = 0;                         /* 队列元素数目,单位:秒 */
     CaculateDBData m_caculateDBData;                        /* 计算音量的环形队列类 */

+ 3 - 3
Server/ThreadCalculate/CompareDoubleThread.cpp

@@ -6,7 +6,7 @@
 #include "GlobalInfo.h"
 #include "ThreadManager.h"
 #include "ConsistencyCompareThread.h"
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 #include "ThreadAlarmManager.h"
 
 
@@ -158,11 +158,11 @@ bool CompareDoubleThread::initData()
         /* 获取生成报警音频文件的线程 */
         if(m_threadCreateAlarmFile1 == nullptr)
         {
-            m_threadCreateAlarmFile1 = ThreadMan.getCreateLongFileThread(m_roadInfo1.nSoundCardNum, m_roadInfo1.roadInfo.nRoadNum);
+            m_threadCreateAlarmFile1 = ThreadMan.getCreateRecordFileThread(m_roadInfo1.nSoundCardNum, m_roadInfo1.roadInfo.nRoadNum);
         }
         if(m_threadCreateAlarmFile2 == nullptr)
         {
-            m_threadCreateAlarmFile2 = ThreadMan.getCreateLongFileThread(m_roadInfo2.nSoundCardNum, m_roadInfo2.roadInfo.nRoadNum);
+            m_threadCreateAlarmFile2 = ThreadMan.getCreateRecordFileThread(m_roadInfo2.nSoundCardNum, m_roadInfo2.roadInfo.nRoadNum);
         }
 
         if( m_threadCreateDB1 != nullptr && m_threadCreateDB2 != nullptr

+ 3 - 3
Server/ThreadCalculate/CompareDoubleThread.h

@@ -13,7 +13,7 @@
 
 
 class ConsistencyCompareThread;
-class CreateLongFileThread;
+class CreateRecordFileThread;
 
 
 /**
@@ -116,8 +116,8 @@ private:
     CaculateDBData m_localData2;
 
     /* ----------------- 生成报警音频文件的线程 ------------------ */
-    CreateLongFileThread* m_threadCreateAlarmFile1 = nullptr;
-    CreateLongFileThread* m_threadCreateAlarmFile2 = nullptr;
+    CreateRecordFileThread* m_threadCreateAlarmFile1 = nullptr;
+    CreateRecordFileThread* m_threadCreateAlarmFile2 = nullptr;
 
     /* 计算音量静音、过载、反相的线程,从这里获取到填充好的音量包 */
 

+ 3 - 3
Server/ThreadCalculate/CompareItemThread.cpp

@@ -313,8 +313,8 @@ void CompareItemThread::do_timeout()
     timerTask();
 
     std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
-    std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - m_nowTimePoint);
-    SPDLOG_LOGGER_DEBUG(m_logger, "{} 定时器任务完成,耗时: {} ms", m_logBase, duration.count());
+    // std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - m_nowTimePoint);
+    // SPDLOG_LOGGER_DEBUG(m_logger, "{} 发送音量包数据到 {} 成功,耗时: {} ms", m_logBase, m_pubTopic.toStdString(), duration.count());
     m_nowTimePoint = endTime; // 更新当前时间点
 }
 
@@ -746,7 +746,7 @@ void CompareItemThread::sendResultData()
     {
         SPDLOG_LOGGER_ERROR(m_logger, "{} 发送音量包数据到 {} 失败,错误代码: {}", m_logBase, m_pubTopic.toStdString(), errorCode);
     }else {
-        SPDLOG_LOGGER_DEBUG(m_logger, "{} 发送音量包数据到 {} 成功", m_logBase, m_pubTopic.toStdString());
+        // SPDLOG_LOGGER_DEBUG(m_logger, "{} 发送音量包数据到 {} 成功", m_logBase, m_pubTopic.toStdString());
     }
 }
 

+ 2 - 2
Server/ThreadCalculate/NoiseDetectThread.cpp

@@ -3,7 +3,7 @@
 #include <random>
 
 #include "CreateWAVThread.h"
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 #include "ThreadAlarmManager.h"
 #include "ThreadManager.h"
 #include "commonDefine.h"
@@ -161,7 +161,7 @@ bool NoiseDetectThread::initData()
         }
         if(m_pThreadCreateAlarm == nullptr)
         {
-            m_pThreadCreateAlarm = ThreadMan.getCreateLongFileThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum);
+            m_pThreadCreateAlarm = ThreadMan.getCreateRecordFileThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum);
         }
         if(m_pThreadWav != nullptr && m_pThreadCreateAlarm != nullptr)
         {

+ 2 - 2
Server/ThreadCalculate/NoiseDetectThread.h

@@ -8,7 +8,7 @@
 #include "RingQueueManualMutex.hpp"
 
 class CreateWAVThread;
-class CreateLongFileThread ;
+class CreateRecordFileThread ;
 
 
 /**
@@ -74,7 +74,7 @@ private:
     SoundCardRoadInfo_t m_roadInfo;                 /* 录音通道编号 */
     std::string m_roadName;                         /* 录音通道名称 */
     CreateWAVThread* m_pThreadWav = nullptr;        /* WAV小文件生成线程指针 */
-    CreateLongFileThread * m_pThreadCreateAlarm = nullptr; /* 生成报警文件的线程 */
+    CreateRecordFileThread * m_pThreadCreateAlarm = nullptr; /* 生成报警文件的线程 */
     std::atomic_bool m_isInDetectPeriod = true;     /* 是否在检测时间段内 */
 
     NoiseDetectBaseConfig_t m_baseNoiseDetectConfig;/* 噪音检测的基础配置 */

+ 6 - 6
Server/ThreadManager/ThreadManager.cpp

@@ -7,7 +7,7 @@
 #include "GlobalVariable.h"
 #include "NoiseDetectThread.h"
 #include "RtpOneRoadThread.h"
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 #include "RecordThread.h"
 #include "AssignSrcDataThread.h"
 
@@ -107,14 +107,14 @@ bool ThreadManager::createRecordThread(const SoundCardRoadInfo_t& roadInfo, int
 
     /* 创建生成长文件的线程 */
     threadInfo.threadType = EThreadType::Type_CreateLongWAV;
-    CreateLongFileThread* pCreateLongWAVThread = new CreateLongFileThread(threadInfo);
+    CreateRecordFileThread* pCreateLongWAVThread = new CreateRecordFileThread(threadInfo);
     if(pCreateLongWAVThread == nullptr)
     {
         SPDLOG_LOGGER_ERROR(m_logger, "{}:{} 创建生成长文件线程失败", roadInfo.strSoundCardName.toStdString(), roadInfo.roadInfo.nRoadNum);
         // return false; // 创建失败
     }else 
     {
-        CPPTP.add_task(&CreateLongFileThread::threadTask, pCreateLongWAVThread);
+        CPPTP.add_task(&CreateRecordFileThread::threadTask, pCreateLongWAVThread);
         std::lock_guard<std::mutex> lock(m_mutexCreateLongWAVThreads);
         m_createLongWAVThreads.push_back(pCreateLongWAVThread);
     }
@@ -422,7 +422,7 @@ void ThreadManager::destroyeRecordThread()
             {
                 if(pThread->getThreadInfo().threadState == EThreadState::State_Running)
                 {
-                    auto pCreateLongWAVThread = dynamic_cast<CreateLongFileThread*>(pThread);
+                    auto pCreateLongWAVThread = dynamic_cast<CreateRecordFileThread*>(pThread);
                     delete pCreateLongWAVThread; // 删除线程
                     pThread = nullptr;
                     it = m_createLongWAVThreads.erase(it); // 从列表中移除
@@ -602,7 +602,7 @@ RTPOneRoadThread* ThreadManager::getRtpSendThread(int cardID, int roadID)
 }
 
 /* 获取录制报警文件的线程 */
-CreateLongFileThread* ThreadManager::getCreateLongFileThread(int cardID, int roadID)
+CreateRecordFileThread* ThreadManager::getCreateRecordFileThread(int cardID, int roadID)
 {
     std::lock_guard<std::mutex> lock(m_mutexCreateLongWAVThreads);
     for(auto& pThread : m_createLongWAVThreads)
@@ -610,7 +610,7 @@ CreateLongFileThread* ThreadManager::getCreateLongFileThread(int cardID, int roa
         if(pThread->getThreadInfo().cardRoadInfo.nSoundCardNum == cardID &&
            pThread->getThreadInfo().cardRoadInfo.roadInfo.nRoadNum == roadID)
         {
-            return dynamic_cast<CreateLongFileThread*>(pThread);
+            return dynamic_cast<CreateRecordFileThread*>(pThread);
         }
     }
     return nullptr;

+ 2 - 2
Server/ThreadManager/ThreadManager.h

@@ -16,7 +16,7 @@ class CreateWAVThread;
 class ConsistencyCompareThread;
 class NoiseDetectThread;
 class RTPOneRoadThread;
-class CreateLongFileThread;
+class CreateRecordFileThread;
 class CreateDBThread;
 
 
@@ -77,7 +77,7 @@ public:
     /* 获取发送Rtp数据的线程 */
     RTPOneRoadThread* getRtpSendThread(int cardID, int roadID);
     /* 获取录制报警文件的线程 */
-    CreateLongFileThread* getCreateLongFileThread(int cardID, int roadID);
+    CreateRecordFileThread* getCreateRecordFileThread(int cardID, int roadID);
 
     /* -------------------------------------------------------------------------------------------
      * 获取计算线程,如果该线程不存在则创建该线程

+ 2 - 2
Server/ThreadRecord/AssignSrcDataThread.cpp

@@ -13,7 +13,7 @@
 
 #include "CreateDBThread.h"
 #include "CreateWAVThread.h"
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 
 
 
@@ -245,7 +245,7 @@ bool AssignSrcDataThread::getDispatchThread()
     if(pLongFileThread != nullptr )
     {
         // m_listDispatchThreads.push_back(dynamic_cast<BaseRecordThread*>(pLongFileThread));
-        m_pThreadCreateLongFile = dynamic_cast<CreateLongFileThread*>(pLongFileThread);
+        m_pThreadCreateLongFile = dynamic_cast<CreateRecordFileThread*>(pLongFileThread);
     }
     /* 获取发送RTP数据的线程 */
     m_rtpSenderThread = ThreadMan.findRecordThread(EThreadType::Type_RtpSend, m_threadInfo.cardRoadInfo.nSoundCardNum, m_threadInfo.cardRoadInfo.roadInfo.nRoadNum);

+ 2 - 2
Server/ThreadRecord/AssignSrcDataThread.h

@@ -13,7 +13,7 @@ struct AudioSrcData;
 class RecordThread;
 class CreateDBThread;
 class CreateWAVThread;
-class CreateLongFileThread;
+class CreateRecordFileThread;
 
 
 /**
@@ -97,7 +97,7 @@ private:
     CreateWAVThread* m_pThreadCreateWAV = nullptr;      /* 创建Wav小文件的线程 */
 
     /* ---------------------- 生成长文件的线程 ---------------------- */
-    CreateLongFileThread* m_pThreadCreateLongFile = nullptr; /* 创建长文件的线程 */
+    CreateRecordFileThread* m_pThreadCreateLongFile = nullptr; /* 创建长文件的线程 */
 
 };
 

+ 48 - 20
Server/ThreadRecord/CreateLongFileThread.cpp → Server/ThreadRecord/CreateRecordFileThread.cpp

@@ -1,4 +1,4 @@
-#include "CreateLongFileThread.h"
+#include "CreateRecordFileThread.h"
 
 #include "AudioData.h"
 #include "GlobalInfo.h"
@@ -8,7 +8,7 @@
 
 
 
-CreateLongFileThread::CreateLongFileThread(RecordThreadInfo_t& threadInfo)
+CreateRecordFileThread::CreateRecordFileThread(RecordThreadInfo_t& threadInfo)
     : BaseRecordThread(threadInfo)
 {
     m_logger = spdlog::get("RecordAudio");
@@ -21,14 +21,14 @@ CreateLongFileThread::CreateLongFileThread(RecordThreadInfo_t& threadInfo)
     initData();
 }
 
-CreateLongFileThread::~CreateLongFileThread()
+CreateRecordFileThread::~CreateRecordFileThread()
 {
 
 }
 
 
 /* 设置数据 */
-bool CreateLongFileThread::setData(const AudioSrcData& srcData)
+bool CreateRecordFileThread::setData(const AudioSrcData& srcData)
 {
     if(srcData.pData == nullptr || srcData.dataSize == 0)
     {
@@ -83,8 +83,36 @@ bool CreateLongFileThread::setData(const AudioSrcData& srcData)
 }
 
 
+/* 开始录制长文件 */
+bool CreateRecordFileThread::startRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo)
+{
+    if(compareItemRoadInfo.nSoundCardNum != m_threadInfo.cardRoadInfo.nSoundCardNum ||
+       compareItemRoadInfo.nRoadNum != m_threadInfo.cardRoadInfo.roadInfo.nRoadNum)
+    {
+        SPDLOG_LOGGER_ERROR(m_logger, "{} 开始录制音频文件失败,通道信息不匹配", m_logBase);
+        return false;
+    }
+    std::lock_guard<std::mutex> lock(m_mutexCompareItemRoadInfo);
+    if(m_listCompareItemRoadInfo.contains(compareItemRoadInfo))
+    {
+        return true;
+    }
+
+    return true;
+}
+
+/* 停止录制长文件 */
+bool CreateRecordFileThread::stopRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo)
+{
+
+
+    return true;
+}
+
+
+
 /* 开启录制 */
-bool CreateLongFileThread::startRecordAlarmFile(const AlarmInfo_t& alarmInfo)
+bool CreateRecordFileThread::startRecordAlarmFile(const AlarmInfo_t& alarmInfo)
 {
     std::lock_guard<std::mutex> lock(m_mutexAlarmFile);
     /* 先检查是否已经在队列中了 */
@@ -111,7 +139,7 @@ bool CreateLongFileThread::startRecordAlarmFile(const AlarmInfo_t& alarmInfo)
 }
 
 /* 停止录制,alarmInfo既是传入参数,也是传出参数,传出文件路径和开始位置 */
-bool CreateLongFileThread::stopRecordAlarmFile(AlarmInfo_t& alarmInfo)
+bool CreateRecordFileThread::stopRecordAlarmFile(AlarmInfo_t& alarmInfo)
 {
     std::lock_guard<std::mutex> lock(m_mutexAlarmFile);
     for(auto& it : m_mapAlarmFile)
@@ -148,7 +176,7 @@ bool CreateLongFileThread::stopRecordAlarmFile(AlarmInfo_t& alarmInfo)
  *          3、文件总长度是1小时的数据,超过1小时则重新开始记录一个新的文件
  * 
  */
-void CreateLongFileThread::task()
+void CreateRecordFileThread::task()
 {
     SPDLOG_LOGGER_INFO(m_logger, "➢ {} 开启记录文件线程 ", m_logBase);
     /* 计算一小时的文件大小 */
@@ -175,7 +203,7 @@ void CreateLongFileThread::task()
 
 
 /* 初始化一些数据 */
-bool CreateLongFileThread::initData()
+bool CreateRecordFileThread::initData()
 {
     m_logBase = fmt::format("录音通道 {}:{} - 记录长文件线程", 
         m_threadInfo.cardRoadInfo.strSoundCardName.toStdString(),
@@ -202,14 +230,14 @@ bool CreateLongFileThread::initData()
 }
 
 /* 清理数据 */
-void CreateLongFileThread::clearData()
+void CreateRecordFileThread::clearData()
 {
     /* 清理缓存数据 */
     m_bufferData.clear();
 }
 
 /* 写入长记录文件 */
-bool CreateLongFileThread::writeLongRecordFile()
+bool CreateRecordFileThread::writeLongRecordFile()
 {
     /* 判断缓存是否达到1分钟数据临界值 */
     {
@@ -322,7 +350,7 @@ bool CreateLongFileThread::writeLongRecordFile()
 }
 
 /* 设置今日目录 */
-bool CreateLongFileThread::setTodayPath(bool isNewFile)
+bool CreateRecordFileThread::setTodayPath(bool isNewFile)
 {
     if(!isNewFile)
     {
@@ -372,7 +400,7 @@ bool CreateLongFileThread::setTodayPath(bool isNewFile)
 }
 
 /* 打开文件 */
-bool CreateLongFileThread::openFile(QFile& wavFile, bool isNewFile)
+bool CreateRecordFileThread::openFile(QFile& wavFile, bool isNewFile)
 {
     if(isNewFile)
     {
@@ -411,14 +439,14 @@ bool CreateLongFileThread::openFile(QFile& wavFile, bool isNewFile)
 }
 
 /* 写入音频数据到文件 */
-bool CreateLongFileThread::writeAudioDataToFile(const AudioSrcData& audioData, const QString& fileName)
+bool CreateRecordFileThread::writeAudioDataToFile(const AudioSrcData& audioData, const QString& fileName)
 {
 
     return true;
 }
 
 /* 生成文件名 */
-QString CreateLongFileThread::generateFileName(const QDateTime& startTime, const QDateTime& endTime) const
+QString CreateRecordFileThread::generateFileName(const QDateTime& startTime, const QDateTime& endTime) const
 {
     // SPDLOG_LOGGER_DEBUG(m_logger, "{} 生成文件名: 开始时间: {}, 结束时间: {}", 
     //     m_logBase, startTime.toString("yyyy-MM-dd hh:mm:ss").toStdString(), 
@@ -435,7 +463,7 @@ QString CreateLongFileThread::generateFileName(const QDateTime& startTime, const
 
 
 /* 判断是否过了整点 */
-bool CreateLongFileThread::isOneHourPassed()
+bool CreateRecordFileThread::isOneHourPassed()
 {
     if(m_writtenSize >= m_oneHourSize)
     {
@@ -460,7 +488,7 @@ bool CreateLongFileThread::isOneHourPassed()
 
 
 /* 生成报警文件名 */
-QString CreateLongFileThread::generateAlarmFileName(const AlarmValue_t& value, bool isNewFile)
+QString CreateRecordFileThread::generateAlarmFileName(const AlarmValue_t& value, bool isNewFile)
 {
     QString retFileName;
     if(isNewFile)
@@ -508,7 +536,7 @@ QString CreateLongFileThread::generateAlarmFileName(const AlarmValue_t& value, b
 
 
 /* 写入报警文件 */
-void CreateLongFileThread::writeAlarmFile()
+void CreateRecordFileThread::writeAlarmFile()
 {
     const int writeSeconds = 2;
     const int newAlarmSeconds = 10;
@@ -624,7 +652,7 @@ void CreateLongFileThread::writeAlarmFile()
 
 
 /* 创建新的文件 */
-void CreateLongFileThread::createNewAlarmFile(AlarmValue_t& value, const std::list<AudioSrcData*>& dataList)
+void CreateRecordFileThread::createNewAlarmFile(AlarmValue_t& value, const std::list<AudioSrcData*>& dataList)
 {
     /* 新的报警录音,生成文件名 */
     value.fileName = generateAlarmFileName(value, true);
@@ -684,7 +712,7 @@ void CreateLongFileThread::createNewAlarmFile(AlarmValue_t& value, const std::li
 
 
 /* 设置今日报警文件夹 */
-bool CreateLongFileThread::setTodayAlarmPath()
+bool CreateRecordFileThread::setTodayAlarmPath()
 {
     /* 先检查当天日期文件夹是否存在,因为其他线程可能已经创建了 */
     QDate today = QDate::currentDate();
@@ -717,7 +745,7 @@ bool CreateLongFileThread::setTodayAlarmPath()
 }
 
 /* 根据报警类型的枚举获取字符 */
-QString CreateLongFileThread::getAlarmTypeString(EAlarmType type) const
+QString CreateRecordFileThread::getAlarmTypeString(EAlarmType type) const
 {
     switch(type)
     {

+ 30 - 20
Server/ThreadRecord/CreateLongFileThread.h → Server/ThreadRecord/CreateRecordFileThread.h

@@ -1,5 +1,5 @@
-#ifndef _CREATELONGFILETHREAD_H_
-#define _CREATELONGFILETHREAD_H_
+#ifndef _CREATERECORDFILETHREAD_H_
+#define _CREATERECORDFILETHREAD_H_
 
 #include "BaseRecordThread.h"
 #include "AudioData.h"
@@ -109,17 +109,22 @@ struct AlarmValue_t
               例如:Application/2025-01-01/1-1/Alarm_1_1_Silent_20250101_120000-20250101_120100.wav
 
  */
-class CreateLongFileThread : public BaseRecordThread
+class CreateRecordFileThread : public BaseRecordThread
 {
 
 public:
-    CreateLongFileThread(RecordThreadInfo_t& threadInfo);
-    ~CreateLongFileThread() override;
+    CreateRecordFileThread(RecordThreadInfo_t& threadInfo);
+    ~CreateRecordFileThread() override;
 
     /* 设置数据 */
     bool setData(const AudioSrcData& srcData) override;
 
-    /* 开启录制 */
+    /* 开始录制长文件 */
+    bool startRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo);
+    /* 停止录制长文件 */
+    bool stopRecordLongFile(const OneCompareItemRoadInfo_t& compareItemRoadInfo);
+
+    /* 开启录制报警信息 */
     bool startRecordAlarmFile(const AlarmInfo_t& alarmInfo);
     /* 停止录制,alarmInfo既是传入参数,也是传出参数,传出文件路径和开始位置 */
     bool stopRecordAlarmFile(AlarmInfo_t& alarmInfo);
@@ -160,25 +165,30 @@ private:
     
 
 private:
+    /* 对比项通道信息列表,记录当前对比项的通道信息,如果没有对比项信息就停止录音 */
+    std::mutex m_mutexCompareItemRoadInfo;
+    QList<OneCompareItemRoadInfo_t> m_listCompareItemRoadInfo;
+
+
     /* 临时缓存数据, 一分钟写入一次,给这个分配2分钟的数据大小 */
     std::mutex m_mutexBuffer;
-    AudioSrcData m_bufferData;
+    AudioSrcData m_bufferData;              /* 音频数据缓冲区,存储写入的1分钟数据 */
+    AudioSrcData m_srcData;                 /* 当前的音频数据,满一分钟后会拷贝到这里,然后写入数据,防止写入的时候阻塞缓冲区 */
 
-    AudioSrcData m_srcData;             /* 当前的音频数据 */
+    int32_t m_writeCriticalSize = 0;        /* 写入文件的临界大小,单位:字节,缓存超过这个大小就写入文件 */
+    int32_t m_oneHourSize = 0;              /* 一小时的音频数据大小 */
 
-    int32_t m_writeCriticalSize = 0;  /* 写入文件的临界大小,单位:字节,缓存超过这个大小就写入文件 */
-    int32_t m_oneHourSize = 0;       /* 一小时的音频数据大小 */
 
+    QDir m_todayDir;                        /* 今日目录 */
+    QDate m_todayDateRecord;                /* 今日日期,记录长文件用 */
+    QString m_wavFileName;                  /* wav文件对象 */
+    int m_openFileErrorSize = 0;            /* 打开文件错误次数 */
+    WavHeader m_wavHeader;                  /* 当前wav文件头信息 */
+    int64_t m_writtenSize = 0;              /* 已经写入的数据大小 */
+    QDateTime m_writtenStartTime;           /* 已经写入数据的开始时间点 */
+    QDateTime m_writtenNowTime;             /* 已经写入数据的最后时间点 */
 
-    QDir m_todayDir;                /* 今日目录 */
-    QDate m_todayDateRecord;        /* 今日日期,记录长文件用 */
-    QString m_wavFileName;          /* wav文件对象 */
-    int m_openFileErrorSize = 0;    /* 打开文件错误次数 */
-    WavHeader m_wavHeader;          /* 当前wav文件头信息 */
-    int64_t m_writtenSize = 0;      /* 已经写入的数据大小 */
-    QDateTime m_writtenStartTime;   /* 已经写入数据的开始时间点 */
-    QDateTime m_writtenNowTime;     /* 已经写入数据的最后时间点 */
-
+    
 
     /*===============================================================================*/
     /* 报警文件名,key是报警信息,value是文件信息 */
@@ -195,4 +205,4 @@ private:
 };
 
 
-#endif // _CREATELONGFILETHREAD_H_
+#endif // _CREATERECORDFILETHREAD_H_

+ 3 - 1
Server/ThreadRecord/RecordThread.cpp

@@ -139,7 +139,9 @@ bool RecordThread::initData()
     m_audioRecord.setRecordParams(m_sampleRate, m_bitsPerSample, m_numChannels);
 
     /* 设置声卡描述符 */
-    m_audioRecordDesc = fmt::format("hw:{},{}", m_threadInfo.cardRoadInfo.nSoundCardNum, m_threadInfo.cardRoadInfo.roadInfo.nRoadNum);
+    // m_audioRecordDesc = fmt::format("hw:{},{}", m_threadInfo.cardRoadInfo.nSoundCardNum, m_threadInfo.cardRoadInfo.roadInfo.nRoadNum);
+    m_audioRecordDesc = fmt::format("plughw:{},{}", m_threadInfo.cardRoadInfo.nSoundCardNum, m_threadInfo.cardRoadInfo.roadInfo.nRoadNum);
+
 
     return true;
 }

+ 5 - 1
SettingLibrary/Modules/Database/databasewidget.cpp

@@ -40,7 +40,11 @@ DatabaseWidget::DatabaseWidget(QWidget *parent) :
 
     ui->lineEdit_recordFilePath->setPlaceholderText("请输入,服务端录音文件存储路径");
 
-    ui->lineEdit_ftpPath->setPlaceholderText("请输入,FTP报警文件路径");
+    ui->lineEdit_ftpPath->setPlaceholderText("请输入,FTP文件路径");
+
+    /* 隐藏客户端通讯端口 */
+    ui->label_6->hide();
+    ui->lineEdit_clientPort->hide();
 
     /* 获取初始配置 */
     do_pBtn_restore_clicked();

+ 27 - 0
common/GlobalInfo/GlobalVariable.cpp

@@ -56,6 +56,33 @@ bool CompareItemRoadInfo_t::operator==(const CompareItemRoadInfo_t &other) const
 
 
 
+OneCompareItemRoadInfo_t::OneCompareItemRoadInfo_t(const OneCompareItemRoadInfo_t &other)
+{
+    *this = other;
+}
+
+OneCompareItemRoadInfo_t& OneCompareItemRoadInfo_t::operator=(const OneCompareItemRoadInfo_t &other)
+{
+    if(this == &other)
+    {
+        return *this; // 自身比较
+    }
+    nCompareItemID = other.nCompareItemID;
+    nCompareRoadNum = other.nCompareRoadNum;
+    nSoundCardNum = other.nSoundCardNum;
+    nRoadNum = other.nRoadNum;
+    return *this;
+}
+
+bool OneCompareItemRoadInfo_t::operator==(const OneCompareItemRoadInfo_t &other) const
+{
+    return (nCompareItemID == other.nCompareItemID &&
+            nCompareRoadNum == other.nCompareRoadNum &&
+            nSoundCardNum == other.nSoundCardNum &&
+            nRoadNum == other.nRoadNum);
+}
+
+
 CompareItemDetectParam_t& CompareItemDetectParam_t::operator=(const CompareItemDetectParam_t &other)
 {
     if(this == &other)

+ 15 - 0
common/GlobalInfo/GlobalVariable.h

@@ -180,6 +180,21 @@ struct CompareItemRoadInfo_t
 };
 
 
+/* 一个对比项通道信息,包含对比项信息 */
+struct OneCompareItemRoadInfo_t
+{
+    int nCompareItemID = -1;                /* 对比项ID */
+    int nCompareRoadNum = -1;               /* 对比项通道编号 */
+    int nSoundCardNum = -1;                 /* 声卡编号 */
+    int nRoadNum = -1;                      /* 声卡通道编号 */
+
+    OneCompareItemRoadInfo_t() = default;
+    OneCompareItemRoadInfo_t(const OneCompareItemRoadInfo_t &other);
+    OneCompareItemRoadInfo_t& operator=(const OneCompareItemRoadInfo_t &other);
+    bool operator==(const OneCompareItemRoadInfo_t &other) const;
+};
+
+
 /* 检测阈值,静音和过载是整数,反相是小数 */
 union Threshold_t
 {

+ 1 - 1
show3/CMakeLists.txt

@@ -54,7 +54,7 @@ target_link_libraries(${this_exe} PRIVATE
     Qt${QT_VERSION_MAJOR}::Widgets
     Qt${QT_VERSION_MAJOR}::Core
     Qt${QT_VERSION_MAJOR}::Network
-
+    Qt${QT_VERSION_MAJOR}::Multimedia
 )
 # message(STATUS "可执行文件输出位置: ${EXECUTABLE_OUTPUT_PATH}")
 target_link_libraries(${this_exe} PRIVATE

+ 56 - 2
show3/main.cpp

@@ -6,15 +6,22 @@
 #include "ThreadPool.h"
 #include "RingQueueManualMutex.hpp"
 #include "spdlog/spdlog.h"
+#include <QApplication>
+#include <QCoreApplication>
+#include <QAudioFormat>
+#include <QAudioOutput>
+#include <QTimer>
+#include <QFile>
 
 
 void test1();
 void test2();
 void test3();
+void test4(int argc, char** argv);
 
-int main() 
+int main(int argc, char* argv[]) 
 {
-
+    
     // 初始化Python解释器
     // signalstats_wrapper::initialize();
 
@@ -28,6 +35,8 @@ int main()
     // std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待任务执行
 
     // signalstats_wrapper::finalize();
+    test4(argc, argv);
+
     
     return 0;
 }
@@ -158,3 +167,48 @@ void test3()
     }
 }
 
+void test4(int argc, char** argv)
+{
+    QCoreApplication a(argc, argv);
+    
+    QAudioFormat format;
+    format.setSampleRate(44100);
+    format.setChannelCount(2);
+    format.setSampleSize(16);
+    format.setCodec("audio/pcm");
+    format.setByteOrder(QAudioFormat::LittleEndian);
+    format.setSampleType(QAudioFormat::SignedInt);
+
+    QAudioOutput* audioOutput = new QAudioOutput(format);
+    QIODevice* device = audioOutput->start();
+    if (!device) {
+        SPDLOG_ERROR("Failed to start audio output device");
+        return;
+    }
+
+    QFile* pFile = new QFile(QCoreApplication::applicationDirPath() + "/test.wav");
+    if(!pFile->open(QIODevice::ReadOnly)) {
+        SPDLOG_ERROR("Failed to open audio file: {}", pFile->errorString().toStdString());
+        return;
+    }
+    int readSize = 4410 * 4;
+    char* buffer = new char[readSize]; // 1 second buffer for stereo 16-bit audio
+
+    QTimer* ptimer = new QTimer();
+    ptimer->setInterval(100);
+    ptimer->setSingleShot(false);
+    ptimer->setTimerType(Qt::PreciseTimer);
+    QObject::connect(ptimer, &QTimer::timeout, [&, device, pFile, buffer]() {
+        SPDLOG_INFO("Timer triggered, reading audio data");
+        pFile->read(buffer, readSize);
+        device->write(buffer, readSize);
+        if(pFile->atEnd()) {
+            pFile->seek(0); // 重新开始播放
+            SPDLOG_INFO("Reached end of file, restarting playback");
+        }
+    });
+    ptimer->start();
+    
+    a.exec();
+}
+