Merge pull request #40 from W0CHP/master

Publish structured link/connectivity status
This commit is contained in:
Jonathan Naylor 2026-08-11 11:30:57 +01:00 committed by GitHub
commit b869053a80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 52 additions and 16 deletions

View file

@ -294,6 +294,7 @@ int CDAPNETGateway::run()
delete m_dapnetNetwork;
::LogError("Cannot open the DAPNET network port");
writeJSONLink("failed", "socket");
return 1;
}
@ -307,6 +308,7 @@ int CDAPNETGateway::run()
delete m_dapnetNetwork;
::LogError("Cannot login to the DAPNET network");
writeJSONLink("failed", "socket");
return 1;
}
@ -358,8 +360,10 @@ int CDAPNETGateway::run()
}
bool ok = m_dapnetNetwork->read();
if (!ok)
if (!ok) {
writeJSONLink("unlinked", "lost");
recover();
}
CPOCSAGMessage* message = m_dapnetNetwork->readMessage();
if (message != nullptr) {
@ -443,6 +447,7 @@ int CDAPNETGateway::run()
LogInfo("DAPNETGateway is stopping");
writeJSONStatus("DAPNETGateway is stopping");
writeJSONLink("unlinked", "");
m_pocsagNetwork->close();
delete m_pocsagNetwork;
@ -643,6 +648,6 @@ void CDAPNETGateway::writeJSONStatus(const std::string& status)
json["timestamp"] = CUtils::createTimestamp();
json["message"] = status;
WriteJSON("status", json);
WriteJSON("status", json, false);
}

View file

@ -100,6 +100,7 @@ bool CDAPNETNetwork::read()
if (!m_loggedIn) {
m_loggedIn = true;
LogMessage("Logged into the DAPNET network");
writeJSONLink("linking", "");
}
// Time synchronisation
char* p = ::strchr((char*)buffer, '\n');
@ -271,6 +272,7 @@ bool CDAPNETNetwork::parseFailedLogin(unsigned char* data)
assert(p != nullptr);
LogMessage("Login failed: %s", p);
writeJSONLink("failed", "auth");
CThread::sleep(BACKOFF[m_failCount]);
if (m_failCount < 9)

17
Log.cpp
View file

@ -18,6 +18,7 @@
#include "Log.h"
#include "MQTTConnection.h"
#include "Utils.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
@ -94,14 +95,26 @@ void Log(unsigned int level, const char* fmt, ...)
exit(1);
}
void WriteJSON(const std::string& topLevel, nlohmann::json& json)
void WriteJSON(const std::string& topLevel, nlohmann::json& json, bool retain)
{
if (m_mqtt != nullptr) {
nlohmann::json top;
top[topLevel] = json;
m_mqtt->publish("json", top.dump());
m_mqtt->publish("json", top.dump(), retain);
}
}
void writeJSONLink(const std::string& action, const std::string& reason)
{
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["action"] = action;
if (!reason.empty())
json["reason"] = reason;
WriteJSON("link", json, true);
}

8
Log.h
View file

@ -35,6 +35,12 @@ extern void Log(unsigned int level, const char* fmt, ...);
extern void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel);
extern void LogFinalise();
extern void WriteJSON(const std::string& topLevel, nlohmann::json& json);
extern void WriteJSON(const std::string& topLevel, nlohmann::json& json, bool retain);
// Publishes a "link" Kind directly, for connectivity events (DAPNET login
// success/failure/loss) that CDAPNETNetwork detects but has no WriteJSON
// access of its own (it isn't CDAPNETGateway) -- same free-function shape
// as APRSGateway's writeJSONLink.
extern void writeJSONLink(const std::string& action, const std::string& reason);
#endif

View file

@ -99,22 +99,22 @@ bool CMQTTConnection::open()
return true;
}
bool CMQTTConnection::publish(const char* topic, const char* text)
bool CMQTTConnection::publish(const char* topic, const char* text, bool retain)
{
assert(topic != nullptr);
assert(text != nullptr);
return publish(topic, (unsigned char*)text, (unsigned int)::strlen(text));
return publish(topic, (unsigned char*)text, (unsigned int)::strlen(text), retain);
}
bool CMQTTConnection::publish(const char* topic, const std::string& text)
bool CMQTTConnection::publish(const char* topic, const std::string& text, bool retain)
{
assert(topic != nullptr);
return publish(topic, (unsigned char*)text.c_str(), (unsigned int)text.size());
return publish(topic, (unsigned char*)text.c_str(), (unsigned int)text.size(), retain);
}
bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len)
bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len, bool retain)
{
assert(topic != nullptr);
assert(data != nullptr);
@ -126,13 +126,13 @@ bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsi
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);
int rc = ::mosquitto_publish(m_mosq, nullptr, topicEx, len, data, static_cast<int>(m_qos), retain);
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);
int rc = ::mosquitto_publish(m_mosq, nullptr, topic, len, data, static_cast<int>(m_qos), retain);
if (rc != MOSQ_ERR_SUCCESS) {
::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc));
return false;

View file

@ -37,9 +37,9 @@ public:
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);
bool publish(const char* topic, const char* text, bool retain = false);
bool publish(const char* topic, const std::string& text, bool retain = false);
bool publish(const char* topic, const unsigned char* data, unsigned int len, bool retain = false);
void close();

View file

@ -1,6 +1,8 @@
{
"$defs": {
"timestamp": {"type": "string"}
"timestamp": {"type": "string"},
"action": {"type": "string", "enum": ["linking", "unlinked", "failed"]},
"reason": {"type": "string", "enum": ["auth", "socket", "lost"]}
},
"status": {
@ -8,6 +10,14 @@
"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"},
"required": ["timestamp", "action"]
}
}