mirror of
https://github.com/g4klx/NXDNClients.git
synced 2026-08-26 12:32:43 -04:00
Merge branch 'mqtt' into json_hosts
This commit is contained in:
commit
385677bfda
24 changed files with 1103 additions and 823 deletions
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2010-2014,2016,2017,2018,2020,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2010-2014,2016,2017,2018,2020,2023,2025 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
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include "APRSWriter.h"
|
||||
#include "MQTTConnection.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
|
@ -24,7 +25,10 @@
|
|||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
CAPRSWriter::CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& address, unsigned short port, bool debug) :
|
||||
// In Log.cpp
|
||||
extern CMQTTConnection* m_mqtt;
|
||||
|
||||
CAPRSWriter::CAPRSWriter(const std::string& callsign, const std::string& suffix, bool debug) :
|
||||
m_idTimer(1000U),
|
||||
m_callsign(callsign),
|
||||
m_debug(debug),
|
||||
|
|
@ -34,10 +38,7 @@ m_latitude(0.0F),
|
|||
m_longitude(0.0F),
|
||||
m_height(0),
|
||||
m_desc(),
|
||||
m_symbol(),
|
||||
m_aprsAddr(),
|
||||
m_aprsAddrLen(0U),
|
||||
m_aprsSocket()
|
||||
m_symbol()
|
||||
#if defined(USE_GPSD)
|
||||
,m_gpsdEnabled(false),
|
||||
m_gpsdAddress(),
|
||||
|
|
@ -46,16 +47,11 @@ m_gpsdData()
|
|||
#endif
|
||||
{
|
||||
assert(!callsign.empty());
|
||||
assert(!address.empty());
|
||||
assert(port > 0U);
|
||||
|
||||
if (!suffix.empty()) {
|
||||
m_callsign.append("-");
|
||||
m_callsign.append(suffix.substr(0U, 1U));
|
||||
}
|
||||
|
||||
if (CUDPSocket::lookup(address, port, m_aprsAddr, m_aprsAddrLen) != 0)
|
||||
m_aprsAddrLen = 0U;
|
||||
}
|
||||
|
||||
CAPRSWriter::~CAPRSWriter()
|
||||
|
|
@ -91,11 +87,6 @@ void CAPRSWriter::setGPSDLocation(const std::string& address, const std::string&
|
|||
|
||||
bool CAPRSWriter::open()
|
||||
{
|
||||
if (m_aprsAddrLen == 0U) {
|
||||
LogError("Unable to resolve the address of the APRS Gateway");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(USE_GPSD)
|
||||
if (m_gpsdEnabled) {
|
||||
int ret = ::gps_open(m_gpsdAddress.c_str(), m_gpsdPort.c_str(), &m_gpsdData);
|
||||
|
|
@ -109,12 +100,6 @@ bool CAPRSWriter::open()
|
|||
LogMessage("Connected to GPSD");
|
||||
}
|
||||
#endif
|
||||
bool ret = m_aprsSocket.open(m_aprsAddr);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
LogMessage("Opened connection to the APRS Gateway");
|
||||
|
||||
m_idTimer.setTimeout(60U);
|
||||
m_idTimer.start();
|
||||
|
||||
|
|
@ -128,7 +113,7 @@ void CAPRSWriter::write(const char* data)
|
|||
if (m_debug)
|
||||
LogDebug("APRS ==> %s", data);
|
||||
|
||||
m_aprsSocket.write((unsigned char*)data, (unsigned int)::strlen(data), m_aprsAddr, m_aprsAddrLen);
|
||||
m_mqtt->publish("aprs-gateway/aprs", data);
|
||||
}
|
||||
|
||||
void CAPRSWriter::clock(unsigned int ms)
|
||||
|
|
@ -156,8 +141,6 @@ void CAPRSWriter::clock(unsigned int ms)
|
|||
|
||||
void CAPRSWriter::close()
|
||||
{
|
||||
m_aprsSocket.close();
|
||||
|
||||
#if defined(USE_GPSD)
|
||||
if (m_gpsdEnabled) {
|
||||
::gps_stream(&m_gpsdData, WATCH_DISABLE, nullptr);
|
||||
|
|
@ -228,9 +211,6 @@ void CAPRSWriter::sendIdFrameFixed()
|
|||
lon, (m_longitude < 0.0F) ? 'W' : 'E', symbol[1],
|
||||
float(m_height) * 3.28F, band, desc);
|
||||
|
||||
if (m_debug)
|
||||
LogDebug("APRS ==> %s", output);
|
||||
|
||||
write(output);
|
||||
}
|
||||
|
||||
|
|
@ -336,9 +316,6 @@ void CAPRSWriter::sendIdFrameMobile()
|
|||
|
||||
::sprintf(output + ::strlen(output), "%s %s\r\n", band, desc);
|
||||
|
||||
if (m_debug)
|
||||
LogDebug("APRS ==> %s", output);
|
||||
|
||||
write(output);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2010,2011,2012,2016,2017,2018,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2010,2011,2012,2016,2017,2018,2020,2023 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
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
#ifndef APRSWriter_H
|
||||
#define APRSWriter_H
|
||||
|
||||
#include "UDPSocket.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <string>
|
||||
|
|
@ -42,7 +41,7 @@
|
|||
|
||||
class CAPRSWriter {
|
||||
public:
|
||||
CAPRSWriter(const std::string& callsign, const std::string& suffix, const std::string& address, unsigned short port, bool debug);
|
||||
CAPRSWriter(const std::string& callsign, const std::string& suffix, bool debug);
|
||||
~CAPRSWriter();
|
||||
|
||||
bool open();
|
||||
|
|
@ -70,9 +69,6 @@ private:
|
|||
int m_height;
|
||||
std::string m_desc;
|
||||
std::string m_symbol;
|
||||
sockaddr_storage m_aprsAddr;
|
||||
unsigned int m_aprsAddrLen;
|
||||
CUDPSocket m_aprsSocket;
|
||||
#if defined(USE_GPSD)
|
||||
bool m_gpsdEnabled;
|
||||
std::string m_gpsdAddress;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2017,2018,2020,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018,2020,2023,2025 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
|
||||
|
|
@ -34,6 +34,7 @@ enum class SECTION {
|
|||
VOICE,
|
||||
LOG,
|
||||
APRS,
|
||||
MQTT,
|
||||
NETWORK,
|
||||
GPSD,
|
||||
REMOTE_COMMANDS
|
||||
|
|
@ -63,16 +64,18 @@ m_voiceEnabled(true),
|
|||
m_voiceLanguage("en_GB"),
|
||||
m_voiceDirectory(),
|
||||
m_logDisplayLevel(0U),
|
||||
m_logFileLevel(0U),
|
||||
m_logFilePath(),
|
||||
m_logFileRoot(),
|
||||
m_logFileRotate(true),
|
||||
m_logMQTTLevel(0U),
|
||||
m_aprsEnabled(false),
|
||||
m_aprsAddress("127.0.0.1"),
|
||||
m_aprsPort(8673U),
|
||||
m_aprsSuffix(),
|
||||
m_aprsDescription(),
|
||||
m_aprsSymbol(),
|
||||
m_mqttAddress("127.0.0.1"),
|
||||
m_mqttPort(1883U),
|
||||
m_mqttKeepalive(60U),
|
||||
m_mqttName("nxdn-gateway"),
|
||||
m_mqttAuthEnabled(false),
|
||||
m_mqttUsername(),
|
||||
m_mqttPassword(),
|
||||
m_networkPort(0U),
|
||||
m_networkHosts1(),
|
||||
m_networkHosts2(),
|
||||
|
|
@ -88,8 +91,7 @@ m_networkDebug(false),
|
|||
m_gpsdEnabled(false),
|
||||
m_gpsdAddress(),
|
||||
m_gpsdPort(),
|
||||
m_remoteCommandsEnabled(false),
|
||||
m_remoteCommandsPort(6075U)
|
||||
m_remoteCommandsEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -99,193 +101,198 @@ CConf::~CConf()
|
|||
|
||||
bool CConf::read()
|
||||
{
|
||||
FILE* fp = ::fopen(m_file.c_str(), "rt");
|
||||
if (fp == nullptr) {
|
||||
::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str());
|
||||
return false;
|
||||
}
|
||||
FILE* fp = ::fopen(m_file.c_str(), "rt");
|
||||
if (fp == nullptr) {
|
||||
::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
SECTION section = SECTION::NONE;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
while (::fgets(buffer, BUFFER_SIZE, fp) != nullptr) {
|
||||
if (buffer[0U] == '#')
|
||||
continue;
|
||||
char buffer[BUFFER_SIZE];
|
||||
while (::fgets(buffer, BUFFER_SIZE, fp) != nullptr) {
|
||||
if (buffer[0U] == '#')
|
||||
continue;
|
||||
|
||||
if (buffer[0U] == '[') {
|
||||
if (::strncmp(buffer, "[General]", 9U) == 0)
|
||||
section = SECTION::GENERAL;
|
||||
else if (::strncmp(buffer, "[Info]", 6U) == 0)
|
||||
section = SECTION::INFO;
|
||||
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
|
||||
section = SECTION::ID_LOOKUP;
|
||||
else if (::strncmp(buffer, "[Voice]", 7U) == 0)
|
||||
section = SECTION::VOICE;
|
||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
||||
section = SECTION::LOG;
|
||||
else if (::strncmp(buffer, "[APRS]", 6U) == 0)
|
||||
section = SECTION::APRS;
|
||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
||||
section = SECTION::NETWORK;
|
||||
else if (::strncmp(buffer, "[GPSD]", 6U) == 0)
|
||||
section = SECTION::GPSD;
|
||||
else if (::strncmp(buffer, "[Remote Commands]", 17U) == 0)
|
||||
section = SECTION::REMOTE_COMMANDS;
|
||||
else
|
||||
section = SECTION::NONE;
|
||||
if (buffer[0U] == '[') {
|
||||
if (::strncmp(buffer, "[General]", 9U) == 0)
|
||||
section = SECTION::GENERAL;
|
||||
else if (::strncmp(buffer, "[Info]", 6U) == 0)
|
||||
section = SECTION::INFO;
|
||||
else if (::strncmp(buffer, "[Id Lookup]", 11U) == 0)
|
||||
section = SECTION::ID_LOOKUP;
|
||||
else if (::strncmp(buffer, "[Voice]", 7U) == 0)
|
||||
section = SECTION::VOICE;
|
||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
||||
section = SECTION::LOG;
|
||||
else if (::strncmp(buffer, "[APRS]", 6U) == 0)
|
||||
section = SECTION::APRS;
|
||||
else if (::strncmp(buffer, "[MQTT]", 6U) == 0)
|
||||
section = SECTION::MQTT;
|
||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
||||
section = SECTION::NETWORK;
|
||||
else if (::strncmp(buffer, "[GPSD]", 6U) == 0)
|
||||
section = SECTION::GPSD;
|
||||
else if (::strncmp(buffer, "[Remote Commands]", 17U) == 0)
|
||||
section = SECTION::REMOTE_COMMANDS;
|
||||
else
|
||||
section = SECTION::NONE;
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
char* key = ::strtok(buffer, " \t=\r\n");
|
||||
if (key == nullptr)
|
||||
continue;
|
||||
char* key = ::strtok(buffer, " \t=\r\n");
|
||||
if (key == nullptr)
|
||||
continue;
|
||||
|
||||
char* value = ::strtok(nullptr, "\r\n");
|
||||
if (value == nullptr)
|
||||
continue;
|
||||
char* value = ::strtok(nullptr, "\r\n");
|
||||
if (value == nullptr)
|
||||
continue;
|
||||
|
||||
// Remove quotes from the value
|
||||
size_t len = ::strlen(value);
|
||||
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
|
||||
value[len - 1U] = '\0';
|
||||
value++;
|
||||
} else {
|
||||
char *p;
|
||||
// Remove quotes from the value
|
||||
size_t len = ::strlen(value);
|
||||
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
|
||||
value[len - 1U] = '\0';
|
||||
value++;
|
||||
} else {
|
||||
char *p;
|
||||
|
||||
// if value is not quoted, remove after # (to make comment)
|
||||
if ((p = strchr(value, '#')) != nullptr)
|
||||
*p = '\0';
|
||||
// if value is not quoted, remove after # (to make comment)
|
||||
if ((p = strchr(value, '#')) != nullptr)
|
||||
*p = '\0';
|
||||
|
||||
// remove trailing tab/space
|
||||
for (p = value + strlen(value) - 1U; p >= value && (*p == '\t' || *p == ' '); p--)
|
||||
*p = '\0';
|
||||
}
|
||||
// remove trailing tab/space
|
||||
for (p = value + strlen(value) - 1U; p >= value && (*p == '\t' || *p == ' '); p--)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
if (section == SECTION::GENERAL) {
|
||||
if (::strcmp(key, "Callsign") == 0) {
|
||||
// Convert the callsign to upper case
|
||||
for (unsigned int i = 0U; value[i] != 0; i++)
|
||||
value[i] = ::toupper(value[i]);
|
||||
m_callsign = value;
|
||||
} else if (::strcmp(key, "Suffix") == 0) {
|
||||
// Convert the callsign to upper case
|
||||
for (unsigned int i = 0U; value[i] != 0; i++)
|
||||
value[i] = ::toupper(value[i]);
|
||||
m_suffix = value;
|
||||
} else if (::strcmp(key, "RptProtocol") == 0)
|
||||
m_rptProtocol = value;
|
||||
else if (::strcmp(key, "RptAddress") == 0)
|
||||
m_rptAddress = value;
|
||||
else if (::strcmp(key, "RptPort") == 0)
|
||||
m_rptPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "LocalPort") == 0)
|
||||
m_myPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_debug = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Daemon") == 0)
|
||||
m_daemon = ::atoi(value) == 1;
|
||||
} else if (section == SECTION::INFO) {
|
||||
if (::strcmp(key, "TXFrequency") == 0)
|
||||
m_txFrequency = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "RXFrequency") == 0)
|
||||
m_rxFrequency = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Power") == 0)
|
||||
m_power = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Latitude") == 0)
|
||||
m_latitude = float(::atof(value));
|
||||
else if (::strcmp(key, "Longitude") == 0)
|
||||
m_longitude = float(::atof(value));
|
||||
else if (::strcmp(key, "Height") == 0)
|
||||
m_height = ::atoi(value);
|
||||
else if (::strcmp(key, "Name") == 0)
|
||||
m_name = value;
|
||||
else if (::strcmp(key, "Description") == 0)
|
||||
m_description = value;
|
||||
} else if (section == SECTION::ID_LOOKUP) {
|
||||
if (::strcmp(key, "Name") == 0)
|
||||
m_lookupName = value;
|
||||
else if (::strcmp(key, "Time") == 0)
|
||||
m_lookupTime = (unsigned int)::atoi(value);
|
||||
} else if (section == SECTION::VOICE) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_voiceEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Language") == 0)
|
||||
m_voiceLanguage = value;
|
||||
else if (::strcmp(key, "Directory") == 0)
|
||||
m_voiceDirectory = value;
|
||||
} else if (section == SECTION::LOG) {
|
||||
if (::strcmp(key, "FilePath") == 0)
|
||||
m_logFilePath = value;
|
||||
else if (::strcmp(key, "FileRoot") == 0)
|
||||
m_logFileRoot = value;
|
||||
else if (::strcmp(key, "FileLevel") == 0)
|
||||
m_logFileLevel = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "DisplayLevel") == 0)
|
||||
m_logDisplayLevel = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "FileRotate") == 0)
|
||||
m_logFileRotate = ::atoi(value) == 1;
|
||||
} else if (section == SECTION::APRS) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_aprsEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_aprsAddress = value;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_aprsPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Suffix") == 0)
|
||||
m_aprsSuffix = value;
|
||||
else if (::strcmp(key, "Description") == 0)
|
||||
m_aprsDescription = value;
|
||||
else if (::strcmp(key, "Symbol") == 0)
|
||||
m_aprsSymbol = value;
|
||||
} else if (section == SECTION::NETWORK) {
|
||||
if (::strcmp(key, "Port") == 0)
|
||||
m_networkPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "HostsFile1") == 0)
|
||||
m_networkHosts1 = value;
|
||||
else if (::strcmp(key, "HostsFile2") == 0)
|
||||
m_networkHosts2 = value;
|
||||
else if (::strcmp(key, "ReloadTime") == 0)
|
||||
m_networkReloadTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "ParrotAddress") == 0)
|
||||
m_networkParrotAddress = value;
|
||||
else if (::strcmp(key, "ParrotPort") == 0)
|
||||
m_networkParrotPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "NXDN2DMRAddress") == 0)
|
||||
m_networkNXDN2DMRAddress = value;
|
||||
else if (::strcmp(key, "NXDN2DMRPort") == 0)
|
||||
m_networkNXDN2DMRPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Static") == 0) {
|
||||
char* p = ::strtok(value, ",\r\n");
|
||||
while (p != nullptr) {
|
||||
unsigned short tg = (unsigned short)::atoi(p);
|
||||
m_networkStatic.push_back(tg);
|
||||
p = ::strtok(nullptr, ",\r\n");
|
||||
}
|
||||
} else if (::strcmp(key, "RFHangTime") == 0)
|
||||
m_networkRFHangTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "NetHangTime") == 0)
|
||||
m_networkNetHangTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_networkDebug = ::atoi(value) == 1;
|
||||
} else if (section == SECTION::GPSD) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_gpsdEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_gpsdAddress = value;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_gpsdPort = value;
|
||||
} else if (section == SECTION::REMOTE_COMMANDS) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_remoteCommandsEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_remoteCommandsPort = (unsigned short)::atoi(value);
|
||||
}
|
||||
}
|
||||
if (section == SECTION::GENERAL) {
|
||||
if (::strcmp(key, "Callsign") == 0) {
|
||||
// Convert the callsign to upper case
|
||||
for (unsigned int i = 0U; value[i] != 0; i++)
|
||||
value[i] = ::toupper(value[i]);
|
||||
m_callsign = value;
|
||||
} else if (::strcmp(key, "Suffix") == 0) {
|
||||
// Convert the callsign to upper case
|
||||
for (unsigned int i = 0U; value[i] != 0; i++)
|
||||
value[i] = ::toupper(value[i]);
|
||||
m_suffix = value;
|
||||
} else if (::strcmp(key, "RptProtocol") == 0)
|
||||
m_rptProtocol = value;
|
||||
else if (::strcmp(key, "RptAddress") == 0)
|
||||
m_rptAddress = value;
|
||||
else if (::strcmp(key, "RptPort") == 0)
|
||||
m_rptPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "LocalPort") == 0)
|
||||
m_myPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_debug = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Daemon") == 0)
|
||||
m_daemon = ::atoi(value) == 1;
|
||||
} else if (section == SECTION::INFO) {
|
||||
if (::strcmp(key, "TXFrequency") == 0)
|
||||
m_txFrequency = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "RXFrequency") == 0)
|
||||
m_rxFrequency = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Power") == 0)
|
||||
m_power = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Latitude") == 0)
|
||||
m_latitude = float(::atof(value));
|
||||
else if (::strcmp(key, "Longitude") == 0)
|
||||
m_longitude = float(::atof(value));
|
||||
else if (::strcmp(key, "Height") == 0)
|
||||
m_height = ::atoi(value);
|
||||
else if (::strcmp(key, "Name") == 0)
|
||||
m_name = value;
|
||||
else if (::strcmp(key, "Description") == 0)
|
||||
m_description = value;
|
||||
} else if (section == SECTION::ID_LOOKUP) {
|
||||
if (::strcmp(key, "Name") == 0)
|
||||
m_lookupName = value;
|
||||
else if (::strcmp(key, "Time") == 0)
|
||||
m_lookupTime = (unsigned int)::atoi(value);
|
||||
} else if (section == SECTION::VOICE) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_voiceEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Language") == 0)
|
||||
m_voiceLanguage = value;
|
||||
else if (::strcmp(key, "Directory") == 0)
|
||||
m_voiceDirectory = value;
|
||||
} else if (section == SECTION::LOG) {
|
||||
if (::strcmp(key, "DisplayLevel") == 0)
|
||||
m_logDisplayLevel = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "MQTTLevel") == 0)
|
||||
m_logMQTTLevel = (unsigned int)::atoi(value);
|
||||
} else if (section == SECTION::APRS) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_aprsEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Suffix") == 0)
|
||||
m_aprsSuffix = value;
|
||||
else if (::strcmp(key, "Description") == 0)
|
||||
m_aprsDescription = value;
|
||||
else if (::strcmp(key, "Symbol") == 0)
|
||||
m_aprsSymbol = value;
|
||||
} else if (section == SECTION::MQTT) {
|
||||
if (::strcmp(key, "Address") == 0)
|
||||
m_mqttAddress = value;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_mqttPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Keepalive") == 0)
|
||||
m_mqttKeepalive = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Name") == 0)
|
||||
m_mqttName = value;
|
||||
else if (::strcmp(key, "Auth") == 0)
|
||||
m_mqttAuthEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Username") == 0)
|
||||
m_mqttUsername = value;
|
||||
else if (::strcmp(key, "Password") == 0)
|
||||
m_mqttPassword = value;
|
||||
} else if (section == SECTION::NETWORK) {
|
||||
if (::strcmp(key, "Port") == 0)
|
||||
m_networkPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "HostsFile1") == 0)
|
||||
m_networkHosts1 = value;
|
||||
else if (::strcmp(key, "HostsFile2") == 0)
|
||||
m_networkHosts2 = value;
|
||||
else if (::strcmp(key, "ReloadTime") == 0)
|
||||
m_networkReloadTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "ParrotAddress") == 0)
|
||||
m_networkParrotAddress = value;
|
||||
else if (::strcmp(key, "ParrotPort") == 0)
|
||||
m_networkParrotPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "NXDN2DMRAddress") == 0)
|
||||
m_networkNXDN2DMRAddress = value;
|
||||
else if (::strcmp(key, "NXDN2DMRPort") == 0)
|
||||
m_networkNXDN2DMRPort = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Static") == 0) {
|
||||
char* p = ::strtok(value, ",\r\n");
|
||||
while (p != nullptr) {
|
||||
unsigned short tg = (unsigned short)::atoi(p);
|
||||
m_networkStatic.push_back(tg);
|
||||
p = ::strtok(nullptr, ",\r\n");
|
||||
}
|
||||
} else if (::strcmp(key, "RFHangTime") == 0)
|
||||
m_networkRFHangTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "NetHangTime") == 0)
|
||||
m_networkNetHangTime = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_networkDebug = ::atoi(value) == 1;
|
||||
} else if (section == SECTION::GPSD) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_gpsdEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_gpsdAddress = value;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_gpsdPort = value;
|
||||
} else if (section == SECTION::REMOTE_COMMANDS) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_remoteCommandsEnabled = ::atoi(value) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
::fclose(fp);
|
||||
::fclose(fp);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CConf::getCallsign() const
|
||||
|
|
@ -398,16 +405,6 @@ bool CConf::getAPRSEnabled() const
|
|||
return m_aprsEnabled;
|
||||
}
|
||||
|
||||
std::string CConf::getAPRSAddress() const
|
||||
{
|
||||
return m_aprsAddress;
|
||||
}
|
||||
|
||||
unsigned short CConf::getAPRSPort() const
|
||||
{
|
||||
return m_aprsPort;
|
||||
}
|
||||
|
||||
std::string CConf::getAPRSSuffix() const
|
||||
{
|
||||
return m_aprsSuffix;
|
||||
|
|
@ -423,29 +420,49 @@ std::string CConf::getAPRSSymbol() const
|
|||
return m_aprsSymbol;
|
||||
}
|
||||
|
||||
std::string CConf::getMQTTAddress() const
|
||||
{
|
||||
return m_mqttAddress;
|
||||
}
|
||||
|
||||
unsigned short CConf::getMQTTPort() const
|
||||
{
|
||||
return m_mqttPort;
|
||||
}
|
||||
|
||||
unsigned int CConf::getMQTTKeepalive() const
|
||||
{
|
||||
return m_mqttKeepalive;
|
||||
}
|
||||
|
||||
std::string CConf::getMQTTName() const
|
||||
{
|
||||
return m_mqttName;
|
||||
}
|
||||
|
||||
bool CConf::getMQTTAuthEnabled() const
|
||||
{
|
||||
return m_mqttAuthEnabled;
|
||||
}
|
||||
|
||||
std::string CConf::getMQTTUsername() const
|
||||
{
|
||||
return m_mqttUsername;
|
||||
}
|
||||
|
||||
std::string CConf::getMQTTPassword() const
|
||||
{
|
||||
return m_mqttPassword;
|
||||
}
|
||||
|
||||
unsigned int CConf::getLogDisplayLevel() const
|
||||
{
|
||||
return m_logDisplayLevel;
|
||||
}
|
||||
|
||||
unsigned int CConf::getLogFileLevel() const
|
||||
unsigned int CConf::getLogMQTTLevel() const
|
||||
{
|
||||
return m_logFileLevel;
|
||||
}
|
||||
|
||||
std::string CConf::getLogFilePath() const
|
||||
{
|
||||
return m_logFilePath;
|
||||
}
|
||||
|
||||
std::string CConf::getLogFileRoot() const
|
||||
{
|
||||
return m_logFileRoot;
|
||||
}
|
||||
|
||||
bool CConf::getLogFileRotate() const
|
||||
{
|
||||
return m_logFileRotate;
|
||||
return m_logMQTTLevel;
|
||||
}
|
||||
|
||||
unsigned short CConf::getNetworkPort() const
|
||||
|
|
@ -528,7 +545,3 @@ bool CConf::getRemoteCommandsEnabled() const
|
|||
return m_remoteCommandsEnabled;
|
||||
}
|
||||
|
||||
unsigned short CConf::getRemoteCommandsPort() const
|
||||
{
|
||||
return m_remoteCommandsPort;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018,2020,2023,2025 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
|
||||
|
|
@ -25,137 +25,143 @@
|
|||
class CConf
|
||||
{
|
||||
public:
|
||||
CConf(const std::string& file);
|
||||
~CConf();
|
||||
CConf(const std::string& file);
|
||||
~CConf();
|
||||
|
||||
bool read();
|
||||
bool read();
|
||||
|
||||
// The General section
|
||||
std::string getCallsign() const;
|
||||
std::string getSuffix() const;
|
||||
std::string getRptProtocol() const;
|
||||
std::string getRptAddress() const;
|
||||
unsigned short getRptPort() const;
|
||||
unsigned short getMyPort() const;
|
||||
bool getDebug() const;
|
||||
bool getDaemon() const;
|
||||
// The General section
|
||||
std::string getCallsign() const;
|
||||
std::string getSuffix() const;
|
||||
std::string getRptProtocol() const;
|
||||
std::string getRptAddress() const;
|
||||
|
||||
// The Info section
|
||||
unsigned int getRxFrequency() const;
|
||||
unsigned int getTxFrequency() const;
|
||||
unsigned int getPower() const;
|
||||
float getLatitude() const;
|
||||
float getLongitude() const;
|
||||
int getHeight() const;
|
||||
std::string getName() const;
|
||||
std::string getDescription() const;
|
||||
unsigned short getRptPort() const;
|
||||
unsigned short getMyPort() const;
|
||||
bool getDebug() const;
|
||||
bool getDaemon() const;
|
||||
|
||||
// The Id Lookup section
|
||||
std::string getLookupName() const;
|
||||
unsigned int getLookupTime() const;
|
||||
// The Info section
|
||||
unsigned int getRxFrequency() const;
|
||||
unsigned int getTxFrequency() const;
|
||||
unsigned int getPower() const;
|
||||
float getLatitude() const;
|
||||
float getLongitude() const;
|
||||
int getHeight() const;
|
||||
std::string getName() const;
|
||||
std::string getDescription() const;
|
||||
|
||||
// The Voice section
|
||||
bool getVoiceEnabled() const;
|
||||
std::string getVoiceLanguage() const;
|
||||
std::string getVoiceDirectory() const;
|
||||
// The Id Lookup section
|
||||
std::string getLookupName() const;
|
||||
unsigned int getLookupTime() const;
|
||||
|
||||
// The APRS section
|
||||
bool getAPRSEnabled() const;
|
||||
std::string getAPRSAddress() const;
|
||||
unsigned short getAPRSPort() const;
|
||||
std::string getAPRSSuffix() const;
|
||||
std::string getAPRSDescription() const;
|
||||
std::string getAPRSSymbol() const;
|
||||
// The Voice section
|
||||
bool getVoiceEnabled() const;
|
||||
std::string getVoiceLanguage() const;
|
||||
std::string getVoiceDirectory() const;
|
||||
|
||||
// The Log section
|
||||
unsigned int getLogDisplayLevel() const;
|
||||
unsigned int getLogFileLevel() const;
|
||||
std::string getLogFilePath() const;
|
||||
std::string getLogFileRoot() const;
|
||||
bool getLogFileRotate() const;
|
||||
// The APRS section
|
||||
bool getAPRSEnabled() const;
|
||||
std::string getAPRSSuffix() const;
|
||||
std::string getAPRSDescription() const;
|
||||
std::string getAPRSSymbol() const;
|
||||
|
||||
// The Network section
|
||||
unsigned short getNetworkPort() const;
|
||||
std::string getNetworkHosts1() const;
|
||||
std::string getNetworkHosts2() const;
|
||||
unsigned int getNetworkReloadTime() const;
|
||||
std::string getNetworkParrotAddress() const;
|
||||
unsigned short getNetworkParrotPort() const;
|
||||
std::string getNetworkNXDN2DMRAddress() const;
|
||||
unsigned short getNetworkNXDN2DMRPort() const;
|
||||
std::vector<unsigned short> getNetworkStatic() const;
|
||||
unsigned int getNetworkRFHangTime() const;
|
||||
unsigned int getNetworkNetHangTime() const;
|
||||
bool getNetworkDebug() const;
|
||||
// The Log section
|
||||
unsigned int getLogDisplayLevel() const;
|
||||
unsigned int getLogMQTTLevel() const;
|
||||
|
||||
// The GPSD section
|
||||
bool getGPSDEnabled() const;
|
||||
std::string getGPSDAddress() const;
|
||||
std::string getGPSDPort() const;
|
||||
// The MQTT section
|
||||
std::string getMQTTAddress() const;
|
||||
unsigned short getMQTTPort() const;
|
||||
unsigned int getMQTTKeepalive() const;
|
||||
std::string getMQTTName() const;
|
||||
bool getMQTTAuthEnabled() const;
|
||||
std::string getMQTTUsername() const;
|
||||
std::string getMQTTPassword() const;
|
||||
|
||||
// The Remote Commands section
|
||||
bool getRemoteCommandsEnabled() const;
|
||||
unsigned short getRemoteCommandsPort() const;
|
||||
// The Network section
|
||||
unsigned short getNetworkPort() const;
|
||||
std::string getNetworkHosts1() const;
|
||||
std::string getNetworkHosts2() const;
|
||||
unsigned int getNetworkReloadTime() const;
|
||||
std::string getNetworkParrotAddress() const;
|
||||
unsigned short getNetworkParrotPort() const;
|
||||
std::string getNetworkNXDN2DMRAddress() const;
|
||||
unsigned short getNetworkNXDN2DMRPort() const;
|
||||
std::vector<unsigned short> getNetworkStatic() const;
|
||||
unsigned int getNetworkRFHangTime() const;
|
||||
unsigned int getNetworkNetHangTime() const;
|
||||
bool getNetworkDebug() const;
|
||||
|
||||
// The GPSD section
|
||||
bool getGPSDEnabled() const;
|
||||
std::string getGPSDAddress() const;
|
||||
std::string getGPSDPort() const;
|
||||
|
||||
// The Remote Commands section
|
||||
bool getRemoteCommandsEnabled() const;
|
||||
|
||||
private:
|
||||
std::string m_file;
|
||||
std::string m_callsign;
|
||||
std::string m_suffix;
|
||||
std::string m_rptProtocol;
|
||||
std::string m_rptAddress;
|
||||
unsigned short m_rptPort;
|
||||
unsigned short m_myPort;
|
||||
bool m_debug;
|
||||
bool m_daemon;
|
||||
std::string m_file;
|
||||
std::string m_callsign;
|
||||
std::string m_suffix;
|
||||
std::string m_rptProtocol;
|
||||
std::string m_rptAddress;
|
||||
unsigned short m_rptPort;
|
||||
unsigned short m_myPort;
|
||||
bool m_debug;
|
||||
bool m_daemon;
|
||||
|
||||
unsigned int m_rxFrequency;
|
||||
unsigned int m_txFrequency;
|
||||
unsigned int m_power;
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
int m_height;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
unsigned int m_rxFrequency;
|
||||
unsigned int m_txFrequency;
|
||||
unsigned int m_power;
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
int m_height;
|
||||
std::string m_name;
|
||||
std::string m_description;
|
||||
|
||||
std::string m_lookupName;
|
||||
unsigned int m_lookupTime;
|
||||
std::string m_lookupName;
|
||||
unsigned int m_lookupTime;
|
||||
|
||||
bool m_voiceEnabled;
|
||||
std::string m_voiceLanguage;
|
||||
std::string m_voiceDirectory;
|
||||
bool m_voiceEnabled;
|
||||
std::string m_voiceLanguage;
|
||||
std::string m_voiceDirectory;
|
||||
|
||||
unsigned int m_logDisplayLevel;
|
||||
unsigned int m_logFileLevel;
|
||||
std::string m_logFilePath;
|
||||
std::string m_logFileRoot;
|
||||
bool m_logFileRotate;
|
||||
unsigned int m_logDisplayLevel;
|
||||
unsigned int m_logMQTTLevel;
|
||||
|
||||
bool m_aprsEnabled;
|
||||
std::string m_aprsAddress;
|
||||
unsigned short m_aprsPort;
|
||||
std::string m_aprsSuffix;
|
||||
std::string m_aprsDescription;
|
||||
std::string m_aprsSymbol;
|
||||
bool m_aprsEnabled;
|
||||
std::string m_aprsSuffix;
|
||||
std::string m_aprsDescription;
|
||||
std::string m_aprsSymbol;
|
||||
|
||||
unsigned short m_networkPort;
|
||||
std::string m_networkHosts1;
|
||||
std::string m_networkHosts2;
|
||||
unsigned int m_networkReloadTime;
|
||||
std::string m_networkParrotAddress;
|
||||
unsigned short m_networkParrotPort;
|
||||
std::string m_networkNXDN2DMRAddress;
|
||||
unsigned short m_networkNXDN2DMRPort;
|
||||
std::vector<unsigned short> m_networkStatic;
|
||||
unsigned int m_networkRFHangTime;
|
||||
unsigned int m_networkNetHangTime;
|
||||
bool m_networkDebug;
|
||||
std::string m_mqttAddress;
|
||||
unsigned short m_mqttPort;
|
||||
unsigned int m_mqttKeepalive;
|
||||
std::string m_mqttName;
|
||||
bool m_mqttAuthEnabled;
|
||||
std::string m_mqttUsername;
|
||||
std::string m_mqttPassword;
|
||||
|
||||
bool m_gpsdEnabled;
|
||||
std::string m_gpsdAddress;
|
||||
std::string m_gpsdPort;
|
||||
unsigned short m_networkPort;
|
||||
std::string m_networkHosts1;
|
||||
std::string m_networkHosts2;
|
||||
unsigned int m_networkReloadTime;
|
||||
std::string m_networkParrotAddress;
|
||||
unsigned short m_networkParrotPort;
|
||||
std::string m_networkNXDN2DMRAddress;
|
||||
unsigned short m_networkNXDN2DMRPort;
|
||||
std::vector<unsigned short> m_networkStatic;
|
||||
unsigned int m_networkRFHangTime;
|
||||
unsigned int m_networkNetHangTime;
|
||||
bool m_networkDebug;
|
||||
|
||||
bool m_remoteCommandsEnabled;
|
||||
unsigned short m_remoteCommandsPort;
|
||||
bool m_gpsdEnabled;
|
||||
std::string m_gpsdAddress;
|
||||
std::string m_gpsdPort;
|
||||
|
||||
bool m_remoteCommandsEnabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2020,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2020,2022,2023,2025 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
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "MQTTConnection.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <Windows.h>
|
||||
|
|
@ -32,117 +33,27 @@
|
|||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
static unsigned int m_fileLevel = 2U;
|
||||
static std::string m_filePath;
|
||||
static std::string m_fileRoot;
|
||||
static bool m_fileRotate = true;
|
||||
CMQTTConnection* m_mqtt = nullptr;
|
||||
|
||||
static FILE* m_fpLog = nullptr;
|
||||
static bool m_daemon = false;
|
||||
static unsigned int m_mqttLevel = 2U;
|
||||
|
||||
static unsigned int m_displayLevel = 2U;
|
||||
|
||||
static struct tm m_tm;
|
||||
|
||||
static char LEVELS[] = " DMIWEF";
|
||||
|
||||
static bool logOpenRotate()
|
||||
void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
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 != nullptr)
|
||||
return true;
|
||||
} else {
|
||||
if (m_fpLog != nullptr)
|
||||
::fclose(m_fpLog);
|
||||
}
|
||||
|
||||
char filename[200U];
|
||||
#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
|
||||
|
||||
if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) {
|
||||
status = true;
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (m_daemon)
|
||||
dup2(fileno(m_fpLog), fileno(stderr));
|
||||
#endif
|
||||
}
|
||||
|
||||
m_tm = *tm;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool logOpenNoRotate()
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
if (m_fileLevel == 0U)
|
||||
return true;
|
||||
|
||||
if (m_fpLog != nullptr)
|
||||
return true;
|
||||
|
||||
char filename[200U];
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
::sprintf(filename, "%s\\%s.log", m_filePath.c_str(), m_fileRoot.c_str());
|
||||
#else
|
||||
::sprintf(filename, "%s/%s.log", m_filePath.c_str(), m_fileRoot.c_str());
|
||||
#endif
|
||||
|
||||
if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) {
|
||||
status = true;
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (m_daemon)
|
||||
dup2(fileno(m_fpLog), fileno(stderr));
|
||||
#endif
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool LogOpen()
|
||||
{
|
||||
if (m_fileRotate)
|
||||
return logOpenRotate();
|
||||
else
|
||||
return logOpenNoRotate();
|
||||
}
|
||||
|
||||
bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate)
|
||||
{
|
||||
m_filePath = filePath;
|
||||
m_fileRoot = fileRoot;
|
||||
m_fileLevel = fileLevel;
|
||||
m_mqttLevel = mqttLevel;
|
||||
m_displayLevel = displayLevel;
|
||||
m_daemon = daemon;
|
||||
m_fileRotate = rotate;
|
||||
|
||||
if (m_daemon)
|
||||
m_displayLevel = 0U;
|
||||
|
||||
return ::LogOpen();
|
||||
}
|
||||
|
||||
void LogFinalise()
|
||||
{
|
||||
if (m_fpLog != nullptr)
|
||||
::fclose(m_fpLog);
|
||||
if (m_mqtt != nullptr) {
|
||||
m_mqtt->close();
|
||||
delete m_mqtt;
|
||||
m_mqtt = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Log(unsigned int level, const char* fmt, ...)
|
||||
|
|
@ -171,22 +82,26 @@ void Log(unsigned int level, const char* fmt, ...)
|
|||
|
||||
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 (m_mqtt != nullptr && level >= m_mqttLevel && m_mqttLevel != 0U)
|
||||
m_mqtt->publish("log", buffer);
|
||||
|
||||
if (level >= m_displayLevel && m_displayLevel != 0U) {
|
||||
::fprintf(stdout, "%s\n", buffer);
|
||||
::fflush(stdout);
|
||||
}
|
||||
|
||||
if (level == 6U) { // Fatal
|
||||
::fclose(m_fpLog);
|
||||
if (level == 6U) // Fatal
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void WriteJSON(const std::string& topLevel, nlohmann::json& json)
|
||||
{
|
||||
if (m_mqtt != nullptr) {
|
||||
nlohmann::json top;
|
||||
|
||||
top[topLevel] = json;
|
||||
|
||||
m_mqtt->publish("json", top.dump());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2020,2022,2023 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
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__)
|
||||
#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__)
|
||||
#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__)
|
||||
|
|
@ -30,7 +32,9 @@
|
|||
|
||||
extern void Log(unsigned int level, const char* fmt, ...);
|
||||
|
||||
extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate);
|
||||
extern void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel);
|
||||
extern void LogFinalise();
|
||||
|
||||
extern void WriteJSON(const std::string& topLevel, nlohmann::json& json);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
222
NXDNGateway/MQTTConnection.cpp
Normal file
222
NXDNGateway/MQTTConnection.cpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (C) 2022,2023,2025 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 "MQTTConnection.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
CMQTTConnection::CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>& subs, unsigned int keepalive, MQTT_QOS qos) :
|
||||
m_host(host),
|
||||
m_port(port),
|
||||
m_name(name),
|
||||
m_authEnabled(authEnabled),
|
||||
m_username(username),
|
||||
m_password(password),
|
||||
m_subs(subs),
|
||||
m_keepalive(keepalive),
|
||||
m_qos(qos),
|
||||
m_mosq(nullptr),
|
||||
m_connected(false)
|
||||
{
|
||||
assert(!host.empty());
|
||||
assert(port > 0U);
|
||||
assert(!name.empty());
|
||||
assert(keepalive >= 5U);
|
||||
|
||||
::mosquitto_lib_init();
|
||||
}
|
||||
|
||||
CMQTTConnection::~CMQTTConnection()
|
||||
{
|
||||
::mosquitto_lib_cleanup();
|
||||
}
|
||||
|
||||
bool CMQTTConnection::open()
|
||||
{
|
||||
char name[50U];
|
||||
::sprintf(name, "NXDNGateway.%ld", ::time(nullptr));
|
||||
|
||||
::fprintf(stdout, "NXDNGateway (%s) connecting to MQTT as %s\n", m_name.c_str(), name);
|
||||
|
||||
m_mosq = ::mosquitto_new(name, true, this);
|
||||
if (m_mosq == nullptr){
|
||||
::fprintf(stderr, "MQTT Error newing: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_authEnabled)
|
||||
::mosquitto_username_pw_set(m_mosq, m_username.c_str(), m_password.c_str());
|
||||
|
||||
::mosquitto_connect_callback_set(m_mosq, onConnect);
|
||||
::mosquitto_subscribe_callback_set(m_mosq, onSubscribe);
|
||||
::mosquitto_message_callback_set(m_mosq, onMessage);
|
||||
::mosquitto_disconnect_callback_set(m_mosq, onDisconnect);
|
||||
|
||||
int rc = ::mosquitto_connect(m_mosq, m_host.c_str(), m_port, m_keepalive);
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::mosquitto_destroy(m_mosq);
|
||||
m_mosq = nullptr;
|
||||
::fprintf(stderr, "MQTT Error connecting: %s\n", ::mosquitto_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = ::mosquitto_loop_start(m_mosq);
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::mosquitto_disconnect(m_mosq);
|
||||
::mosquitto_destroy(m_mosq);
|
||||
m_mosq = nullptr;
|
||||
::fprintf(stderr, "MQTT Error loop starting: %s\n", ::mosquitto_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMQTTConnection::publish(const char* topic, const char* text)
|
||||
{
|
||||
assert(topic != nullptr);
|
||||
assert(text != nullptr);
|
||||
|
||||
return publish(topic, (unsigned char*)text, (unsigned int)::strlen(text));
|
||||
}
|
||||
|
||||
bool CMQTTConnection::publish(const char* topic, const std::string& text)
|
||||
{
|
||||
assert(topic != nullptr);
|
||||
|
||||
return publish(topic, (unsigned char*)text.c_str(), (unsigned int)text.size());
|
||||
}
|
||||
|
||||
bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len)
|
||||
{
|
||||
assert(topic != nullptr);
|
||||
assert(data != nullptr);
|
||||
|
||||
if (!m_connected)
|
||||
return false;
|
||||
|
||||
if (::strchr(topic, '/') == nullptr) {
|
||||
char topicEx[100U];
|
||||
::sprintf(topicEx, "%s/%s", m_name.c_str(), topic);
|
||||
|
||||
int rc = ::mosquitto_publish(m_mosq, nullptr, topicEx, len, data, static_cast<int>(m_qos), false);
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
int rc = ::mosquitto_publish(m_mosq, nullptr, topic, len, data, static_cast<int>(m_qos), false);
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMQTTConnection::close()
|
||||
{
|
||||
if (m_mosq != nullptr) {
|
||||
::mosquitto_disconnect(m_mosq);
|
||||
::mosquitto_destroy(m_mosq);
|
||||
m_mosq = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CMQTTConnection::onConnect(mosquitto* mosq, void* obj, int rc)
|
||||
{
|
||||
assert(mosq != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
::fprintf(stdout, "MQTT: on_connect: %s\n", ::mosquitto_connack_string(rc));
|
||||
if (rc != 0) {
|
||||
::mosquitto_disconnect(mosq);
|
||||
return;
|
||||
}
|
||||
|
||||
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
|
||||
p->m_connected = true;
|
||||
|
||||
for (std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) {
|
||||
std::string topic = (*it).first;
|
||||
|
||||
if (topic.find_first_of('/') == std::string::npos) {
|
||||
char topicEx[100U];
|
||||
::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str());
|
||||
|
||||
rc = ::mosquitto_subscribe(mosq, nullptr, topicEx, static_cast<int>(p->m_qos));
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topicEx, ::mosquitto_strerror(rc));
|
||||
::mosquitto_disconnect(mosq);
|
||||
}
|
||||
} else {
|
||||
rc = ::mosquitto_subscribe(mosq, nullptr, topic.c_str(), static_cast<int>(p->m_qos));
|
||||
if (rc != MOSQ_ERR_SUCCESS) {
|
||||
::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topic.c_str(), ::mosquitto_strerror(rc));
|
||||
::mosquitto_disconnect(mosq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMQTTConnection::onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS)
|
||||
{
|
||||
assert(mosq != nullptr);
|
||||
assert(obj != nullptr);
|
||||
assert(grantedQOS != nullptr);
|
||||
|
||||
for (int i = 0; i < qosCount; i++)
|
||||
::fprintf(stdout, "MQTT: on_subscribe: %d:%d\n", i, grantedQOS[i]);
|
||||
}
|
||||
|
||||
void CMQTTConnection::onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message)
|
||||
{
|
||||
assert(mosq != nullptr);
|
||||
assert(obj != nullptr);
|
||||
assert(message != nullptr);
|
||||
|
||||
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
|
||||
|
||||
for (std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) {
|
||||
std::string topic = (*it).first;
|
||||
|
||||
char topicEx[100U];
|
||||
::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str());
|
||||
|
||||
if (::strcmp(topicEx, message->topic) == 0) {
|
||||
(*it).second((unsigned char*)message->payload, message->payloadlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMQTTConnection::onDisconnect(mosquitto* mosq, void* obj, int rc)
|
||||
{
|
||||
assert(mosq != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
::fprintf(stdout, "MQTT: on_disconnect: %s\n", ::mosquitto_reason_string(rc));
|
||||
|
||||
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
|
||||
p->m_connected = false;
|
||||
}
|
||||
|
||||
66
NXDNGateway/MQTTConnection.h
Normal file
66
NXDNGateway/MQTTConnection.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2022,2023,2025 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(MQTTPUBLISHER_H)
|
||||
#define MQTTPUBLISHER_H
|
||||
|
||||
#include <mosquitto.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
enum class MQTT_QOS : int {
|
||||
AT_MODE_ONCE = 0U,
|
||||
AT_LEAST_ONCE = 1U,
|
||||
EXACTLY_ONCE = 2U
|
||||
};
|
||||
|
||||
class CMQTTConnection {
|
||||
public:
|
||||
CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>& subs, unsigned int keepalive, MQTT_QOS qos = MQTT_QOS::EXACTLY_ONCE);
|
||||
~CMQTTConnection();
|
||||
|
||||
bool open();
|
||||
|
||||
bool publish(const char* topic, const char* text);
|
||||
bool publish(const char* topic, const std::string& text);
|
||||
bool publish(const char* topic, const unsigned char* data, unsigned int len);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
std::string m_host;
|
||||
unsigned short m_port;
|
||||
std::string m_name;
|
||||
bool m_authEnabled;
|
||||
std::string m_username;
|
||||
std::string m_password;
|
||||
std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>> m_subs;
|
||||
unsigned int m_keepalive;
|
||||
MQTT_QOS m_qos;
|
||||
mosquitto* m_mosq;
|
||||
bool m_connected;
|
||||
|
||||
static void onConnect(mosquitto* mosq, void* obj, int rc);
|
||||
static void onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS);
|
||||
static void onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message);
|
||||
static void onDisconnect(mosquitto* mosq, void* obj, int rc);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -2,17 +2,17 @@ CC = cc
|
|||
CXX = c++
|
||||
|
||||
# Use the following CFLAGS and LIBS if you don't want to use gpsd.
|
||||
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -std=c++0x -pthread
|
||||
LIBS = -lpthread
|
||||
CFLAGS = -g -O3 -Wall -std=c++0x -pthread
|
||||
LIBS = -lpthread -lmosquitto
|
||||
|
||||
# Use the following CFLAGS and LIBS if you do want to use gpsd.
|
||||
#CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUSE_GPSD -std=c++0x -pthread
|
||||
#LIBS = -lpthread -lgps
|
||||
#CFLAGS = -g -O3 -Wall -DUSE_GPSD -std=c++0x -pthread
|
||||
#LIBS = -lpthread -lgps -lmosquitto
|
||||
|
||||
LDFLAGS = -g
|
||||
|
||||
OBJECTS = APRSWriter.o Conf.o GPSHandler.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o NXDNCRC.o NXDNGateway.o NXDNLookup.o NXDNNetwork.o \
|
||||
Reflectors.o RptNetwork.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
|
||||
OBJECTS = APRSWriter.o Conf.o GPSHandler.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o MQTTConnection.o NXDNCRC.o NXDNGateway.o \
|
||||
NXDNLookup.o NXDNNetwork.o Reflectors.o RptNetwork.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o
|
||||
|
||||
all: NXDNGateway
|
||||
|
||||
|
|
@ -28,7 +28,6 @@ NXDNGateway.o: GitVersion.h FORCE
|
|||
|
||||
FORCE:
|
||||
|
||||
|
||||
install:
|
||||
install -m 755 NXDNGateway /usr/local/bin/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016,2017,2018,2020,2024,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017,2018,2020,2023,2024,2025 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
|
||||
|
|
@ -17,17 +17,15 @@
|
|||
*/
|
||||
|
||||
#include "KenwoodNetwork.h"
|
||||
#include "MQTTConnection.h"
|
||||
#include "IcomNetwork.h"
|
||||
#include "NXDNNetwork.h"
|
||||
#include "NXDNGateway.h"
|
||||
#include "RptNetwork.h"
|
||||
#include "NXDNLookup.h"
|
||||
#include "Reflectors.h"
|
||||
#include "GPSHandler.h"
|
||||
#include "StopWatch.h"
|
||||
#include "Version.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
#include "GitVersion.h"
|
||||
|
|
@ -59,6 +57,11 @@ const char* DEFAULT_INI_FILE = "/etc/NXDNGateway.ini";
|
|||
#include <ctime>
|
||||
#include <cstring>
|
||||
|
||||
// In Log.cpp
|
||||
extern CMQTTConnection* m_mqtt;
|
||||
|
||||
static CNXDNGateway* gateway = nullptr;
|
||||
|
||||
static bool m_killed = false;
|
||||
static int m_signal = 0;
|
||||
|
||||
|
|
@ -76,13 +79,6 @@ const unsigned char NXDN_TYPE_TX_REL = 0x08U;
|
|||
|
||||
const unsigned short NXDN_VOICE_ID = 9999U;
|
||||
|
||||
class CStaticTG {
|
||||
public:
|
||||
unsigned short m_tg;
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* iniFile = DEFAULT_INI_FILE;
|
||||
|
|
@ -101,7 +97,6 @@ int main(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
::signal(SIGINT, sigHandler);
|
||||
::signal(SIGTERM, sigHandler);
|
||||
|
|
@ -114,7 +109,7 @@ int main(int argc, char** argv)
|
|||
m_signal = 0;
|
||||
m_killed = false;
|
||||
|
||||
CNXDNGateway* gateway = new CNXDNGateway(std::string(iniFile));
|
||||
gateway = new CNXDNGateway(std::string(iniFile));
|
||||
ret = gateway->run();
|
||||
|
||||
delete gateway;
|
||||
|
|
@ -146,7 +141,16 @@ CNXDNGateway::CNXDNGateway(const std::string& file) :
|
|||
m_conf(file),
|
||||
m_writer(nullptr),
|
||||
m_gps(nullptr),
|
||||
m_voice(nullptr)
|
||||
m_voice(nullptr),
|
||||
m_remoteNetwork(nullptr),
|
||||
m_currentTG(0U),
|
||||
m_currentAddrLen(0U),
|
||||
m_currentAddr(),
|
||||
m_currentIsStatic(false),
|
||||
m_hangTimer(1000U),
|
||||
m_rfHangTime(0U),
|
||||
m_reflectors(nullptr),
|
||||
m_staticTGs()
|
||||
{
|
||||
CUDPSocket::startup();
|
||||
}
|
||||
|
|
@ -220,16 +224,6 @@ int CNXDNGateway::run()
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
|
||||
#else
|
||||
ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
|
||||
#endif
|
||||
if (!ret) {
|
||||
::fprintf(stderr, "NXDNGateway: unable to open the log file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (m_daemon) {
|
||||
::close(STDIN_FILENO);
|
||||
|
|
@ -237,6 +231,16 @@ int CNXDNGateway::run()
|
|||
::close(STDERR_FILENO);
|
||||
}
|
||||
#endif
|
||||
::LogInitialise(m_conf.getLogDisplayLevel(), m_conf.getLogMQTTLevel());
|
||||
|
||||
std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>> subscriptions;
|
||||
if (m_conf.getRemoteCommandsEnabled())
|
||||
subscriptions.push_back(std::make_pair("command", CNXDNGateway::onCommand));
|
||||
|
||||
m_mqtt = new CMQTTConnection(m_conf.getMQTTAddress(), m_conf.getMQTTPort(), m_conf.getMQTTName(), m_conf.getMQTTAuthEnabled(), m_conf.getMQTTUsername(), m_conf.getMQTTPassword(), subscriptions, m_conf.getMQTTKeepalive());
|
||||
ret = m_mqtt->open();
|
||||
if (!ret)
|
||||
return 1;
|
||||
|
||||
createGPS();
|
||||
|
||||
|
|
@ -249,42 +253,33 @@ int CNXDNGateway::run()
|
|||
localNetwork = new CIcomNetwork(m_conf.getMyPort(), m_conf.getRptAddress(), m_conf.getRptPort(), m_conf.getDebug());
|
||||
|
||||
ret = localNetwork->open();
|
||||
if (!ret)
|
||||
return 1;
|
||||
|
||||
CNXDNNetwork remoteNetwork(m_conf.getNetworkPort(), m_conf.getCallsign(), m_conf.getNetworkDebug());
|
||||
ret = remoteNetwork.open();
|
||||
if (!ret) {
|
||||
delete localNetwork;
|
||||
return 1;
|
||||
}
|
||||
|
||||
m_remoteNetwork = new CNXDNNetwork(m_conf.getNetworkPort(), m_conf.getCallsign(), m_conf.getNetworkDebug());
|
||||
ret = m_remoteNetwork->open();
|
||||
if (!ret) {
|
||||
delete m_remoteNetwork;
|
||||
localNetwork->close();
|
||||
delete localNetwork;
|
||||
return 1;
|
||||
}
|
||||
|
||||
CUDPSocket* remoteSocket = nullptr;
|
||||
if (m_conf.getRemoteCommandsEnabled()) {
|
||||
remoteSocket = new CUDPSocket(m_conf.getRemoteCommandsPort());
|
||||
ret = remoteSocket->open();
|
||||
if (!ret) {
|
||||
delete remoteSocket;
|
||||
remoteSocket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CReflectors reflectors(m_conf.getNetworkHosts1(), m_conf.getNetworkHosts2(), m_conf.getNetworkReloadTime());
|
||||
m_reflectors = new CReflectors(m_conf.getNetworkHosts1(), m_conf.getNetworkHosts2(), m_conf.getNetworkReloadTime());
|
||||
if (m_conf.getNetworkParrotPort() > 0U)
|
||||
reflectors.setParrot(m_conf.getNetworkParrotAddress(), m_conf.getNetworkParrotPort());
|
||||
m_reflectors->setParrot(m_conf.getNetworkParrotAddress(), m_conf.getNetworkParrotPort());
|
||||
if (m_conf.getNetworkNXDN2DMRPort() > 0U)
|
||||
reflectors.setNXDN2DMR(m_conf.getNetworkNXDN2DMRAddress(), m_conf.getNetworkNXDN2DMRPort());
|
||||
reflectors.load();
|
||||
m_reflectors->setNXDN2DMR(m_conf.getNetworkNXDN2DMRAddress(), m_conf.getNetworkNXDN2DMRPort());
|
||||
m_reflectors->load();
|
||||
|
||||
CNXDNLookup* lookup = new CNXDNLookup(m_conf.getLookupName(), m_conf.getLookupTime());
|
||||
lookup->read();
|
||||
|
||||
unsigned int rfHangTime = m_conf.getNetworkRFHangTime();
|
||||
m_rfHangTime = m_conf.getNetworkRFHangTime();
|
||||
unsigned int netHangTime = m_conf.getNetworkNetHangTime();
|
||||
|
||||
CTimer hangTimer(1000U);
|
||||
|
||||
CTimer pollTimer(1000U, 5U);
|
||||
pollTimer.start();
|
||||
|
||||
|
|
@ -300,28 +295,32 @@ int CNXDNGateway::run()
|
|||
}
|
||||
}
|
||||
|
||||
LogMessage("Starting NXDNGateway-%s", VERSION);
|
||||
LogInfo("NXDNGateway-%s is starting", VERSION);
|
||||
LogInfo("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion);
|
||||
|
||||
writeJSONStatus("NXDNGateway is starting");
|
||||
|
||||
unsigned short srcId = 0U;
|
||||
unsigned short dstTG = 0U;
|
||||
bool grp = false;
|
||||
|
||||
bool currentIsStatic = false;
|
||||
CNXDNReflector currentTG;
|
||||
|
||||
std::vector<unsigned short> staticIds = m_conf.getNetworkStatic();
|
||||
|
||||
std::vector<CNXDNReflector> staticTGs;
|
||||
for (const auto& it : staticIds) {
|
||||
CNXDNReflector* reflector = reflectors.find(it);
|
||||
for (std::vector<unsigned short>::const_iterator it = staticIds.cbegin(); it != staticIds.cend(); ++it) {
|
||||
CNXDNReflector* reflector = m_reflectors->find(*it);
|
||||
if (reflector != nullptr) {
|
||||
staticTGs.push_back(*reflector);
|
||||
CStaticTG staticTG;
|
||||
staticTG.m_tg = *it;
|
||||
staticTG.m_addr = reflector->m_addr;
|
||||
staticTG.m_addrLen = reflector->m_addrLen;
|
||||
m_staticTGs.push_back(staticTG);
|
||||
|
||||
remoteNetwork.writePoll(*reflector);
|
||||
remoteNetwork.writePoll(*reflector);
|
||||
remoteNetwork.writePoll(*reflector);
|
||||
m_remoteNetwork->writePoll(staticTG.m_addr, staticTG.m_addrLen, staticTG.m_tg);
|
||||
m_remoteNetwork->writePoll(staticTG.m_addr, staticTG.m_addrLen, staticTG.m_tg);
|
||||
m_remoteNetwork->writePoll(staticTG.m_addr, staticTG.m_addrLen, staticTG.m_tg);
|
||||
|
||||
LogMessage("Statically linked to reflector %u", it);
|
||||
LogMessage("Statically linked to reflector %u", staticTG.m_tg);
|
||||
writeJSONLinking("startup", staticTG.m_tg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -331,10 +330,10 @@ int CNXDNGateway::run()
|
|||
unsigned int addrLen;
|
||||
|
||||
// From the reflector to the MMDVM
|
||||
unsigned int len = remoteNetwork.readData(buffer, 200U, addr, addrLen);
|
||||
unsigned int len = m_remoteNetwork->readData(buffer, 200U, addr, addrLen);
|
||||
while (len > 0U) {
|
||||
// If we're linked and it's from the right place, send it on
|
||||
if (currentTG.isUsed() && CNXDNNetwork::match(addr, currentTG)) {
|
||||
if (m_currentAddrLen > 0U && CUDPSocket::match(m_currentAddr, addr)) {
|
||||
// Don't pass reflector control data through to the MMDVM
|
||||
if (::memcmp(buffer, "NXDND", 5U) == 0) {
|
||||
unsigned short dstTG = 0U;
|
||||
|
|
@ -343,28 +342,32 @@ int CNXDNGateway::run()
|
|||
|
||||
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
||||
|
||||
if (grp && currentTG.m_id == dstTG) {
|
||||
if (grp && m_currentTG == dstTG)
|
||||
localNetwork->write(buffer + 10U, len - 10U);
|
||||
if (grp && m_currentTG == dstTG) {
|
||||
if (!isVoiceBusy())
|
||||
localNetwork->write(buffer + 10U, len - 10U);
|
||||
}
|
||||
|
||||
hangTimer.start();
|
||||
m_hangTimer.start();
|
||||
}
|
||||
} else if (currentTG.isEmpty()) {
|
||||
} else if (m_currentTG == 0U) {
|
||||
bool poll = false;
|
||||
|
||||
// We weren't really connected yet, but we got a reply from a poll, or some data
|
||||
if ((::memcmp(buffer, "NXDND", 5U) == 0) || (poll = (::memcmp(buffer, "NXDNP", 5U) == 0))) {
|
||||
// Find the static TG that this audio data/poll belongs to
|
||||
for (const auto& it : staticTGs) {
|
||||
if (CNXDNNetwork::match(addr, it)) {
|
||||
currentTG = it;
|
||||
for (std::vector<CStaticTG>::const_iterator it = m_staticTGs.cbegin(); it != m_staticTGs.cend(); ++it) {
|
||||
if (CUDPSocket::match(addr, (*it).m_addr)) {
|
||||
m_currentTG = (*it).m_tg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTG.isUsed()) {
|
||||
currentIsStatic = true;
|
||||
if (m_currentTG > 0U) {
|
||||
m_currentAddr = addr;
|
||||
m_currentAddrLen = addrLen;
|
||||
m_currentIsStatic = true;
|
||||
|
||||
unsigned short dstTG = 0U;
|
||||
dstTG |= (buffer[7U] << 8) & 0xFF00U;
|
||||
|
|
@ -372,20 +375,22 @@ int CNXDNGateway::run()
|
|||
|
||||
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
||||
|
||||
if (grp && (currentTG.m_id == dstTG) && !poll) {
|
||||
if (grp && m_currentTG == dstTG && !poll) {
|
||||
if (!isVoiceBusy())
|
||||
localNetwork->write(buffer + 10U, len - 10U);
|
||||
}
|
||||
|
||||
LogMessage("Switched to reflector %u due to network activity", currentTG.m_id);
|
||||
writeJSONLinking("network", m_currentTG);
|
||||
|
||||
hangTimer.setTimeout(netHangTime);
|
||||
hangTimer.start();
|
||||
LogMessage("Switched to reflector %u due to network activity", m_currentTG);
|
||||
|
||||
m_hangTimer.setTimeout(netHangTime);
|
||||
m_hangTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
len = remoteNetwork.readData(buffer, 200U, addr, addrLen);
|
||||
len = m_remoteNetwork->readData(buffer, 200U, addr, addrLen);
|
||||
}
|
||||
|
||||
// From the MMDVM to the reflector or control data
|
||||
|
|
@ -401,64 +406,72 @@ int CNXDNGateway::run()
|
|||
dstTG = (buffer[10U] << 8) & 0xFF00U;
|
||||
dstTG |= (buffer[11U] << 0) & 0x00FFU;
|
||||
|
||||
if (dstTG != currentTG.m_id) {
|
||||
if (currentTG.isUsed()) {
|
||||
if (dstTG != m_currentTG) {
|
||||
if (m_currentAddrLen > 0U) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Unlinking from reflector %u by %s", currentTG.m_id, callsign.c_str());
|
||||
|
||||
if (!currentIsStatic) {
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
writeJSONUnlinked("user");
|
||||
LogMessage("Unlinking from reflector %u by %s", m_currentTG, callsign.c_str());
|
||||
|
||||
if (!m_currentIsStatic) {
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
}
|
||||
|
||||
hangTimer.stop();
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
|
||||
CNXDNReflector found;
|
||||
for (const auto& it : staticTGs) {
|
||||
if (dstTG == it.m_id) {
|
||||
found = it;
|
||||
const CStaticTG* found = nullptr;
|
||||
for (std::vector<CStaticTG>::const_iterator it = m_staticTGs.cbegin(); it != m_staticTGs.cend(); ++it) {
|
||||
if (dstTG == (*it).m_tg) {
|
||||
found = &(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found.isEmpty()) {
|
||||
CNXDNReflector* refl = reflectors.find(dstTG);
|
||||
if (found == nullptr) {
|
||||
CNXDNReflector* refl = m_reflectors->find(dstTG);
|
||||
if (refl != nullptr) {
|
||||
currentTG = *refl;
|
||||
currentIsStatic = false;
|
||||
m_currentTG = dstTG;
|
||||
m_currentAddr = refl->m_addr;
|
||||
m_currentAddrLen = refl->m_addrLen;
|
||||
m_currentIsStatic = false;
|
||||
} else {
|
||||
currentTG.reset();
|
||||
currentIsStatic = false;
|
||||
m_currentTG = dstTG;
|
||||
m_currentAddrLen = 0U;
|
||||
m_currentIsStatic = false;
|
||||
}
|
||||
} else {
|
||||
currentTG = found;
|
||||
currentIsStatic = true;
|
||||
m_currentTG = found->m_tg;
|
||||
m_currentAddr = found->m_addr;
|
||||
m_currentAddrLen = found->m_addrLen;
|
||||
m_currentIsStatic = true;
|
||||
}
|
||||
|
||||
// Link to the new reflector
|
||||
if (currentTG.isUsed()) {
|
||||
if (m_currentAddrLen > 0U) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Switched to reflector %u due to RF activity from %s", currentTG.m_id, callsign.c_str());
|
||||
LogMessage("Switched to reflector %u due to RF activity from %s", m_currentTG, callsign.c_str());
|
||||
writeJSONLinking("user", m_currentTG);
|
||||
|
||||
if (!currentIsStatic) {
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
if (!m_currentIsStatic) {
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
}
|
||||
|
||||
hangTimer.setTimeout(rfHangTime);
|
||||
hangTimer.start();
|
||||
m_hangTimer.setTimeout(m_rfHangTime);
|
||||
m_hangTimer.start();
|
||||
} else {
|
||||
hangTimer.stop();
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
|
||||
if (m_voice != nullptr) {
|
||||
if (currentTG.isEmpty())
|
||||
if (m_currentAddrLen == 0U)
|
||||
m_voice->unlinked();
|
||||
else
|
||||
m_voice->linkedTo(currentTG.m_id);
|
||||
m_voice->linkedTo(m_currentTG);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -493,9 +506,9 @@ int CNXDNGateway::run()
|
|||
}
|
||||
|
||||
// If we're linked and we have a network, send it on
|
||||
if (currentTG.isUsed()) {
|
||||
remoteNetwork.writeData(buffer, len, srcId, dstTG, grp, currentTG);
|
||||
hangTimer.start();
|
||||
if (m_currentAddrLen > 0U) {
|
||||
m_remoteNetwork->writeData(buffer, len, srcId, dstTG, grp, m_currentAddr, m_currentAddrLen);
|
||||
m_hangTimer.start();
|
||||
}
|
||||
|
||||
len = localNetwork->read(buffer);
|
||||
|
|
@ -507,140 +520,48 @@ int CNXDNGateway::run()
|
|||
localNetwork->write(buffer, length);
|
||||
}
|
||||
|
||||
if (remoteSocket != nullptr) {
|
||||
sockaddr_storage addr;
|
||||
unsigned int addrLen;
|
||||
int res = remoteSocket->read(buffer, 200U, addr, addrLen);
|
||||
if (res > 0) {
|
||||
buffer[res] = '\0';
|
||||
if (::memcmp(buffer + 0U, "TalkGroup", 9U) == 0) {
|
||||
unsigned int tg = ((strlen((char*)buffer + 0U) > 10) ? (unsigned int)::atoi((char*)(buffer + 10U)) : 9999);
|
||||
|
||||
if (tg != currentTG.m_id) {
|
||||
if (currentTG.isUsed()) {
|
||||
LogMessage("Unlinked from reflector %u by remote command", currentTG.m_id);
|
||||
|
||||
if (!currentIsStatic) {
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
}
|
||||
|
||||
hangTimer.stop();
|
||||
}
|
||||
|
||||
CNXDNReflector found;
|
||||
for (const auto& it : staticTGs) {
|
||||
if (tg == it.m_id) {
|
||||
found = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found.isEmpty()) {
|
||||
CNXDNReflector* refl = reflectors.find(tg);
|
||||
if (refl != nullptr) {
|
||||
currentTG = *refl;
|
||||
currentIsStatic = false;
|
||||
} else {
|
||||
currentTG.reset();
|
||||
currentIsStatic = false;
|
||||
}
|
||||
} else {
|
||||
currentTG = found;
|
||||
currentIsStatic = true;
|
||||
}
|
||||
|
||||
// Link to the new reflector
|
||||
if (currentTG.isUsed()) {
|
||||
LogMessage("Switched to reflector %u by remote command", currentTG.m_id);
|
||||
|
||||
if (!currentIsStatic) {
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
}
|
||||
|
||||
hangTimer.setTimeout(rfHangTime);
|
||||
hangTimer.start();
|
||||
} else {
|
||||
hangTimer.stop();
|
||||
}
|
||||
|
||||
if (m_voice != nullptr) {
|
||||
if (currentTG.isEmpty())
|
||||
m_voice->unlinked();
|
||||
else
|
||||
m_voice->linkedTo(currentTG.m_id);
|
||||
}
|
||||
}
|
||||
} else if (::memcmp(buffer + 0U, "status", 6U) == 0) {
|
||||
std::string state = std::string("nxdn:") + (currentTG.isUsed() ? "conn" : "disc");
|
||||
remoteSocket->write((unsigned char*)state.c_str(), (unsigned int)state.length(), addr, addrLen);
|
||||
} else if (::memcmp(buffer + 0U, "host", 4U) == 0) {
|
||||
std::string host = "nxdn:\"NONE\"";
|
||||
|
||||
if (currentTG.isUsed()) {
|
||||
if (remoteNetwork.hasIPv6() && currentTG.hasIPv6()) {
|
||||
char buffer[100U];
|
||||
if (::getnameinfo((struct sockaddr*)¤tTG.IPv6.m_addr, currentTG.IPv6.m_addrLen, buffer, sizeof(buffer), 0, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0)
|
||||
host = "p25:\"" + std::string(buffer) + "\"";
|
||||
}
|
||||
else if (remoteNetwork.hasIPv4() && currentTG.hasIPv4()) {
|
||||
char buffer[100U];
|
||||
if (::getnameinfo((struct sockaddr*)¤tTG.IPv4.m_addr, currentTG.IPv4.m_addrLen, buffer, sizeof(buffer), 0, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0)
|
||||
host = "p25:\"" + std::string(buffer) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
remoteSocket->write((unsigned char*)host.c_str(), (unsigned int)host.length(), addr, addrLen);
|
||||
} else {
|
||||
CUtils::dump("Invalid remote command received", buffer, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
reflectors.clock(ms);
|
||||
m_reflectors->clock(ms);
|
||||
|
||||
localNetwork->clock(ms);
|
||||
|
||||
if (m_voice != nullptr)
|
||||
m_voice->clock(ms);
|
||||
|
||||
hangTimer.clock(ms);
|
||||
if (hangTimer.isRunning() && hangTimer.hasExpired()) {
|
||||
if (currentTG.isUsed()) {
|
||||
LogMessage("Unlinking from %u due to inactivity", currentTG.m_id);
|
||||
m_hangTimer.clock(ms);
|
||||
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
|
||||
if (m_currentAddrLen > 0U) {
|
||||
LogMessage("Unlinking from %u due to inactivity", m_currentTG);
|
||||
writeJSONUnlinked("timer");
|
||||
|
||||
if (!currentIsStatic) {
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
remoteNetwork.writeUnlink(currentTG);
|
||||
if (!m_currentIsStatic) {
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
}
|
||||
|
||||
if (m_voice != nullptr)
|
||||
m_voice->unlinked();
|
||||
|
||||
currentTG.reset();
|
||||
m_currentAddrLen = 0U;
|
||||
|
||||
hangTimer.stop();
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
|
||||
currentTG.reset();
|
||||
m_currentTG = 0U;
|
||||
}
|
||||
|
||||
pollTimer.clock(ms);
|
||||
if (pollTimer.isRunning() && pollTimer.hasExpired()) {
|
||||
// Poll the static TGs
|
||||
for (const auto& it : staticTGs)
|
||||
remoteNetwork.writePoll(it);
|
||||
for (std::vector<CStaticTG>::const_iterator it = m_staticTGs.cbegin(); it != m_staticTGs.cend(); ++it)
|
||||
m_remoteNetwork->writePoll((*it).m_addr, (*it).m_addrLen, (*it).m_tg);
|
||||
|
||||
// Poll the dynamic TG
|
||||
if (!currentIsStatic && currentTG.isUsed())
|
||||
remoteNetwork.writePoll(currentTG);
|
||||
if (!m_currentIsStatic && m_currentAddrLen > 0U)
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
|
||||
pollTimer.start();
|
||||
}
|
||||
|
|
@ -652,17 +573,16 @@ int CNXDNGateway::run()
|
|||
CThread::sleep(5U);
|
||||
}
|
||||
|
||||
LogInfo("NXDNGateway is stopping");
|
||||
writeJSONStatus("NXDNGateway is stopping");
|
||||
|
||||
delete m_voice;
|
||||
|
||||
localNetwork->close();
|
||||
delete localNetwork;
|
||||
|
||||
if (remoteSocket != nullptr) {
|
||||
remoteSocket->close();
|
||||
delete remoteSocket;
|
||||
}
|
||||
|
||||
remoteNetwork.close();
|
||||
m_remoteNetwork->close();
|
||||
delete m_remoteNetwork;
|
||||
|
||||
lookup->stop();
|
||||
|
||||
|
|
@ -682,12 +602,10 @@ void CNXDNGateway::createGPS()
|
|||
|
||||
std::string callsign = m_conf.getCallsign();
|
||||
std::string rptSuffix = m_conf.getSuffix();
|
||||
std::string address = m_conf.getAPRSAddress();
|
||||
unsigned short port = m_conf.getAPRSPort();
|
||||
std::string suffix = m_conf.getAPRSSuffix();
|
||||
bool debug = m_conf.getDebug();
|
||||
|
||||
m_writer = new CAPRSWriter(callsign, rptSuffix, address, port, debug);
|
||||
m_writer = new CAPRSWriter(callsign, rptSuffix, debug);
|
||||
|
||||
unsigned int txFrequency = m_conf.getTxFrequency();
|
||||
unsigned int rxFrequency = m_conf.getRxFrequency();
|
||||
|
|
@ -720,6 +638,98 @@ void CNXDNGateway::createGPS()
|
|||
m_gps = new CGPSHandler(callsign, suffix, m_writer);
|
||||
}
|
||||
|
||||
void CNXDNGateway::writeCommand(const std::string& command)
|
||||
{
|
||||
if (command.substr(0, 9) == "TalkGroup") {
|
||||
unsigned int tg = 9999U;
|
||||
if (command.length() > 10)
|
||||
tg = (unsigned int)std::stoi(command.substr(10));
|
||||
|
||||
if (tg != m_currentTG) {
|
||||
if (m_currentAddrLen > 0U) {
|
||||
LogMessage("Unlinked from reflector %u by remote command", m_currentTG);
|
||||
writeJSONUnlinked("remote");
|
||||
|
||||
if (!m_currentIsStatic) {
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writeUnlink(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
}
|
||||
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
|
||||
const CStaticTG* found = nullptr;
|
||||
for (std::vector<CStaticTG>::const_iterator it = m_staticTGs.cbegin(); it != m_staticTGs.cend(); ++it) {
|
||||
if (tg == (*it).m_tg) {
|
||||
found = &(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == nullptr) {
|
||||
CNXDNReflector* refl = m_reflectors->find(tg);
|
||||
if (refl != nullptr) {
|
||||
m_currentTG = tg;
|
||||
m_currentAddr = refl->m_addr;
|
||||
m_currentAddrLen = refl->m_addrLen;
|
||||
m_currentIsStatic = false;
|
||||
} else {
|
||||
m_currentTG = tg;
|
||||
m_currentAddrLen = 0U;
|
||||
m_currentIsStatic = false;
|
||||
}
|
||||
} else {
|
||||
m_currentTG = found->m_tg;
|
||||
m_currentAddr = found->m_addr;
|
||||
m_currentAddrLen = found->m_addrLen;
|
||||
m_currentIsStatic = true;
|
||||
}
|
||||
|
||||
// Link to the new reflector
|
||||
if (m_currentAddrLen > 0U) {
|
||||
LogMessage("Switched to reflector %u by remote command", m_currentTG);
|
||||
writeJSONLinking("remote", m_currentTG);
|
||||
|
||||
if (!m_currentIsStatic) {
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
m_remoteNetwork->writePoll(m_currentAddr, m_currentAddrLen, m_currentTG);
|
||||
}
|
||||
|
||||
m_hangTimer.setTimeout(m_rfHangTime);
|
||||
m_hangTimer.start();
|
||||
} else {
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
|
||||
if (m_voice != nullptr) {
|
||||
if (m_currentAddrLen == 0U)
|
||||
m_voice->unlinked();
|
||||
else
|
||||
m_voice->linkedTo(m_currentTG);
|
||||
}
|
||||
}
|
||||
} else if (command.substr(0, 6) == "status") {
|
||||
std::string state = std::string("nxdn:") + ((m_currentAddrLen > 0) ? "conn" : "disc");
|
||||
m_mqtt->publish("response", state);
|
||||
} else if (command.substr(0, 4) == "host") {
|
||||
std::string ref;
|
||||
|
||||
if (m_currentAddrLen > 0) {
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
if (::getnameinfo((struct sockaddr*)&m_currentAddr, m_currentAddrLen, buffer, sizeof(buffer), 0, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||
ref = std::string(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::string host = std::string("nxdn:\"") + ((ref.length() == 0) ? "NONE" : ref) + "\"";
|
||||
m_mqtt->publish("response", host);
|
||||
} else {
|
||||
CUtils::dump("Invalid remote command received", (unsigned char*)command.c_str(), (unsigned int)command.length());
|
||||
}
|
||||
}
|
||||
|
||||
bool CNXDNGateway::isVoiceBusy() const
|
||||
{
|
||||
if (m_voice == nullptr)
|
||||
|
|
@ -727,3 +737,55 @@ bool CNXDNGateway::isVoiceBusy() const
|
|||
|
||||
return m_voice->isBusy();
|
||||
}
|
||||
|
||||
void CNXDNGateway::writeJSONStatus(const std::string& status)
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["timestamp"] = CUtils::createTimestamp();
|
||||
json["message"] = status;
|
||||
|
||||
WriteJSON("status", json);
|
||||
}
|
||||
|
||||
void CNXDNGateway::writeJSONLinking(const std::string& reason, unsigned short tg)
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["timestamp"] = CUtils::createTimestamp();
|
||||
json["action"] = "linking";
|
||||
json["reason"] = reason;
|
||||
json["talkgroup"] = int(tg);
|
||||
|
||||
WriteJSON("link", json);
|
||||
}
|
||||
|
||||
void CNXDNGateway::writeJSONUnlinked(const std::string& reason)
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["timestamp"] = CUtils::createTimestamp();
|
||||
json["action"] = "unlinked";
|
||||
json["reason"] = reason;
|
||||
|
||||
WriteJSON("link", json);
|
||||
}
|
||||
|
||||
void CNXDNGateway::writeJSONRelinking(unsigned short tg)
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["timestamp"] = CUtils::createTimestamp();
|
||||
json["action"] = "relinking";
|
||||
json["talkgroup"] = int(tg);
|
||||
|
||||
WriteJSON("link", json);
|
||||
}
|
||||
|
||||
void CNXDNGateway::onCommand(const unsigned char* command, unsigned int length)
|
||||
{
|
||||
assert(gateway != nullptr);
|
||||
assert(command != nullptr);
|
||||
|
||||
gateway->writeCommand(std::string((char*)command, length));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016,2018,2024 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2018,2023,2024 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
|
||||
|
|
@ -19,8 +19,10 @@
|
|||
#if !defined(NXDNGateway_H)
|
||||
#define NXDNGateway_H
|
||||
|
||||
#include "NXDNNetwork.h"
|
||||
#include "APRSWriter.h"
|
||||
#include "GPSHandler.h"
|
||||
#include "Reflectors.h"
|
||||
#include "Voice.h"
|
||||
#include "Timer.h"
|
||||
#include "Conf.h"
|
||||
|
|
@ -41,6 +43,13 @@
|
|||
#include <winsock.h>
|
||||
#endif
|
||||
|
||||
class CStaticTG {
|
||||
public:
|
||||
unsigned short m_tg;
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
};
|
||||
|
||||
class CNXDNGateway
|
||||
{
|
||||
public:
|
||||
|
|
@ -50,14 +59,33 @@ public:
|
|||
int run();
|
||||
|
||||
private:
|
||||
CConf m_conf;
|
||||
CAPRSWriter* m_writer;
|
||||
CGPSHandler* m_gps;
|
||||
CVoice* m_voice;
|
||||
CConf m_conf;
|
||||
CAPRSWriter* m_writer;
|
||||
CGPSHandler* m_gps;
|
||||
CVoice* m_voice;
|
||||
CNXDNNetwork* m_remoteNetwork;
|
||||
unsigned short m_currentTG;
|
||||
unsigned int m_currentAddrLen;
|
||||
sockaddr_storage m_currentAddr;
|
||||
bool m_currentIsStatic;
|
||||
CTimer m_hangTimer;
|
||||
unsigned int m_rfHangTime;
|
||||
CReflectors* m_reflectors;
|
||||
std::vector<CStaticTG> m_staticTGs;
|
||||
|
||||
void createGPS();
|
||||
|
||||
bool isVoiceBusy() const;
|
||||
|
||||
void writeJSONStatus(const std::string& status);
|
||||
void writeJSONLinking(const std::string& reason, unsigned short tg);
|
||||
void writeJSONUnlinked(const std::string& reason);
|
||||
void writeJSONRelinking(unsigned short tg);
|
||||
|
||||
void writeCommand(const std::string& command);
|
||||
|
||||
static void onCommand(const unsigned char* command, unsigned int length);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@ Directory=./Audio
|
|||
|
||||
[APRS]
|
||||
Enable=0
|
||||
Address=127.0.0.1
|
||||
Port=8673
|
||||
Suffix=N
|
||||
Description=APRS Description
|
||||
# Symbol="/r"
|
||||
|
|
@ -49,10 +47,16 @@ Time=24
|
|||
[Log]
|
||||
# Logging levels, 0=No logging
|
||||
DisplayLevel=1
|
||||
FileLevel=1
|
||||
FilePath=.
|
||||
FileRoot=NXDNGateway
|
||||
FileRotate=1
|
||||
MQTTLevel=1
|
||||
|
||||
[MQTT]
|
||||
Address=127.0.0.1
|
||||
Port=1883
|
||||
Keepalive=60
|
||||
Auth=0
|
||||
Username=mmdvm
|
||||
Password=mmdvm
|
||||
Name=nxdn-gateway
|
||||
|
||||
[Network]
|
||||
Port=14050
|
||||
|
|
@ -75,4 +79,4 @@ Port=2947
|
|||
|
||||
[Remote Commands]
|
||||
Enable=0
|
||||
Port=6075
|
||||
|
||||
|
|
|
|||
|
|
@ -28,26 +28,26 @@
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
|
|
@ -88,12 +88,13 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;UDP_SOCKET_MAX=2;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>C:\Program Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>prebuild.cmd</Command>
|
||||
|
|
@ -106,12 +107,13 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;UDP_SOCKET_MAX=2;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>C:\Program Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>prebuild.cmd</Command>
|
||||
|
|
@ -126,14 +128,15 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;UDP_SOCKET_MAX=2;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>C:\Program Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>prebuild.cmd</Command>
|
||||
|
|
@ -148,14 +151,15 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;UDP_SOCKET_MAX=2;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>C:\Program Files</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>prebuild.cmd</Command>
|
||||
|
|
@ -168,6 +172,7 @@
|
|||
<ClInclude Include="IcomNetwork.h" />
|
||||
<ClInclude Include="KenwoodNetwork.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="MQTTConnection.h" />
|
||||
<ClInclude Include="Mutex.h" />
|
||||
<ClInclude Include="NXDNCRC.h" />
|
||||
<ClInclude Include="NXDNGateway.h" />
|
||||
|
|
@ -191,6 +196,7 @@
|
|||
<ClCompile Include="IcomNetwork.cpp" />
|
||||
<ClCompile Include="KenwoodNetwork.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="MQTTConnection.cpp" />
|
||||
<ClCompile Include="Mutex.cpp" />
|
||||
<ClCompile Include="NXDNCRC.cpp" />
|
||||
<ClCompile Include="NXDNGateway.cpp" />
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@
|
|||
<ClInclude Include="KenwoodNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MQTTConnection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Conf.cpp">
|
||||
|
|
@ -133,5 +136,8 @@
|
|||
<ClCompile Include="KenwoodNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MQTTConnection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -20,7 +20,6 @@
|
|||
#
|
||||
###############################################################################
|
||||
#
|
||||
# Full path to the NXDNHosts JSON file
|
||||
#
|
||||
NXDNHOSTS=/path/to/NXDNHosts.json
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
|||
/* Port is always digits, no needs to lookup service */
|
||||
hints.ai_flags |= AI_NUMERICSERV;
|
||||
|
||||
int err = ::getaddrinfo(hostname.empty() ? nullptr : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
int err = ::getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
if (err != 0) {
|
||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2009,2014,2015,2016,2025 Jonathan Naylor, G4KLX
|
||||
* Copyright (C) 2009,2014,2015,2016,2023,2025 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
|
||||
|
|
@ -17,6 +17,13 @@
|
|||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
|
@ -70,77 +77,24 @@ void CUtils::dump(int level, const std::string& title, const unsigned char* data
|
|||
}
|
||||
}
|
||||
|
||||
void CUtils::dump(const std::string& title, const bool* bits, unsigned int length)
|
||||
std::string CUtils::createTimestamp()
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
char buffer[100U];
|
||||
|
||||
dump(2U, title, bits, length);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
SYSTEMTIME st;
|
||||
::GetSystemTime(&st);
|
||||
|
||||
::sprintf(buffer, "%04u-%02u-%02u %02u:%02u:%02u.%03u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
|
||||
#else
|
||||
struct timeval now;
|
||||
::gettimeofday(&now, nullptr);
|
||||
|
||||
struct tm* tm = ::gmtime(&now.tv_sec);
|
||||
|
||||
::sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03lld", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000LL);
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length)
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
|
||||
unsigned char bytes[100U];
|
||||
unsigned int nBytes = 0U;
|
||||
for (unsigned int n = 0U; n < length; n += 8U, nBytes++)
|
||||
bitsToByteBE(bits + n, bytes[nBytes]);
|
||||
|
||||
dump(level, title, bytes, nBytes);
|
||||
}
|
||||
|
||||
void CUtils::byteToBitsBE(unsigned char byte, bool* bits)
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
|
||||
bits[0U] = (byte & 0x80U) == 0x80U;
|
||||
bits[1U] = (byte & 0x40U) == 0x40U;
|
||||
bits[2U] = (byte & 0x20U) == 0x20U;
|
||||
bits[3U] = (byte & 0x10U) == 0x10U;
|
||||
bits[4U] = (byte & 0x08U) == 0x08U;
|
||||
bits[5U] = (byte & 0x04U) == 0x04U;
|
||||
bits[6U] = (byte & 0x02U) == 0x02U;
|
||||
bits[7U] = (byte & 0x01U) == 0x01U;
|
||||
}
|
||||
|
||||
void CUtils::byteToBitsLE(unsigned char byte, bool* bits)
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
|
||||
bits[0U] = (byte & 0x01U) == 0x01U;
|
||||
bits[1U] = (byte & 0x02U) == 0x02U;
|
||||
bits[2U] = (byte & 0x04U) == 0x04U;
|
||||
bits[3U] = (byte & 0x08U) == 0x08U;
|
||||
bits[4U] = (byte & 0x10U) == 0x10U;
|
||||
bits[5U] = (byte & 0x20U) == 0x20U;
|
||||
bits[6U] = (byte & 0x40U) == 0x40U;
|
||||
bits[7U] = (byte & 0x80U) == 0x80U;
|
||||
}
|
||||
|
||||
void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte)
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
|
||||
byte = bits[0U] ? 0x80U : 0x00U;
|
||||
byte |= bits[1U] ? 0x40U : 0x00U;
|
||||
byte |= bits[2U] ? 0x20U : 0x00U;
|
||||
byte |= bits[3U] ? 0x10U : 0x00U;
|
||||
byte |= bits[4U] ? 0x08U : 0x00U;
|
||||
byte |= bits[5U] ? 0x04U : 0x00U;
|
||||
byte |= bits[6U] ? 0x02U : 0x00U;
|
||||
byte |= bits[7U] ? 0x01U : 0x00U;
|
||||
}
|
||||
|
||||
void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte)
|
||||
{
|
||||
assert(bits != nullptr);
|
||||
|
||||
byte = bits[0U] ? 0x01U : 0x00U;
|
||||
byte |= bits[1U] ? 0x02U : 0x00U;
|
||||
byte |= bits[2U] ? 0x04U : 0x00U;
|
||||
byte |= bits[3U] ? 0x08U : 0x00U;
|
||||
byte |= bits[4U] ? 0x10U : 0x00U;
|
||||
byte |= bits[5U] ? 0x20U : 0x00U;
|
||||
byte |= bits[6U] ? 0x40U : 0x00U;
|
||||
byte |= bits[7U] ? 0x80U : 0x00U;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2009,2014,2015 by Jonathan Naylor, G4KLX
|
||||
* Copyright (C) 2009,2014,2015,2023 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
|
||||
|
|
@ -21,14 +21,7 @@ public:
|
|||
static void dump(const std::string& title, const unsigned char* data, unsigned int length);
|
||||
static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length);
|
||||
|
||||
static void dump(const std::string& title, const bool* bits, unsigned int length);
|
||||
static void dump(int level, const std::string& title, const bool* bits, unsigned int length);
|
||||
|
||||
static void byteToBitsBE(unsigned char byte, bool* bits);
|
||||
static void byteToBitsLE(unsigned char byte, bool* bits);
|
||||
|
||||
static void bitsToByteBE(const bool* bits, unsigned char& byte);
|
||||
static void bitsToByteLE(const bool* bits, unsigned char& byte);
|
||||
static std::string createTimestamp();
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2018,2020,2023,2024,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015-2018,2020,2023,2024,2025,2026 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
|
||||
|
|
@ -19,6 +19,6 @@
|
|||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20250506";
|
||||
const char* VERSION = "20260215";
|
||||
|
||||
#endif
|
||||
|
|
|
|||
25
NXDNGateway/schema.json
Normal file
25
NXDNGateway/schema.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"$defs": {
|
||||
"timestamp": {"type": "string"},
|
||||
"talkgroup": {"type": "integer"},
|
||||
"action": {"type": "string", "enum": ["linking", "unlinked", "failed", "relinking"]},
|
||||
"reason": {"type": "string", "enum": ["user", "network", "timer", "remote", "startup"]}
|
||||
},
|
||||
|
||||
"status": {
|
||||
"type": "object",
|
||||
"timestamp": {"$ref": "#/$defs/timestamp"},
|
||||
"message": {"type": "string"},
|
||||
"required": ["timestamp", "message"]
|
||||
},
|
||||
|
||||
"link": {
|
||||
"type": "object",
|
||||
"timestamp": {"$ref": "#/$defs/timestamp"},
|
||||
"action": {"$ref": "#/$defs/action"},
|
||||
"reason": {"$ref": "#/$defs/reason"},
|
||||
"talkgroup": {"$ref": "#/defs/talkgroup"},
|
||||
"required": ["timestamp", "action"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,26 +28,26 @@
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
|||
/* Port is always digits, no needs to lookup service */
|
||||
hints.ai_flags |= AI_NUMERICSERV;
|
||||
|
||||
int err = ::getaddrinfo(hostname.empty() ? nullptr : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
int err = ::getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
if (err != 0) {
|
||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2018,2024,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2018,2024,2025,2026 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
|
||||
|
|
@ -19,6 +19,6 @@
|
|||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20250319";
|
||||
const char* VERSION = "20260215";
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,8 +15,13 @@ line. The Parrot takes the UDP port number to listen on as an argument.
|
|||
The MMDVM .ini file should have the IP address and port number of the client in
|
||||
the [NXDN Network] settings.
|
||||
|
||||
The file that contains the information about the reachable reflectors is held in
|
||||
the NXDNHosts.txt file that should be donwloaded from the DVRef.com web site. A
|
||||
script to do this under Linux is included. This is handled automatically in WPSD
|
||||
and Pi-Star.
|
||||
|
||||
These programs build on 32-bit and 64-bit Linux as well as on Windows using
|
||||
Visual Studio 2019 on x86 and x64.
|
||||
Visual Studio 2022 on x86 and x64.
|
||||
|
||||
This software is licenced under the GPL v2 and is primarily intended for amateur and
|
||||
educational use.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue