DAPNETGateway/DAPNETNetwork.cpp

283 lines
6.9 KiB
C++
Raw Normal View History

2018-06-06 20:33:31 +01:00
/*
2025-03-18 13:52:17 +00:00
* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX
2018-06-06 20:33:31 +01:00
*
* 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 "DAPNETNetwork.h"
#include "Thread.h"
#include "Utils.h"
#include "Log.h"
2018-09-10 09:11:57 +01:00
#include <algorithm>
2018-06-06 20:33:31 +01:00
#include <cstdio>
#include <cassert>
#include <cstring>
2025-03-18 13:52:17 +00:00
const unsigned int BACKOFF[] = { 2000U, 4000U, 8000U, 10000U, 20000U, 60000U, 120000U, 240000U, 480000U, 600000U };
2018-06-06 20:33:31 +01:00
const unsigned int BUFFER_LENGTH = 200U;
CDAPNETNetwork::CDAPNETNetwork(const std::string& address, unsigned short port, const std::string& callsign, const std::string& authKey, const char* version, bool loggedIn, int failCount, bool debug) :
2018-06-06 20:33:31 +01:00
m_socket(address, port),
m_callsign(callsign),
m_authKey(authKey),
m_version(version),
m_loggedIn(false),
m_failCount(failCount),
2018-06-06 20:33:31 +01:00
m_debug(debug),
2025-03-18 13:52:17 +00:00
m_message(nullptr),
m_schedule(nullptr)
2018-06-06 20:33:31 +01:00
{
assert(!callsign.empty());
assert(!authKey.empty());
2025-03-18 13:52:17 +00:00
assert(version != nullptr);
2018-06-06 20:33:31 +01:00
}
CDAPNETNetwork::~CDAPNETNetwork()
{
2018-06-19 18:46:43 +01:00
delete m_message;
delete[] m_schedule;
2018-06-06 20:33:31 +01:00
}
bool CDAPNETNetwork::open()
{
LogMessage("Opening DAPNET connection");
return m_socket.open();
}
bool CDAPNETNetwork::login()
{
LogMessage("Logging into DAPNET");
2018-09-10 09:11:57 +01:00
std::transform(m_callsign.begin(), m_callsign.end(), m_callsign.begin(), ::tolower);
2018-06-06 20:33:31 +01:00
char login[200U];
::snprintf(login, 200, "[DAPNETGateway v%s %s %s]\r\n", m_version, m_callsign.c_str(), m_authKey.c_str());
2018-06-06 20:33:31 +01:00
return write((unsigned char*)login);
}
bool CDAPNETNetwork::read()
{
unsigned char buffer[BUFFER_LENGTH];
int length = m_socket.read(buffer, BUFFER_LENGTH, 0U);
2018-06-06 21:38:43 +01:00
if (length == -1) // Error
return false;
if (length == -2) // Connection lost
2018-06-06 20:33:31 +01:00
return false;
if (length == 0)
return true;
if (m_debug)
CUtils::dump(1U, "DAPNET Data Received", buffer, length);
// Turn it into a C string
buffer[length] = 0x00U;
if (buffer[0U] == '+') {
// Success
} else if (buffer[0U] == '-') {
// Error
LogWarning("An error has been reported by DAPNET");
} else if (buffer[0U] == '2') {
2018-10-22 23:05:46 +02:00
// First time sync paket indicated successful login
if (!m_loggedIn) {
m_loggedIn = true;
2018-10-22 23:05:46 +02:00
LogMessage("Logged into the DAPNET network");
writeJSONLink("linking", "");
2018-10-22 23:05:46 +02:00
}
2018-06-06 20:33:31 +01:00
// Time synchronisation
2018-06-06 21:38:43 +01:00
char* p = ::strchr((char*)buffer, '\n');
2025-03-18 13:52:17 +00:00
if (p != nullptr)
2018-06-06 20:33:31 +01:00
::strcpy(p, ":0000\r\n");
else
::strcat((char*)buffer, ":0000\r\n");
bool ok = write(buffer);
if (!ok)
return false;
return write((unsigned char*)"+\r\n");
} else if (buffer[0U] == '3') {
// ???
return write((unsigned char*)"+\r\n");
} else if (buffer[0U] == '4') {
// Timeslot information
2018-06-19 18:46:43 +01:00
return parseSchedule(buffer);
2018-10-22 23:05:46 +02:00
} else if (buffer[0U] == '7') {
// Login failed
return parseFailedLogin(buffer);
2018-06-06 20:33:31 +01:00
} else if (buffer[0U] == '#') {
// A message
2018-06-19 18:46:43 +01:00
return parseMessage(buffer, length);
2018-06-06 20:33:31 +01:00
} else {
CUtils::dump(3U, "An unknown message from DAPNET", buffer, length);
return write((unsigned char*)"-\r\n");
}
return true;
}
2018-06-19 18:46:43 +01:00
bool* CDAPNETNetwork::readSchedule()
{
bool* schedule = m_schedule;
2025-03-18 13:52:17 +00:00
m_schedule = nullptr;
2018-06-19 18:46:43 +01:00
return schedule;
}
2018-06-06 20:33:31 +01:00
CPOCSAGMessage* CDAPNETNetwork::readMessage()
{
2025-03-18 13:52:17 +00:00
if (m_message == nullptr)
return nullptr;
2018-06-06 20:33:31 +01:00
CPOCSAGMessage* message = m_message;
2025-03-18 13:52:17 +00:00
m_message = nullptr;
2018-06-06 20:33:31 +01:00
return message;
}
void CDAPNETNetwork::close()
{
m_socket.close();
LogMessage("Closing DAPNET connection");
}
bool CDAPNETNetwork::write(unsigned char* data)
{
2025-03-18 13:52:17 +00:00
assert(data != nullptr);
2018-06-06 20:33:31 +01:00
2025-03-18 13:52:17 +00:00
unsigned int length = (unsigned int)::strlen((char*)data);
2018-06-06 20:33:31 +01:00
if (m_debug)
2018-06-06 22:23:29 +01:00
CUtils::dump(1U, "DAPNET Data Transmitted", data, length);
2018-06-06 20:33:31 +01:00
2018-06-06 21:38:43 +01:00
bool ok = m_socket.write(data, length);
if (!ok)
LogWarning("Error when writing to DAPNET");
return ok;
2018-06-06 20:33:31 +01:00
}
2018-06-19 18:46:43 +01:00
bool CDAPNETNetwork::parseMessage(unsigned char* buffer, unsigned int length)
{
2025-03-18 13:52:17 +00:00
assert(buffer != nullptr);
2018-06-19 18:46:43 +01:00
2025-03-18 13:52:17 +00:00
unsigned int id = ::strtoul((char*)buffer + 1U, nullptr, 16);
2018-06-19 18:46:43 +01:00
char* p1 = ::strtok((char*)buffer + 4U, ":\r\n");
2025-03-18 13:52:17 +00:00
char* p2 = ::strtok(nullptr, ":\r\n");
char* p3 = ::strtok(nullptr, ":\r\n");
char* p4 = ::strtok(nullptr, ":\r\n");
char* p5 = ::strtok(nullptr, "\r\n");
2018-06-19 18:46:43 +01:00
2025-03-18 13:52:17 +00:00
if (p1 == nullptr || p2 == nullptr || p3 == nullptr || p4 == nullptr || p5 == nullptr) {
2018-06-19 18:46:43 +01:00
CUtils::dump(3U, "Received a malformed message from DAPNET", buffer, length);
id = (id + 1U) % 256UL;
char reply[20U];
::snprintf(reply, 20U, "#%02X -\r\n", id);
return write((unsigned char*)reply);
} else {
2025-03-18 13:52:17 +00:00
unsigned int type = ::strtoul(p1, nullptr, 10);
unsigned int addr = ::strtoul(p3, nullptr, 16);
unsigned int func = ::strtoul(p4, nullptr, 10);
2018-06-19 18:46:43 +01:00
2025-03-18 13:52:17 +00:00
m_message = new CPOCSAGMessage(type, addr, func, (unsigned char*)p5, (unsigned int)::strlen(p5));
2018-06-19 18:46:43 +01:00
id = (id + 1U) % 256UL;
char reply[20U];
::snprintf(reply, 20U, "#%02X +\r\n", id);
return write((unsigned char*)reply);
}
}
bool CDAPNETNetwork::parseSchedule(unsigned char* data)
{
2025-03-18 13:52:17 +00:00
assert(data != nullptr);
2018-06-19 18:46:43 +01:00
2018-06-19 18:49:37 +01:00
char* p = ::strtok((char*)data + 2U, "\r\n");
2025-03-18 13:52:17 +00:00
assert(p != nullptr);
2018-06-19 18:49:37 +01:00
LogMessage("Schedule information received: %s", p);
2018-06-19 18:46:43 +01:00
delete[] m_schedule;
m_schedule = new bool[16U];
for (unsigned int i = 0U; i < 16U; i++)
m_schedule[i] = false;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '0') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[0U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '1') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[1U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '2') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[2U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '3') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[3U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '4') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[4U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '5') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[5U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '6') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[6U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '7') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[7U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '8') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[8U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, '9') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[9U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'A') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[10U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'B') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[11U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'C') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[12U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'D') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[13U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'E') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[14U] = true;
2025-03-18 13:52:17 +00:00
if (::strchr(p, 'F') != nullptr)
2018-06-19 18:46:43 +01:00
m_schedule[15U] = true;
return write((unsigned char*)"+\r\n");
}
2018-10-22 23:05:46 +02:00
bool CDAPNETNetwork::parseFailedLogin(unsigned char* data)
{
2025-03-18 13:52:17 +00:00
assert(data != nullptr);
2018-10-22 23:05:46 +02:00
char* p = ::strtok((char*)data + 2U, "\r\n");
2025-03-18 13:52:17 +00:00
assert(p != nullptr);
2018-10-22 23:05:46 +02:00
LogMessage("Login failed: %s", p);
writeJSONLink("failed", "auth");
2018-10-22 23:05:46 +02:00
2025-03-18 13:52:17 +00:00
CThread::sleep(BACKOFF[m_failCount]);
if (m_failCount < 9)
m_failCount++;
2018-10-22 23:05:46 +02:00
return write((unsigned char*)"+\r\n");
}