diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp
index c8c89f78..079a5510 100644
--- a/src/common/network/BaseNetwork.cpp
+++ b/src/common/network/BaseNetwork.cpp
@@ -252,9 +252,12 @@ uint32_t BaseNetwork::getDMRStreamId(uint32_t slotNo) const
/// Length of buffer to write.
///
///
-bool BaseNetwork::writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId)
+///
+bool BaseNetwork::writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly)
{
m_frameQueue->enqueueMessage(data, length, streamId, m_peerId, opcode, pktSeq, m_addr, m_addrLen);
+ if (queueOnly)
+ return true;
return m_frameQueue->flushQueue();
}
diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h
index 96dd06df..4b55de86 100644
--- a/src/common/network/BaseNetwork.h
+++ b/src/common/network/BaseNetwork.h
@@ -192,7 +192,7 @@ namespace network
/// Helper to send a data message to the master.
bool writeMaster(FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length,
- uint16_t pktSeq, uint32_t streamId);
+ uint16_t pktSeq, uint32_t streamId, bool queueOnly = false);
/** Digital Mobile Radio */
/// Reads DMR raw frame data from the DMR ring buffer.
diff --git a/src/fne/ActivityLog.cpp b/src/fne/ActivityLog.cpp
new file mode 100644
index 00000000..0ee95111
--- /dev/null
+++ b/src/fne/ActivityLog.cpp
@@ -0,0 +1,161 @@
+/**
+* Digital Voice Modem - Conference FNE Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Conference FNE Software
+*
+*/
+/*
+* Copyright (C) 2024 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "ActivityLog.h"
+#include "common/network/BaseNetwork.h"
+#include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork()
+
+#include
+
+#if defined(CATCH2_TEST_COMPILATION)
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Constants
+// ---------------------------------------------------------------------------
+
+#define EOL "\r\n"
+
+const uint32_t ACT_LOG_BUFFER_LEN = 501U;
+
+// ---------------------------------------------------------------------------
+// Global Variables
+// ---------------------------------------------------------------------------
+
+static std::string m_actFilePath;
+static std::string m_actFileRoot;
+
+static FILE* m_actFpLog = nullptr;
+
+static struct tm m_actTm;
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+///
+/// Helper to open the activity log file, file handle.
+///
+/// True, if log file is opened, otherwise false.
+static bool ActivityLogOpen()
+{
+ if (CurrentLogFileLevel() == 0U)
+ return true;
+
+ time_t now;
+ ::time(&now);
+
+ struct tm* tm = ::gmtime(&now);
+
+ if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) {
+ if (m_actFpLog != nullptr)
+ return true;
+ }
+ else {
+ if (m_actFpLog != nullptr)
+ ::fclose(m_actFpLog);
+ }
+
+ char filename[200U];
+ ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+
+ m_actFpLog = ::fopen(filename, "a+t");
+ m_actTm = *tm;
+
+ return m_actFpLog != nullptr;
+}
+
+///
+/// Initializes the activity log.
+///
+/// Full-path to the activity log file.
+/// Prefix of the activity log file name.
+bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot)
+{
+#if defined(CATCH2_TEST_COMPILATION)
+ return true;
+#endif
+ m_actFilePath = filePath;
+ m_actFileRoot = fileRoot;
+
+ return ::ActivityLogOpen();
+}
+
+///
+/// Finalizes the activity log.
+///
+void ActivityLogFinalise()
+{
+#if defined(CATCH2_TEST_COMPILATION)
+ return;
+#endif
+ if (m_actFpLog != nullptr)
+ ::fclose(m_actFpLog);
+}
+
+///
+/// Writes a new entry to the activity log.
+///
+/// This is a variable argument function.
+/// Formatted string to write to activity log.
+void ActivityLog(const char* msg, ...)
+{
+#if defined(CATCH2_TEST_COMPILATION)
+ return;
+#endif
+ assert(msg != nullptr);
+
+ char buffer[ACT_LOG_BUFFER_LEN];
+
+ va_list vl;
+ va_start(vl, msg);
+
+ ::vsnprintf(buffer, ACT_LOG_BUFFER_LEN - 1U, msg, vl);
+
+ va_end(vl);
+
+ bool ret = ::ActivityLogOpen();
+ if (!ret)
+ return;
+
+ if (CurrentLogFileLevel() == 0U)
+ return;
+
+ ::fprintf(m_actFpLog, "%s\n", buffer);
+ ::fflush(m_actFpLog);
+
+ if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) {
+ ::fprintf(stdout, "%s" EOL, buffer);
+ ::fflush(stdout);
+ }
+}
diff --git a/src/fne/ActivityLog.h b/src/fne/ActivityLog.h
new file mode 100644
index 00000000..3d5282f3
--- /dev/null
+++ b/src/fne/ActivityLog.h
@@ -0,0 +1,44 @@
+/**
+* Digital Voice Modem - Conference FNE Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Conference FNE Software
+*
+*/
+/*
+* Copyright (C) 2024 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#if !defined(__ACTIVITY_LOG_H__)
+#define __ACTIVITY_LOG_H__
+
+#include "Defines.h"
+
+#include
+
+// ---------------------------------------------------------------------------
+// Global Functions
+// ---------------------------------------------------------------------------
+
+/// Initializes the activity log.
+extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot);
+/// Finalizes the activity log.
+extern HOST_SW_API void ActivityLogFinalise();
+/// Writes a new entry to the activity log.
+extern HOST_SW_API void ActivityLog(const char* msg, ...);
+
+#endif // __ACTIVITY_LOG_H__
diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp
index 86bddc30..0c6e2d9f 100644
--- a/src/fne/FNEMain.cpp
+++ b/src/fne/FNEMain.cpp
@@ -30,6 +30,7 @@
*/
#include "Defines.h"
#include "common/Log.h"
+#include "ActivityLog.h"
#include "FNEMain.h"
#include "HostFNE.h"
@@ -248,6 +249,7 @@ int main(int argc, char** argv)
} while (g_signal == 1);
::LogFinalise();
+ ::ActivityLogFinalise();
return ret;
}
diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp
index 3021448a..18f1b6da 100644
--- a/src/fne/HostFNE.cpp
+++ b/src/fne/HostFNE.cpp
@@ -32,6 +32,7 @@
#include "network/fne/TagDMRData.h"
#include "network/fne/TagP25Data.h"
#include "network/fne/TagNXDNData.h"
+#include "ActivityLog.h"
#include "HostFNE.h"
#include "FNEMain.h"
@@ -121,6 +122,11 @@ int HostFNE::run()
::fatal("unable to open the log file\n");
}
+ ret = ::ActivityLogInitialise(logConf["activityFilePath"].as(), logConf["fileRoot"].as());
+ if (!ret) {
+ ::fatal("unable to open the activity log file\n");
+ }
+
// handle POSIX process forking
if (m_daemon) {
// create new process
diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp
index 033b0415..8265a27f 100644
--- a/src/fne/network/FNENetwork.cpp
+++ b/src/fne/network/FNENetwork.cpp
@@ -7,7 +7,7 @@
*
*/
/*
-* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
+* Copyright (C) 2023-2024 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
#include "network/fne/TagDMRData.h"
#include "network/fne/TagP25Data.h"
#include "network/fne/TagNXDNData.h"
+#include "fne/ActivityLog.h"
#include "HostFNE.h"
using namespace network;
@@ -539,10 +540,7 @@ void FNENetwork::clock(uint32_t ms)
::memcpy(rawPayload, buffer.get() + 11U, length - 11U);
std::string payload(rawPayload, rawPayload + (length - 11U));
- std::stringstream ss;
- ss << peerId << " " << payload;
-
- ::Log(9999U, nullptr, "%s", ss.str().c_str());
+ ::ActivityLog("%u %s", peerId, payload.c_str());
}
else {
writePeerNAK(peerId, TAG_TRANSFER_ACT_LOG);
diff --git a/src/fne/network/fne/TagDMRData.cpp b/src/fne/network/fne/TagDMRData.cpp
index f5c4b0f7..91382b17 100644
--- a/src/fne/network/fne/TagDMRData.cpp
+++ b/src/fne/network/fne/TagDMRData.cpp
@@ -239,6 +239,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
m_network->m_callInProgress = true;
}
}
+ m_network->m_frameQueue->flushQueue();
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
@@ -257,11 +258,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
// perform TGID route rewrites if configured
routeRewrite(outboundPeerBuffer, peerId, dmrData, dataType, dstId, slotNo);
- peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId);
+ peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_DMR }, outboundPeerBuffer, len, pktSeq, streamId, true);
}
}
-
m_network->m_frameQueue->flushQueue();
+
return true;
}
diff --git a/src/fne/network/fne/TagNXDNData.cpp b/src/fne/network/fne/TagNXDNData.cpp
index 5c6e0883..cf4d80b4 100644
--- a/src/fne/network/fne/TagNXDNData.cpp
+++ b/src/fne/network/fne/TagNXDNData.cpp
@@ -209,6 +209,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
m_network->m_callInProgress = true;
}
}
+ m_network->m_frameQueue->flushQueue();
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
@@ -227,11 +228,11 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI
// perform TGID route rewrites if configured
routeRewrite(outboundPeerBuffer, peerId, messageType, dstId);
- peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId);
+ peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_NXDN }, outboundPeerBuffer, len, pktSeq, streamId, true);
}
}
-
m_network->m_frameQueue->flushQueue();
+
return true;
}
diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp
index 38c41f7f..05f35431 100644
--- a/src/fne/network/fne/TagP25Data.cpp
+++ b/src/fne/network/fne/TagP25Data.cpp
@@ -252,6 +252,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
m_network->m_callInProgress = true;
}
}
+ m_network->m_frameQueue->flushQueue();
// repeat traffic to upstream peers
if (m_network->m_host->m_peerNetworks.size() > 0 && !tg.config().parrot()) {
@@ -270,11 +271,11 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
// perform TGID route rewrites if configured
routeRewrite(outboundPeerBuffer, peerId, duid, dstId);
- peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId);
+ peer.second->writeMaster({ NET_FUNC_PROTOCOL, NET_PROTOCOL_SUBFUNC_P25 }, outboundPeerBuffer, len, pktSeq, streamId, true);
}
}
-
m_network->m_frameQueue->flushQueue();
+
return true;
}