mirror of
https://github.com/g4klx/DMRGateway
synced 2025-12-23 14:56:11 +08:00
Convert to use of MQTT for command, APRS, and dynamic TG control.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014,2016,2017,2018,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2010-2014,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
|
||||
@@ -16,6 +16,7 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "MQTTConnection.h"
|
||||
#include "APRSWriter.h"
|
||||
#include "DMRDefines.h"
|
||||
#include "Log.h"
|
||||
@@ -25,7 +26,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),
|
||||
@@ -35,22 +39,14 @@ m_latitude(0.0F),
|
||||
m_longitude(0.0F),
|
||||
m_height(0),
|
||||
m_desc(),
|
||||
m_symbol(),
|
||||
m_aprsAddr(),
|
||||
m_aprsLen(0U),
|
||||
m_aprsSocket()
|
||||
m_symbol()
|
||||
{
|
||||
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_aprsLen) != 0)
|
||||
m_aprsLen = 0U;
|
||||
}
|
||||
|
||||
CAPRSWriter::~CAPRSWriter()
|
||||
@@ -75,17 +71,6 @@ void CAPRSWriter::setLocation(float latitude, float longitude, int height)
|
||||
|
||||
bool CAPRSWriter::open()
|
||||
{
|
||||
if (m_aprsLen == 0U) {
|
||||
LogError("Could not lookup the address of the APRS-IS server");
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -104,7 +89,6 @@ void CAPRSWriter::clock(unsigned int ms)
|
||||
|
||||
void CAPRSWriter::close()
|
||||
{
|
||||
m_aprsSocket.close();
|
||||
}
|
||||
|
||||
void CAPRSWriter::sendIdFrame()
|
||||
@@ -172,5 +156,6 @@ void CAPRSWriter::sendIdFrame()
|
||||
if (m_debug)
|
||||
LogDebug("APRS ==> %s", output);
|
||||
|
||||
m_aprsSocket.write((unsigned char*)output, (unsigned int)::strlen(output), m_aprsAddr, m_aprsLen);
|
||||
m_mqtt->publish("aprs-gateway/aprs", output);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -39,7 +38,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();
|
||||
@@ -63,9 +62,6 @@ private:
|
||||
int m_height;
|
||||
std::string m_desc;
|
||||
std::string m_symbol;
|
||||
sockaddr_storage m_aprsAddr;
|
||||
unsigned int m_aprsLen;
|
||||
CUDPSocket m_aprsSocket;
|
||||
|
||||
void sendIdFrame();
|
||||
};
|
||||
|
||||
134
Conf.cpp
134
Conf.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015-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
|
||||
@@ -40,8 +40,9 @@ enum SECTION {
|
||||
SECTION_XLX_NETWORK,
|
||||
SECTION_GPSD,
|
||||
SECTION_APRS,
|
||||
SECTION_MQTT,
|
||||
SECTION_DYNAMIC_TG_CONTROL,
|
||||
SECTION_REMOTE_CONTROL
|
||||
SECTION_REMOTE_COMMANDS
|
||||
};
|
||||
|
||||
CConf::CConf(const std::string& file) :
|
||||
@@ -59,10 +60,7 @@ 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_infoLatitude(0.0F),
|
||||
m_infoLongitude(0.0F),
|
||||
m_infoHeight(0),
|
||||
@@ -178,16 +176,15 @@ m_gpsdEnabled(false),
|
||||
m_gpsdAddress(),
|
||||
m_gpsdPort(),
|
||||
m_aprsEnabled(false),
|
||||
m_aprsAddress(),
|
||||
m_aprsPort(0U),
|
||||
m_aprsSuffix(),
|
||||
m_aprsDescription(),
|
||||
m_aprsSymbol(),
|
||||
m_mqttAddress("127.0.0.1"),
|
||||
m_mqttPort(1883U),
|
||||
m_mqttKeepalive(60U),
|
||||
m_mqttName("dmr-gateway"),
|
||||
m_dynamicTGControlEnabled(false),
|
||||
m_dynamicTGControlPort(3769U),
|
||||
m_remoteControlEnabled(false),
|
||||
m_remoteControlAddress("127.0.0.1"),
|
||||
m_remoteControlPort(0U)
|
||||
m_remoteCommandsEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -235,10 +232,12 @@ bool CConf::read()
|
||||
section = SECTION_GPSD;
|
||||
else if (::strncmp(buffer, "[APRS]", 6U) == 0)
|
||||
section = SECTION_APRS;
|
||||
else if (::strncmp(buffer, "[MQTT]", 6U) == 0)
|
||||
section = SECTION_MQTT;
|
||||
else if (::strncmp(buffer, "[Dynamic TG Control]", 20U) == 0)
|
||||
section = SECTION_DYNAMIC_TG_CONTROL;
|
||||
else if (::strncmp(buffer, "[Remote Control]", 16U) == 0)
|
||||
section = SECTION_REMOTE_CONTROL;
|
||||
else if (::strncmp(buffer, "[Remote Commands]", 17U) == 0)
|
||||
section = SECTION_REMOTE_COMMANDS;
|
||||
else
|
||||
section = SECTION_NONE;
|
||||
|
||||
@@ -292,16 +291,10 @@ bool CConf::read()
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_debug = ::atoi(value) == 1;
|
||||
} 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);
|
||||
if (::strcmp(key, "MQTTLevel") == 0)
|
||||
m_logMQTTLevel = (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_VOICE) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_voiceEnabled = ::atoi(value) == 1;
|
||||
@@ -353,8 +346,7 @@ bool CConf::read()
|
||||
*p++ = '0';
|
||||
|
||||
m_xlxNetworkStartup = std::string(buffer);
|
||||
}
|
||||
else if (::strcmp(key, "Relink") == 0)
|
||||
} else if (::strcmp(key, "Relink") == 0)
|
||||
m_xlxNetworkRelink = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_xlxNetworkDebug = ::atoi(value) == 1;
|
||||
@@ -982,28 +974,27 @@ bool CConf::read()
|
||||
} 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_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 (section == SECTION_DYNAMIC_TG_CONTROL) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_dynamicTGControlEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_dynamicTGControlPort = (unsigned short)::atoi(value);
|
||||
} else if (section == SECTION_REMOTE_CONTROL) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_remoteControlEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_remoteControlAddress = value;
|
||||
else if (::strcmp(key, "Port") == 0)
|
||||
m_remoteControlPort = (unsigned short)::atoi(value);
|
||||
m_dynamicTGControlEnabled = ::atoi(value) == 1;
|
||||
} else if (section == SECTION_REMOTE_COMMANDS) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_remoteCommandsEnabled = ::atoi(value) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1062,24 +1053,9 @@ 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;
|
||||
}
|
||||
|
||||
bool CConf::getVoiceEnabled() const
|
||||
@@ -1685,16 +1661,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;
|
||||
@@ -1710,27 +1676,33 @@ 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::getDynamicTGControlEnabled() const
|
||||
{
|
||||
return m_dynamicTGControlEnabled;
|
||||
}
|
||||
|
||||
unsigned short CConf::getDynamicTGControlPort() const
|
||||
bool CConf::getRemoteCommandsEnabled() const
|
||||
{
|
||||
return m_dynamicTGControlPort;
|
||||
return m_remoteCommandsEnabled;
|
||||
}
|
||||
|
||||
bool CConf::getRemoteControlEnabled() const
|
||||
{
|
||||
return m_remoteControlEnabled;
|
||||
}
|
||||
|
||||
std::string CConf::getRemoteControlAddress() const
|
||||
{
|
||||
return m_remoteControlAddress;
|
||||
}
|
||||
|
||||
unsigned short CConf::getRemoteControlPort() const
|
||||
{
|
||||
return m_remoteControlPort;
|
||||
}
|
||||
|
||||
41
Conf.h
41
Conf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017,2019,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2019,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
|
||||
@@ -90,10 +90,7 @@ public:
|
||||
|
||||
// The Log section
|
||||
unsigned int getLogDisplayLevel() const;
|
||||
unsigned int getLogFileLevel() const;
|
||||
std::string getLogFilePath() const;
|
||||
std::string getLogFileRoot() const;
|
||||
bool getLogFileRotate() const;
|
||||
unsigned int getLogMQTTLevel() const;
|
||||
|
||||
// The Voice section
|
||||
bool getVoiceEnabled() const;
|
||||
@@ -232,20 +229,21 @@ public:
|
||||
|
||||
// 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 MQTT section
|
||||
std::string getMQTTAddress() const;
|
||||
unsigned short getMQTTPort() const;
|
||||
unsigned int getMQTTKeepalive() const;
|
||||
std::string getMQTTName() const;
|
||||
|
||||
// The Dynamic TG Control section
|
||||
bool getDynamicTGControlEnabled() const;
|
||||
unsigned short getDynamicTGControlPort() const;
|
||||
|
||||
// The Remote Control section
|
||||
bool getRemoteControlEnabled() const;
|
||||
std::string getRemoteControlAddress() const;
|
||||
unsigned short getRemoteControlPort() const;
|
||||
// The Remote Commands section
|
||||
bool getRemoteCommandsEnabled() const;
|
||||
|
||||
private:
|
||||
std::string m_file;
|
||||
@@ -264,10 +262,7 @@ private:
|
||||
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_logMQTTLevel;
|
||||
|
||||
float m_infoLatitude;
|
||||
float m_infoLongitude;
|
||||
@@ -392,18 +387,18 @@ private:
|
||||
std::string m_gpsdPort;
|
||||
|
||||
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_dynamicTGControlEnabled;
|
||||
unsigned short m_dynamicTGControlPort;
|
||||
std::string m_mqttAddress;
|
||||
unsigned short m_mqttPort;
|
||||
unsigned int m_mqttKeepalive;
|
||||
std::string m_mqttName;
|
||||
|
||||
bool m_remoteControlEnabled;
|
||||
std::string m_remoteControlAddress;
|
||||
unsigned short m_remoteControlPort;
|
||||
bool m_dynamicTGControlEnabled;
|
||||
|
||||
bool m_remoteCommandsEnabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
151
DMRGateway.cpp
151
DMRGateway.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2021 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015-2021,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
|
||||
@@ -16,6 +16,7 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "MQTTConnection.h"
|
||||
#include "RewriteType.h"
|
||||
#include "DMRSlotType.h"
|
||||
#include "RewriteSrc.h"
|
||||
@@ -67,10 +68,15 @@ static void sigHandler(int signum)
|
||||
}
|
||||
#endif
|
||||
|
||||
// In Log.cpp
|
||||
extern CMQTTConnection* m_mqtt;
|
||||
|
||||
static CDMRGateway* gateway = NULL;
|
||||
|
||||
const char* HEADER1 = "This software is for use on amateur radio networks only,";
|
||||
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
|
||||
const char* HEADER3 = "commercial networks is strictly prohibited.";
|
||||
const char* HEADER4 = "Copyright(C) 2017-2022 by Jonathan Naylor, G4KLX and others";
|
||||
const char* HEADER4 = "Copyright(C) 2017-2023 by Jonathan Naylor, G4KLX and others";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
@@ -102,10 +108,9 @@ int main(int argc, char** argv)
|
||||
do {
|
||||
m_signal = 0;
|
||||
|
||||
CDMRGateway* host = new CDMRGateway(std::string(iniFile));
|
||||
ret = host->run();
|
||||
|
||||
delete host;
|
||||
gateway = new CDMRGateway(std::string(iniFile));
|
||||
ret = gateway->run();
|
||||
delete gateway;
|
||||
|
||||
if (m_signal == 2)
|
||||
::LogInfo("DMRGateway-%s exited on receipt of SIGINT", VERSION);
|
||||
@@ -181,7 +186,6 @@ m_dmr4Passalls(),
|
||||
m_dmr5Passalls(),
|
||||
m_dynVoices(),
|
||||
m_dynRF(),
|
||||
m_socket(NULL),
|
||||
m_writer(NULL),
|
||||
m_callsign(),
|
||||
m_txFrequency(0U),
|
||||
@@ -343,16 +347,6 @@ int CDMRGateway::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, "DMRGateway: unable to open the log file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (m_daemon) {
|
||||
::close(STDIN_FILENO);
|
||||
@@ -360,6 +354,20 @@ int CDMRGateway::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.getDynamicTGControlEnabled())
|
||||
subscriptions.push_back(std::make_pair("dynamic", CDMRGateway::onDynamic));
|
||||
if (m_conf.getRemoteCommandsEnabled())
|
||||
subscriptions.push_back(std::make_pair("command", CDMRGateway::onCommand));
|
||||
|
||||
m_mqtt = new CMQTTConnection(m_conf.getMQTTAddress(), m_conf.getMQTTPort(), m_conf.getMQTTName(), subscriptions, m_conf.getMQTTKeepalive());
|
||||
ret = m_mqtt->open();
|
||||
if (!ret) {
|
||||
delete m_mqtt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
m_network1Enabled = m_conf.getDMRNetwork1Enabled();
|
||||
m_network2Enabled = m_conf.getDMRNetwork2Enabled();
|
||||
@@ -448,24 +456,9 @@ int CDMRGateway::run()
|
||||
}
|
||||
}
|
||||
|
||||
bool remoteControlEnabled = m_conf.getRemoteControlEnabled();
|
||||
if (remoteControlEnabled) {
|
||||
std::string address = m_conf.getRemoteControlAddress();
|
||||
unsigned short port = m_conf.getRemoteControlPort();
|
||||
|
||||
LogInfo("Remote Control Parameters");
|
||||
LogInfo(" Address: %s", address.c_str());
|
||||
LogInfo(" Port: %hu", port);
|
||||
|
||||
m_remoteControl = new CRemoteControl(this, address, port);
|
||||
|
||||
ret = m_remoteControl->open();
|
||||
if (!ret) {
|
||||
LogInfo("Failed to open Remove Control Socket");
|
||||
delete m_remoteControl;
|
||||
m_remoteControl = NULL;
|
||||
}
|
||||
}
|
||||
bool remoteCommandsEnabled = m_conf.getRemoteCommandsEnabled();
|
||||
if (remoteCommandsEnabled)
|
||||
m_remoteControl = new CRemoteControl(this);
|
||||
|
||||
if (m_network1Enabled && m_conf.getDMRNetwork1Enabled()) {
|
||||
ret = createDMRNetwork1();
|
||||
@@ -497,12 +490,6 @@ int CDMRGateway::run()
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m_conf.getDynamicTGControlEnabled()) {
|
||||
bool ret = createDynamicTGControl();
|
||||
if (!ret)
|
||||
return 1;
|
||||
}
|
||||
|
||||
createAPRS();
|
||||
|
||||
unsigned int rfTimeout = m_conf.getRFTimeout();
|
||||
@@ -1222,11 +1209,6 @@ int CDMRGateway::run()
|
||||
m_repeater->write(data);
|
||||
}
|
||||
|
||||
if (m_socket != NULL)
|
||||
processDynamicTGControl();
|
||||
|
||||
remoteControl();
|
||||
|
||||
unsigned int ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
@@ -1286,10 +1268,7 @@ int CDMRGateway::run()
|
||||
m_repeater->close();
|
||||
delete m_repeater;
|
||||
|
||||
if (m_remoteControl != NULL) {
|
||||
m_remoteControl->close();
|
||||
delete m_remoteControl;
|
||||
}
|
||||
|
||||
#if defined(USE_GPSD)
|
||||
if (m_gpsd != NULL) {
|
||||
@@ -1333,11 +1312,6 @@ int CDMRGateway::run()
|
||||
delete m_xlxNetwork;
|
||||
}
|
||||
|
||||
if (m_socket != NULL) {
|
||||
m_socket->close();
|
||||
delete m_socket;
|
||||
}
|
||||
|
||||
delete timer[1U];
|
||||
delete timer[2U];
|
||||
|
||||
@@ -2288,22 +2262,6 @@ bool CDMRGateway::createXLXNetwork()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDMRGateway::createDynamicTGControl()
|
||||
{
|
||||
unsigned short port = m_conf.getDynamicTGControlPort();
|
||||
|
||||
m_socket = new CUDPSocket(port);
|
||||
|
||||
bool ret = m_socket->open();
|
||||
if (!ret) {
|
||||
delete m_socket;
|
||||
m_socket = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDMRGateway::linkXLX(const std::string &number)
|
||||
{
|
||||
CReflector* reflector = m_xlxReflectors->find(number);
|
||||
@@ -2520,12 +2478,10 @@ void CDMRGateway::createAPRS()
|
||||
if (!m_conf.getAPRSEnabled())
|
||||
return;
|
||||
|
||||
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(m_callsign, suffix, address, port, debug);
|
||||
m_writer = new CAPRSWriter(m_callsign, suffix, debug);
|
||||
|
||||
std::string desc = m_conf.getAPRSDescription();
|
||||
std::string symbol = m_conf.getAPRSSymbol();
|
||||
@@ -2551,42 +2507,38 @@ void CDMRGateway::createAPRS()
|
||||
#endif
|
||||
}
|
||||
|
||||
void CDMRGateway::processDynamicTGControl()
|
||||
void CDMRGateway::processDynamicTGControl(const std::string& command)
|
||||
{
|
||||
unsigned char buffer[100U];
|
||||
sockaddr_storage address;
|
||||
unsigned int addrlen;
|
||||
int len = m_socket->read(buffer, 100U, address, addrlen);
|
||||
if (len <= 0)
|
||||
return;
|
||||
std::vector<std::string> args;
|
||||
|
||||
buffer[len] = '\0';
|
||||
std::stringstream tokeniser(command);
|
||||
|
||||
if (::memcmp(buffer + 0U, "DynTG", 5U) == 0) {
|
||||
char* pSlot = ::strtok((char*)(buffer + 5U), ", \r\n");
|
||||
char* pTG = ::strtok(NULL, ", \r\n");
|
||||
// Parse the original command into a vector of strings.
|
||||
std::string token;
|
||||
while (std::getline(tokeniser, token, ' '))
|
||||
args.push_back(token);
|
||||
|
||||
if (pSlot == NULL || pTG == NULL) {
|
||||
if (args.at(0U) == "DynTG") {
|
||||
if (args.size() < 3) {
|
||||
LogWarning("Malformed dynamic TG control message");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int slot = (unsigned int)::atoi(pSlot);
|
||||
unsigned int tg = (unsigned int)::atoi(pTG);
|
||||
unsigned int slot = (unsigned int)std::stoi(args.at(1U));
|
||||
unsigned int tg = (unsigned int)std::stoi(args.at(2U));
|
||||
|
||||
for (std::vector<CRewriteDynTGRF*>::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it)
|
||||
(*it)->tgChange(slot, tg);
|
||||
} else {
|
||||
LogWarning("Unknown dynamic TG control message: %s", buffer);
|
||||
LogWarning("Unknown dynamic TG control message: %s", command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CDMRGateway::remoteControl()
|
||||
void CDMRGateway::remoteControl(const std::string& commandStr)
|
||||
{
|
||||
if (m_remoteControl == NULL)
|
||||
return;
|
||||
assert(m_remoteControl != NULL);
|
||||
|
||||
REMOTE_COMMAND command = m_remoteControl->getCommand();
|
||||
REMOTE_COMMAND command = m_remoteControl->processCommand(commandStr);
|
||||
switch (command) {
|
||||
case RCD_ENABLE_NETWORK1:
|
||||
processEnableCommand(m_dmrNetwork1, "DMR Network 1", m_network1Enabled, true);
|
||||
@@ -2689,3 +2641,18 @@ void CDMRGateway::buildNetworkHostNetworkString(std::string &str, const std::str
|
||||
str += name + ":\""+ ((network == NULL) ? "NONE" : ((host.length() > 0) ? host : "NONE")) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
void CDMRGateway::onDynamic(const unsigned char* message, unsigned int length)
|
||||
{
|
||||
assert(gateway != NULL);
|
||||
|
||||
gateway->processDynamicTGControl(std::string((char*)message, length));
|
||||
}
|
||||
|
||||
void CDMRGateway::onCommand(const unsigned char* message, unsigned int length)
|
||||
{
|
||||
assert(gateway != NULL);
|
||||
|
||||
gateway->remoteControl(std::string((char*)message, length));
|
||||
}
|
||||
|
||||
|
||||
11
DMRGateway.h
11
DMRGateway.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017,2019,2020 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2019,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
|
||||
@@ -117,7 +117,6 @@ private:
|
||||
std::vector<CRewrite*> m_dmr5Passalls;
|
||||
std::vector<CDynVoice*> m_dynVoices;
|
||||
std::vector<CRewriteDynTGRF*> m_dynRF;
|
||||
CUDPSocket* m_socket;
|
||||
CAPRSWriter* m_writer;
|
||||
std::string m_callsign;
|
||||
unsigned int m_txFrequency;
|
||||
@@ -140,7 +139,6 @@ private:
|
||||
bool createDMRNetwork4();
|
||||
bool createDMRNetwork5();
|
||||
bool createXLXNetwork();
|
||||
bool createDynamicTGControl();
|
||||
|
||||
bool linkXLX(const std::string &number);
|
||||
void unlinkXLX();
|
||||
@@ -153,11 +151,14 @@ private:
|
||||
void processRadioPosition();
|
||||
void processTalkerAlias();
|
||||
void createAPRS();
|
||||
void processDynamicTGControl();
|
||||
void remoteControl();
|
||||
void processDynamicTGControl(const std::string& command);
|
||||
void remoteControl(const std::string& command);
|
||||
void processEnableCommand(CDMRNetwork* network, const std::string& name, bool& mode, bool enabled);
|
||||
void buildNetworkStatusNetworkString(std::string &str, const std::string& name, CDMRNetwork* network, bool enabled);
|
||||
void buildNetworkHostNetworkString(std::string &str, const std::string& name, CDMRNetwork* network);
|
||||
|
||||
static void onCommand(const unsigned char* message, unsigned int length);
|
||||
static void onDynamic(const unsigned char* message, unsigned int length);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,10 +13,7 @@ Debug=0
|
||||
[Log]
|
||||
# Logging levels, 0=No logging
|
||||
DisplayLevel=1
|
||||
FileLevel=1
|
||||
FilePath=.
|
||||
FileRoot=DMRGateway
|
||||
FileRotate=1
|
||||
MQTTLevel=1
|
||||
|
||||
[Voice]
|
||||
Enabled=1
|
||||
@@ -145,17 +142,19 @@ Port=2947
|
||||
|
||||
[APRS]
|
||||
Enable=0
|
||||
Address=127.0.0.1
|
||||
Port=8673
|
||||
Description=APRS Description
|
||||
Suffix=3
|
||||
# Symbol="/r"
|
||||
|
||||
[Dynamic TG Control]
|
||||
Enabled=1
|
||||
Port=3769
|
||||
|
||||
[Remote Control]
|
||||
Enable=0
|
||||
[MQTT]
|
||||
Address=127.0.0.1
|
||||
Port=7643
|
||||
Port=1883
|
||||
Keepalive=60
|
||||
Name=dmr-gateway
|
||||
|
||||
[Dynamic TG Control]
|
||||
Enable=1
|
||||
|
||||
[Remote Commands]
|
||||
Enable=0
|
||||
|
||||
|
||||
135
Log.cpp
135
Log.cpp
@@ -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
|
||||
@@ -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 = NULL;
|
||||
|
||||
static FILE* m_fpLog = NULL;
|
||||
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 != NULL)
|
||||
return true;
|
||||
} else {
|
||||
if (m_fpLog != NULL)
|
||||
::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")) != NULL) {
|
||||
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 != NULL)
|
||||
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")) != NULL) {
|
||||
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 != NULL)
|
||||
::fclose(m_fpLog);
|
||||
if (m_mqtt != NULL) {
|
||||
m_mqtt->close();
|
||||
delete m_mqtt;
|
||||
m_mqtt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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 != NULL && 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 != NULL) {
|
||||
nlohmann::json top;
|
||||
|
||||
top[topLevel] = json;
|
||||
|
||||
m_mqtt->publish("json", top.dump());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
Log.h
8
Log.h
@@ -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
|
||||
|
||||
211
MQTTConnection.cpp
Normal file
211
MQTTConnection.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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>
|
||||
|
||||
|
||||
CMQTTConnection::CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, 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_subs(subs),
|
||||
m_keepalive(keepalive),
|
||||
m_qos(qos),
|
||||
m_mosq(NULL),
|
||||
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()
|
||||
{
|
||||
m_mosq = ::mosquitto_new(m_name.c_str(), true, this);
|
||||
if (m_mosq == NULL){
|
||||
::fprintf(stderr, "MQTT Error newing: Out of memory.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
::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 = NULL;
|
||||
::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 = NULL;
|
||||
::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 != NULL);
|
||||
assert(text != NULL);
|
||||
|
||||
return publish(topic, (unsigned char*)text, ::strlen(text));
|
||||
}
|
||||
|
||||
bool CMQTTConnection::publish(const char* topic, const std::string& text)
|
||||
{
|
||||
assert(topic != NULL);
|
||||
|
||||
return publish(topic, (unsigned char*)text.c_str(), text.size());
|
||||
}
|
||||
|
||||
bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len)
|
||||
{
|
||||
assert(topic != NULL);
|
||||
assert(data != NULL);
|
||||
|
||||
if (!m_connected)
|
||||
return false;
|
||||
|
||||
if (::strchr(topic, '/') == NULL) {
|
||||
char topicEx[100U];
|
||||
::sprintf(topicEx, "%s/%s", m_name.c_str(), topic);
|
||||
|
||||
int rc = ::mosquitto_publish(m_mosq, NULL, 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, NULL, 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 != NULL) {
|
||||
::mosquitto_disconnect(m_mosq);
|
||||
::mosquitto_destroy(m_mosq);
|
||||
m_mosq = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CMQTTConnection::onConnect(mosquitto* mosq, void* obj, int rc)
|
||||
{
|
||||
assert(mosq != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
::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, NULL, 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, NULL, 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 != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(grantedQOS != NULL);
|
||||
|
||||
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 != NULL);
|
||||
assert(obj != NULL);
|
||||
assert(message != NULL);
|
||||
|
||||
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 != NULL);
|
||||
assert(obj != NULL);
|
||||
|
||||
::fprintf(stdout, "MQTT: on_disconnect: %s\n", ::mosquitto_reason_string(rc));
|
||||
|
||||
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
|
||||
p->m_connected = false;
|
||||
}
|
||||
|
||||
63
MQTTConnection.h
Normal file
63
MQTTConnection.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
* 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 MQTT_QOS {
|
||||
MQTT_QOS_AT_MODE_ONCE = 0U,
|
||||
MQTT_QOS_AT_LEAST_ONCE = 1U,
|
||||
MQTT_QOS_EXACTLY_ONCE = 2U
|
||||
};
|
||||
|
||||
class CMQTTConnection {
|
||||
public:
|
||||
CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, 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;
|
||||
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
|
||||
|
||||
14
Makefile
14
Makefile
@@ -2,19 +2,19 @@ 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 BPTC19696.o Conf.o CRC.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREmbeddedData.o DMREMB.o DMRFullLC.o DMRGateway.o \
|
||||
DMRLC.o DMRNetwork.o DMRSlotType.o DynVoice.o Golay2087.o GPSD.o Hamming.o Log.o MMDVMNetwork.o PassAllPC.o PassAllTG.o \
|
||||
QR1676.o Reflectors.o RemoteControl.o Rewrite.o RewriteDstId.o RewriteDynTGNet.o RewriteDynTGRF.o RewritePC.o RewriteSrc.o RewriteSrcId.o \
|
||||
RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o Sync.o Thread.o Timer.o UDPSocket.o Utils.o XLXVoice.o
|
||||
DMRLC.o DMRNetwork.o DMRSlotType.o DynVoice.o Golay2087.o GPSD.o Hamming.o Log.o MMDVMNetwork.o MQTTConnection.o PassAllPC.o \
|
||||
PassAllTG.o QR1676.o Reflectors.o RemoteControl.o Rewrite.o RewriteDstId.o RewriteDynTGNet.o RewriteDynTGRF.o RewritePC.o \
|
||||
RewriteSrc.o RewriteSrcId.o RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o Sync.o Thread.o Timer.o UDPSocket.o Utils.o XLXVoice.o
|
||||
|
||||
all: DMRGateway
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019,2021 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2019,2021,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
|
||||
@@ -16,6 +16,7 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "MQTTConnection.h"
|
||||
#include "RemoteControl.h"
|
||||
#include "Log.h"
|
||||
#include "DMRGateway.h"
|
||||
@@ -30,48 +31,34 @@ const unsigned int DISABLE_ARGS = 2U;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 100U;
|
||||
|
||||
CRemoteControl::CRemoteControl(CDMRGateway* host, const std::string address, unsigned short port) :
|
||||
// In Log.cpp
|
||||
extern CMQTTConnection* m_mqtt;
|
||||
|
||||
CRemoteControl::CRemoteControl(CDMRGateway* host) :
|
||||
m_host(host),
|
||||
m_socket(address, port),
|
||||
m_command(RCD_NONE),
|
||||
m_args()
|
||||
{
|
||||
assert(port > 0U);
|
||||
}
|
||||
|
||||
CRemoteControl::~CRemoteControl()
|
||||
{
|
||||
}
|
||||
|
||||
bool CRemoteControl::open()
|
||||
{
|
||||
return m_socket.open();
|
||||
}
|
||||
|
||||
REMOTE_COMMAND CRemoteControl::getCommand()
|
||||
REMOTE_COMMAND CRemoteControl::processCommand(const std::string& command)
|
||||
{
|
||||
m_command = RCD_NONE;
|
||||
m_args.clear();
|
||||
|
||||
char command[BUFFER_LENGTH];
|
||||
char buffer[BUFFER_LENGTH * 2];
|
||||
std::string replyStr = "OK";
|
||||
sockaddr_storage address;
|
||||
unsigned int addrlen;
|
||||
int ret = m_socket.read((unsigned char*)buffer, BUFFER_LENGTH, address, addrlen);
|
||||
if (ret > 0) {
|
||||
buffer[ret] = '\0';
|
||||
|
||||
// Make a copy of the original command for logging.
|
||||
::strcpy(command, buffer);
|
||||
std::stringstream tokeniser(command);
|
||||
|
||||
// Parse the original command into a vector of strings.
|
||||
char* b = buffer;
|
||||
char* p = NULL;
|
||||
while ((p = ::strtok(b, " ")) != NULL) {
|
||||
b = NULL;
|
||||
m_args.push_back(std::string(p));
|
||||
}
|
||||
std::string token;
|
||||
while (std::getline(tokeniser, token, ' '))
|
||||
m_args.push_back(token);
|
||||
|
||||
if (m_args.at(0U) == "enable" && m_args.size() >= ENABLE_ARGS) {
|
||||
if (m_args.at(1U) == "net1")
|
||||
m_command = RCD_ENABLE_NETWORK1;
|
||||
@@ -103,28 +90,26 @@ REMOTE_COMMAND CRemoteControl::getCommand()
|
||||
else
|
||||
replyStr = "KO";
|
||||
} else if (m_args.at(0U) == "status") {
|
||||
if (m_host != NULL) {
|
||||
if (m_host != NULL)
|
||||
m_host->buildNetworkStatusString(replyStr);
|
||||
}
|
||||
else {
|
||||
else
|
||||
replyStr = "KO";
|
||||
}
|
||||
|
||||
m_command = RCD_CONNECTION_STATUS;
|
||||
} else if (m_args.at(0U) == "hosts") {
|
||||
if (m_host != NULL) {
|
||||
if (m_host != NULL)
|
||||
m_host->buildNetworkHostsString(replyStr);
|
||||
}
|
||||
else {
|
||||
else
|
||||
replyStr = "KO";
|
||||
}
|
||||
|
||||
m_command = RCD_CONFIG_HOSTS;
|
||||
} else {
|
||||
replyStr = "KO";
|
||||
}
|
||||
|
||||
::snprintf(buffer, BUFFER_LENGTH * 2, "%s remote command of \"%s\" received", ((m_command == RCD_NONE) ? "Invalid" : "Valid"), command);
|
||||
char buffer[200U];
|
||||
::snprintf(buffer, 200, "%s remote command of \"%s\" received", ((m_command == RCD_NONE) ? "Invalid" : "Valid"), command.c_str());
|
||||
|
||||
if (m_command == RCD_NONE) {
|
||||
m_args.clear();
|
||||
LogWarning(buffer);
|
||||
@@ -134,8 +119,7 @@ REMOTE_COMMAND CRemoteControl::getCommand()
|
||||
#endif
|
||||
}
|
||||
|
||||
m_socket.write((unsigned char*)replyStr.c_str(), (unsigned int)replyStr.length(), address, addrlen);
|
||||
}
|
||||
m_mqtt->publish("response", replyStr);
|
||||
|
||||
return m_command;
|
||||
}
|
||||
@@ -171,7 +155,3 @@ int CRemoteControl::getArgInt(unsigned int n) const
|
||||
return ::atoi(getArgString(n).c_str());
|
||||
}
|
||||
|
||||
void CRemoteControl::close()
|
||||
{
|
||||
m_socket.close();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019,2021 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2019,2021,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,8 +19,6 @@
|
||||
#ifndef RemoteControl_H
|
||||
#define RemoteControl_H
|
||||
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@@ -47,12 +45,10 @@ class CDMRNetwork;
|
||||
|
||||
class CRemoteControl {
|
||||
public:
|
||||
CRemoteControl(CDMRGateway* host, const std::string address, unsigned short port);
|
||||
CRemoteControl(CDMRGateway* host);
|
||||
~CRemoteControl();
|
||||
|
||||
bool open();
|
||||
|
||||
REMOTE_COMMAND getCommand();
|
||||
REMOTE_COMMAND processCommand(const std::string& command);
|
||||
|
||||
unsigned int getArgCount() const;
|
||||
|
||||
@@ -60,11 +56,8 @@ public:
|
||||
unsigned int getArgUInt(unsigned int n) const;
|
||||
signed int getArgInt(unsigned int n) const;
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CDMRGateway* m_host;
|
||||
CUDPSocket m_socket;
|
||||
REMOTE_COMMAND m_command;
|
||||
std::vector<std::string> m_args;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2021 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 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
|
||||
@@ -19,6 +19,6 @@
|
||||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20230212";
|
||||
const char* VERSION = "20230707";
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user