diff --git a/P25Parrot/Log.cpp b/P25Parrot/Log.cpp new file mode 100644 index 0000000..e289fe3 --- /dev/null +++ b/P25Parrot/Log.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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 "Log.h" + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +static unsigned int m_fileLevel = 2U; +static std::string m_filePath; +static std::string m_fileRoot; + +static FILE* m_fpLog = NULL; + +static unsigned int m_displayLevel = 2U; + +static struct tm m_tm; + +static char LEVELS[] = " DMIWEF"; + +static bool LogOpen() +{ + if (m_fileLevel == 0U) + return true; + + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { + if (m_fpLog != NULL) + return true; + } else { + if (m_fpLog != NULL) + ::fclose(m_fpLog); + } + + char filename[50U]; +#if defined(_WIN32) || defined(_WIN64) + ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif + + m_fpLog = ::fopen(filename, "a+t"); + m_tm = *tm; + + return m_fpLog != NULL; +} + +bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) +{ + m_filePath = filePath; + m_fileRoot = fileRoot; + m_fileLevel = fileLevel; + m_displayLevel = displayLevel; + return ::LogOpen(); +} + +void LogFinalise() +{ + if (m_fpLog != NULL) + ::fclose(m_fpLog); +} + +void Log(unsigned int level, const char* fmt, ...) +{ + assert(fmt != NULL); + + char buffer[300U]; +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + ::GetSystemTime(&st); + + ::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + struct timeval now; + ::gettimeofday(&now, NULL); + + struct tm* tm = ::gmtime(&now.tv_sec); + + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U); +#endif + + va_list vl; + va_start(vl, fmt); + + ::vsprintf(buffer + ::strlen(buffer), fmt, vl); + + va_end(vl); + + if (level >= m_fileLevel && m_fileLevel != 0U) { + bool ret = ::LogOpen(); + if (!ret) + return; + + ::fprintf(m_fpLog, "%s\n", buffer); + ::fflush(m_fpLog); + } + + if (level >= m_displayLevel && m_displayLevel != 0U) { + ::fprintf(stdout, "%s\n", buffer); + ::fflush(stdout); + } + + if (level == 6U) { // Fatal + ::fclose(m_fpLog); + exit(1); + } +} diff --git a/P25Parrot/Log.h b/P25Parrot/Log.h new file mode 100644 index 0000000..d671ef9 --- /dev/null +++ b/P25Parrot/Log.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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(LOG_H) +#define LOG_H + +#include + +#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) +#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) +#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__) +#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__) +#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__) + +extern void Log(unsigned int level, const char* fmt, ...); + +extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); +extern void LogFinalise(); + +#endif diff --git a/P25Parrot/Makefile b/P25Parrot/Makefile index e0bcaf0..dba3740 100644 --- a/P25Parrot/Makefile +++ b/P25Parrot/Makefile @@ -4,7 +4,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x LIBS = LDFLAGS = -g -OBJECTS = Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o +OBJECTS = Log.o Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o all: P25Parrot diff --git a/P25Parrot/Makefile.Solaris b/P25Parrot/Makefile.Solaris index 0a637d8..7a46f5f 100644 --- a/P25Parrot/Makefile.Solaris +++ b/P25Parrot/Makefile.Solaris @@ -4,7 +4,7 @@ CFLAGS = -g -O3 -Wall -std=c++0x LIBS = -lsocket LDFLAGS = -g -OBJECTS = Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o +OBJECTS = Log.o Network.o P25Parrot.o Parrot.o StopWatch.o Timer.o UDPSocket.o Utils.o all: P25Parrot diff --git a/P25Parrot/Network.cpp b/P25Parrot/Network.cpp index 647a2ed..7c366cb 100644 --- a/P25Parrot/Network.cpp +++ b/P25Parrot/Network.cpp @@ -18,6 +18,7 @@ #include "Network.h" #include "Utils.h" +#include "Log.h" #include #include @@ -25,11 +26,10 @@ const unsigned int BUFFER_LENGTH = 200U; -CNetwork::CNetwork(unsigned int port, bool debug) : +CNetwork::CNetwork(unsigned int port) : m_socket(port), m_address(), m_port(0U), -m_debug(debug), m_buffer(1000U, "P25 Network") { } @@ -40,7 +40,7 @@ CNetwork::~CNetwork() bool CNetwork::open() { - ::fprintf(stdout, "Opening P25 network connection\n"); + LogInfo("Opening P25 network connection"); return m_socket.open(); } @@ -52,8 +52,7 @@ bool CNetwork::write(const unsigned char* data, unsigned int length) assert(data != NULL); - if (m_debug) - CUtils::dump(1U, "P25 Network Data Sent", data, length); + CUtils::dump(1U, "P25 Network Data Sent", data, length); return m_socket.write(data, length, m_address, m_port); } @@ -71,8 +70,7 @@ void CNetwork::clock(unsigned int ms) m_address.s_addr = address.s_addr; m_port = port; - if (m_debug) - CUtils::dump(1U, "P25 Network Data Received", buffer, length); + CUtils::dump(1U, "P25 Network Data Received", buffer, length); unsigned char l = length; m_buffer.addData(&l, 1U); @@ -104,5 +102,5 @@ void CNetwork::close() { m_socket.close(); - ::fprintf(stdout, "Closing P25 network connection\n"); + LogInfo("Closing P25 network connection"); } diff --git a/P25Parrot/Network.h b/P25Parrot/Network.h index 0b48a49..135c578 100644 --- a/P25Parrot/Network.h +++ b/P25Parrot/Network.h @@ -27,7 +27,7 @@ class CNetwork { public: - CNetwork(unsigned int port, bool debug); + CNetwork(unsigned int port); ~CNetwork(); bool open(); @@ -46,7 +46,6 @@ private: CUDPSocket m_socket; in_addr m_address; unsigned int m_port; - bool m_debug; CRingBuffer m_buffer; }; diff --git a/P25Parrot/P25Parrot.cpp b/P25Parrot/P25Parrot.cpp index 0e5c21a..d8b55b8 100644 --- a/P25Parrot/P25Parrot.cpp +++ b/P25Parrot/P25Parrot.cpp @@ -22,31 +22,42 @@ #include "Network.h" #include "Version.h" #include "Timer.h" +#include "Log.h" #include #include +#include int main(int argc, char** argv) { if (argc == 1) { - ::fprintf(stderr, "Usage: P25Parrot \n"); + ::fprintf(stderr, "Usage: P25Parrot [-d|--debug] \n"); return 1; } - unsigned int port = ::atoi(argv[1]); + unsigned int n = 1U; + + bool debug = false; + if (::strcmp(argv[1], "-d") == 0 || ::strcmp(argv[1], "--debug") == 0) { + debug = true; + n = 2U; + } + + unsigned int port = ::atoi(argv[n]); if (port == 0U) { - ::fprintf(stderr, "P25Parrot: invalid port number\n"); + ::fprintf(stderr, "P25Parrot: invalid port number - %s\n", argv[n]); return 1; } - CP25Parrot parrot(port); + CP25Parrot parrot(port, debug); parrot.run(); return 0; } -CP25Parrot::CP25Parrot(unsigned int port) : -m_port(port) +CP25Parrot::CP25Parrot(unsigned int port, bool debug) : +m_port(port), +m_debug(debug) { } @@ -56,12 +67,20 @@ CP25Parrot::~CP25Parrot() void CP25Parrot::run() { - CParrot parrot(180U); - CNetwork network(m_port, false); - - bool ret = network.open(); - if (!ret) + bool ret = ::LogInitialise(".", "P25Parrot", m_debug ? 1U : 2U, m_debug ? 1U : 2U); + if (!ret) { + ::fprintf(stderr, "P25Parrot: unable to open the log file\n"); return; + } + + CParrot parrot(180U); + CNetwork network(m_port); + + ret = network.open(); + if (!ret) { + ::LogFinalise(); + return; + } CStopWatch stopWatch; stopWatch.start(); @@ -73,7 +92,7 @@ void CP25Parrot::run() unsigned int count = 0U; bool playing = false; - ::fprintf(stdout, "Starting P25Parrot-%s\n", VERSION); + LogInfo("Starting P25Parrot-%s", VERSION); for (;;) { unsigned char buffer[200U]; @@ -84,7 +103,7 @@ void CP25Parrot::run() watchdogTimer.start(); if ((buffer[0U] == 0x6AU && buffer[15U] == 0x00U) || (buffer[0U] == 0x73U && buffer[15U] == 0x00U)) { - ::fprintf(stdout, "Received end of transmission\n"); + LogDebug("Received end of transmission"); turnaroundTimer.start(); watchdogTimer.stop(); parrot.end(); @@ -123,7 +142,7 @@ void CP25Parrot::run() turnaroundTimer.clock(ms); if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) { - ::fprintf(stdout, "Network watchdog has expired\n"); + LogDebug("Network watchdog has expired"); turnaroundTimer.start(); watchdogTimer.stop(); parrot.end(); @@ -139,4 +158,6 @@ void CP25Parrot::run() } network.close(); + + ::LogFinalise(); } diff --git a/P25Parrot/P25Parrot.h b/P25Parrot/P25Parrot.h index e813e7f..1fb4d28 100644 --- a/P25Parrot/P25Parrot.h +++ b/P25Parrot/P25Parrot.h @@ -22,13 +22,14 @@ class CP25Parrot { public: - CP25Parrot(unsigned int port); + CP25Parrot(unsigned int port, bool debug); ~CP25Parrot(); void run(); private: unsigned int m_port; + bool m_debug; }; #endif diff --git a/P25Parrot/P25Parrot.vcxproj b/P25Parrot/P25Parrot.vcxproj index 0178db9..48a642f 100644 --- a/P25Parrot/P25Parrot.vcxproj +++ b/P25Parrot/P25Parrot.vcxproj @@ -146,6 +146,7 @@ + @@ -157,6 +158,7 @@ + diff --git a/P25Parrot/P25Parrot.vcxproj.filters b/P25Parrot/P25Parrot.vcxproj.filters index 7505647..05b9d69 100644 --- a/P25Parrot/P25Parrot.vcxproj.filters +++ b/P25Parrot/P25Parrot.vcxproj.filters @@ -38,6 +38,9 @@ Header Files + + Header Files + @@ -61,5 +64,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/P25Parrot/Parrot.cpp b/P25Parrot/Parrot.cpp index 912e5af..1e8344f 100644 --- a/P25Parrot/Parrot.cpp +++ b/P25Parrot/Parrot.cpp @@ -24,7 +24,7 @@ CParrot::CParrot(unsigned int timeout) : m_data(NULL), -m_length(timeout * 1550U + 1000U), +m_length(timeout * 1000U + 1000U), m_used(0U), m_ptr(0U) { diff --git a/P25Parrot/RingBuffer.h b/P25Parrot/RingBuffer.h index 186709d..7805005 100644 --- a/P25Parrot/RingBuffer.h +++ b/P25Parrot/RingBuffer.h @@ -19,6 +19,8 @@ #ifndef RingBuffer_H #define RingBuffer_H +#include "Log.h" + #include #include #include @@ -48,7 +50,7 @@ public: bool addData(const T* buffer, unsigned int nSamples) { if (nSamples >= freeSpace()) { - ::fprintf(stderr, "**** Overflow in %s ring buffer, %u >= %u\n", m_name, nSamples, freeSpace()); + LogError("**** Overflow in %s ring buffer, %u >= %u", m_name, nSamples, freeSpace()); return false; } @@ -65,7 +67,7 @@ public: bool getData(T* buffer, unsigned int nSamples) { if (dataSize() < nSamples) { - ::fprintf(stderr, "**** Underflow in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + LogError("**** Underflow in %s ring buffer, %u < %u", m_name, dataSize(), nSamples); return false; } @@ -82,7 +84,7 @@ public: bool peek(T* buffer, unsigned int nSamples) { if (dataSize() < nSamples) { - ::fprintf(stderr, "**** Underflow peek in %s ring buffer, %u < %u\n", m_name, dataSize(), nSamples); + LogError("**** Underflow peek in %s ring buffer, %u < %u", m_name, dataSize(), nSamples); return false; } diff --git a/P25Parrot/UDPSocket.cpp b/P25Parrot/UDPSocket.cpp index 733edd0..733dd20 100644 --- a/P25Parrot/UDPSocket.cpp +++ b/P25Parrot/UDPSocket.cpp @@ -17,6 +17,7 @@ */ #include "UDPSocket.h" +#include "Log.h" #include @@ -37,7 +38,7 @@ m_fd(-1) WSAData data; int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); if (wsaRet != 0) - ::fprintf(stderr, "Error from WSAStartup\n"); + LogError("Error from WSAStartup"); #endif } @@ -50,7 +51,7 @@ m_fd(-1) WSAData data; int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); if (wsaRet != 0) - ::fprintf(stderr, "Error from WSAStartup\n"); + LogError("Error from WSAStartup"); #endif } @@ -77,7 +78,7 @@ in_addr CUDPSocket::lookup(const std::string& hostname) return addr; } - ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + LogError("Cannot find address for host %s", hostname.c_str()); addr.s_addr = INADDR_NONE; return addr; @@ -94,7 +95,7 @@ in_addr CUDPSocket::lookup(const std::string& hostname) return addr; } - ::fprintf(stderr, "Cannot find address for host %s\n", hostname.c_str()); + LogError("Cannot find address for host %s", hostname.c_str()); addr.s_addr = INADDR_NONE; return addr; @@ -106,9 +107,9 @@ bool CUDPSocket::open() m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); if (m_fd < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot create the UDP socket, err: %lu\n", ::GetLastError()); + LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot create the UDP socket, err: %d\n", errno); + LogError("Cannot create the UDP socket, err: %d", errno); #endif return false; } @@ -127,7 +128,7 @@ bool CUDPSocket::open() addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); #endif if (addr.sin_addr.s_addr == INADDR_NONE) { - ::fprintf(stderr, "The local address is invalid - %s\n", m_address.c_str()); + LogError("The local address is invalid - %s", m_address.c_str()); return false; } } @@ -135,21 +136,23 @@ bool CUDPSocket::open() int reuse = 1; if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot set the UDP socket option, err: %lu\n", ::GetLastError()); + LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot set the UDP socket option, err: %d\n", errno); + LogError("Cannot set the UDP socket option, err: %d", errno); #endif return false; } if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Cannot bind the UDP address, err: %lu\n", ::GetLastError()); + LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Cannot bind the UDP address, err: %d\n", errno); + LogError("Cannot bind the UDP address, err: %d", errno); #endif return false; } + + LogInfo("Opening UDP port on %u", m_port); } return true; @@ -177,9 +180,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& addres int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from UDP select, err: %lu\n", ::GetLastError()); + LogError("Error returned from UDP select, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from UDP select, err: %d\n", errno); + LogError("Error returned from UDP select, err: %d", errno); #endif return -1; } @@ -201,9 +204,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& addres #endif if (len <= 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from recvfrom, err: %lu\n", ::GetLastError()); + LogError("Error returned from recvfrom, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from recvfrom, err: %d\n", errno); + LogError("Error returned from recvfrom, err: %d", errno); #endif return -1; } @@ -233,9 +236,9 @@ bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const i #endif if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - ::fprintf(stderr, "Error returned from sendto, err: %lu\n", ::GetLastError()); + LogError("Error returned from sendto, err: %lu", ::GetLastError()); #else - ::fprintf(stderr, "Error returned from sendto, err: %d\n", errno); + LogError("Error returned from sendto, err: %d", errno); #endif return false; } diff --git a/P25Parrot/Utils.cpp b/P25Parrot/Utils.cpp index d5b2c75..49ded13 100644 --- a/P25Parrot/Utils.cpp +++ b/P25Parrot/Utils.cpp @@ -12,6 +12,7 @@ */ #include "Utils.h" +#include "Log.h" #include #include @@ -27,7 +28,7 @@ void CUtils::dump(int level, const std::string& title, const unsigned char* data { assert(data != NULL); - ::fprintf(stdout, "%s\n", title.c_str()); + ::Log(level, "%s", title.c_str()); unsigned int offset = 0U; @@ -58,7 +59,7 @@ void CUtils::dump(int level, const std::string& title, const unsigned char* data output += '*'; - ::fprintf(stdout, "%04X: %s\n", offset, output.c_str()); + ::Log(level, "%04X: %s", offset, output.c_str()); offset += 16U;