You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
smart-group-server-xl/SGSXLThread.cpp

488 lines
12 KiB

/*
* Copyright (C) 2010-2013,2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas Early N7TAE
*
* 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 <thread>
#include <chrono>
#include <sys/types.h>
#include <sys/stat.h>
#include <netdb.h>
#include <pwd.h>
#include <ctime>
#include <fstream>
#include <cstring>
#include <cassert>
#include "SGSXLThread.h"
#include "GroupHandler.h"
#include "DExtraHandler.h" // DEXTRA LINK
#include "DCSHandler.h" // DCS LINK
#include "HeaderData.h"
#include "G2Handler.h"
#include "AMBEData.h"
#include "Utils.h"
const unsigned int REMOTE_DUMMY_PORT = 65015U;
CSGSXLThread::CSGSXLThread(unsigned int countDExtra, unsigned int countDCS) :
m_countDExtra(countDExtra),
m_countDCS(countDCS),
m_killed(false),
m_stopped(true),
m_audioEnabled(false),
m_callsign(),
m_address(),
m_g2Handler(NULL),
m_irc(NULL),
m_cache(),
m_logEnabled(false),
m_statusTimer(1000U, 1U), // 1 second
m_lastStatus(IS_DISCONNECTED),
m_remoteEnabled(false),
m_remotePassword(),
m_remotePort(0U),
m_remote(NULL)
{
CHeaderData::initialise();
CG2Handler::initialise(0);
printf("SGSXLThread created. DExtra channels: %d, DCS Channels: %d\n", countDExtra, countDCS);
}
CSGSXLThread::~CSGSXLThread()
{
CHeaderData::finalise();
CG2Handler::finalise();
CGroupHandler::finalise();
CDExtraHandler::finalise();
CDCSHandler::finalise();
printf("SGSXLThread destroyed\n");
}
void CSGSXLThread::run()
{
m_g2Handler = new CG2ProtocolHandler(G2_DV_PORT, m_address);
bool ret = m_g2Handler->open();
if (!ret) {
printf("Could not open the G2 protocol handler\n");
delete m_g2Handler;
m_g2Handler = NULL;
}
// Wait here until we have the essentials to run
while (!m_killed && (m_g2Handler == NULL || m_irc == NULL || 0==m_callsign.size()))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (m_killed)
return;
m_stopped = false;
printf("Starting the Smart Group Server thread\n");
loadReflectors(DEXTRA_HOSTS_FILE_NAME, DP_DEXTRA);
loadReflectors(DCS_HOSTS_FILE_NAME, DP_DCS);
CDExtraProtocolHandlerPool dextraPool(DEXTRA_PORT, m_address);
CDCSProtocolHandlerPool dcsPool(DCS_PORT, m_address);
CG2Handler::setG2ProtocolHandler(m_g2Handler);
CDExtraHandler::setCallsign(m_callsign);
CDExtraHandler::setDExtraProtocolHandlerPool(&dextraPool);
CDCSHandler::setDCSProtocolHandlerPool(&dcsPool);
CDCSHandler::setGatewayType(GT_SMARTGROUP);
CGroupHandler::setCache(&m_cache);
CGroupHandler::setGateway(m_callsign);
CGroupHandler::setG2Handler(m_g2Handler);
CGroupHandler::setIRC(m_irc);
if (m_countDExtra || m_countDCS)
CGroupHandler::link();
if (m_remoteEnabled && m_remotePassword.size() && m_remotePort > 0U) {
m_remote = new CRemoteHandler();
bool res = m_remote->open(m_remotePassword, m_remotePort, false);
if (res) {
delete m_remote;
m_remote = NULL;
}
}
time_t start;
time(&start);
m_statusTimer.start();
try {
while (!m_killed) {
processIrcDDB();
processG2();
processDExtra(&dextraPool);
processDCS(&dcsPool);
if (m_remote != NULL)
m_remote->process();
time_t now;
time(&now);
unsigned long ms = (unsigned long)(1000.0 * difftime(now, start));
time(&start);
m_statusTimer.clock(ms);
CG2Handler::clock(ms);
CGroupHandler::clock(ms);
CDExtraHandler::clock(ms);
CDCSHandler::clock(ms);
std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PER_TIC_MS));
}
}
catch (std::exception& e) {
printf("Exception raised - \"%s\"\n", e.what());
}
catch (...) {
printf("Unknown exception raised\n");
}
printf("Stopping the Smart Group Server thread\n");
// Unlink from all reflectors
CDExtraHandler::unlink();
dextraPool.close();
// Unlink from all reflectors
CDCSHandler::unlink();
dcsPool.close();
m_g2Handler->close();
delete m_g2Handler;
m_irc->close();
delete m_irc;
if (m_remote != NULL) {
delete m_remote;
}
}
void CSGSXLThread::kill()
{
m_killed = true;
}
void CSGSXLThread::setCallsign(const std::string& callsign)
{
if (!m_stopped)
return;
m_callsign = callsign;
}
void CSGSXLThread::setAddress(const std::string& address)
{
m_address = address;
}
void CSGSXLThread::addGroup(const std::string& callsign, const std::string& logoff, const std::string& repeater, const std::string& infoText, const std::string& permanent, unsigned int userTimeout, CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string& reflector)
{
CGroupHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, callsignSwitch, txMsgSwitch, reflector);
}
void CSGSXLThread::setIRC(CIRCDDB* irc)
{
assert(irc != NULL);
m_irc = irc;
}
void CSGSXLThread::setLanguage(bool audioEnabled, TEXT_LANG lang)
{
m_audioEnabled = audioEnabled;
CAudioUnit::setLanguage(lang);
}
void CSGSXLThread::setRemote(bool enabled, const std::string& password, unsigned int port)
{
if (enabled) {
m_remoteEnabled = true;
m_remotePassword = password;
m_remotePort = port;
} else {
m_remoteEnabled = false;
m_remotePassword = password;
m_remotePort = REMOTE_DUMMY_PORT;
}
}
void CSGSXLThread::processIrcDDB()
{
// Once per second
if (m_statusTimer.hasExpired()) {
int status = m_irc->getConnectionState();
switch (status) {
case 0:
case 10:
if (m_lastStatus != IS_DISCONNECTED) {
printf("Disconnected from ircDDB\n");
m_lastStatus = IS_DISCONNECTED;
}
break;
case 7:
if (m_lastStatus != IS_CONNECTED) {
printf("Connected to ircDDB\n");
m_lastStatus = IS_CONNECTED;
}
break;
default:
if (m_lastStatus != IS_CONNECTING) {
printf("Connecting to ircDDB\n");
m_lastStatus = IS_CONNECTING;
}
break;
}
m_statusTimer.start();
}
// Process all incoming ircDDB messages, updating the caches
for (;;) {
IRCDDB_RESPONSE_TYPE type = m_irc->getMessageType();
switch (type) {
case IDRT_NONE:
return;
case IDRT_USER: {
std::string user, repeater, gateway, address, timestamp;
bool res = m_irc->receiveUser(user, repeater, gateway, address, timestamp);
if (!res)
break;
if (!address.empty()) {
printf("USER: %s %s %s %s\n", user.c_str(), repeater.c_str(), gateway.c_str(), address.c_str());
m_cache.updateUser(user, repeater, gateway, address, timestamp, DP_DEXTRA, false, false);
} else {
printf("USER: %s has no IP address!\n", user.c_str());
}
}
break;
case IDRT_REPEATER: {
std::string repeater, gateway, address;
bool res = m_irc->receiveRepeater(repeater, gateway, address);
if (!res)
break;
if (!address.empty()) {
printf("REPEATER: %s %s %s\n", repeater.c_str(), gateway.c_str(), address.c_str());
m_cache.updateRepeater(repeater, gateway, address, DP_DEXTRA, false, false);
} else {
printf("REPEATER: %s NOT FOUND\n", repeater.c_str());
}
}
break;
case IDRT_GATEWAY: {
std::string gateway, address;
bool res = m_irc->receiveGateway(gateway, address);
if (!res)
break;
CDExtraHandler::gatewayUpdate(gateway, address);
CDCSHandler::gatewayUpdate(gateway, address);
if (!address.empty()) {
printf("GATEWAY: %s %s\n", gateway.c_str(), address.c_str());
m_cache.updateGateway(gateway, address, DP_DEXTRA, false, false);
} else {
printf("GATEWAY: %s NOT FOUND\n", gateway.c_str());
}
}
break;
}
}
}
void CSGSXLThread::processDExtra(CDExtraProtocolHandlerPool *dextraPool)
{
for (;;) {
DEXTRA_TYPE type = dextraPool->read();
switch (type) {
case DE_NONE:
return;
case DE_POLL: {
CPollData* poll = dextraPool->newPoll();
if (poll != NULL) {
CDExtraHandler::process(*poll);
delete poll;
}
}
break;
case DE_CONNECT: {
CConnectData* connect = dextraPool->newConnect();
if (connect != NULL) {
CDExtraHandler::process(*connect);
delete connect;
}
}
break;
case DE_HEADER: {
CHeaderData* header = dextraPool->newHeader();
if (header != NULL) {
// printf("DExtra header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str());
CDExtraHandler::process(*header);
delete header;
}
}
break;
case DE_AMBE: {
CAMBEData* data = dextraPool->newAMBE();
if (data != NULL) {
CDExtraHandler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSXLThread::processDCS(CDCSProtocolHandlerPool *dcsPool)
{
for (;;) {
DCS_TYPE type = dcsPool->read();
switch (type) {
case DC_NONE:
return;
case DC_POLL: {
CPollData* poll = dcsPool->readPoll();
if (poll != NULL) {
CDCSHandler::process(*poll);
delete poll;
}
}
break;
case DC_CONNECT: {
CConnectData* connect = dcsPool->readConnect();
if (connect != NULL) {
CDCSHandler::process(*connect);
delete connect;
}
}
break;
case DC_DATA: {
CAMBEData* data = dcsPool->readData();
if (data != NULL) {
// printf("DCS header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str());
CDCSHandler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSXLThread::processG2()
{
for (;;) {
G2_TYPE type = m_g2Handler->read();
switch (type) {
case GT_NONE:
return;
case GT_HEADER: {
CHeaderData* header = m_g2Handler->readHeader();
if (header != NULL) {
//printf("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3());
CG2Handler::process(*header);
delete header;
}
}
break;
case GT_AMBE: {
CAMBEData* data = m_g2Handler->readAMBE();
if (data != NULL) {
CG2Handler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSXLThread::loadReflectors(const std::string fname, DSTAR_PROTOCOL dstarProtocol)
{
std::string filepath(DATA_DIR);
filepath += std::string("/") + fname;
struct stat sbuf;
if (stat(filepath.c_str(), &sbuf)) {
printf("%s doesn't exist!\n", filepath.c_str());
return;
}
std::ifstream hostfile;
hostfile.open(filepath, std::ifstream::in);
char line[256];
hostfile.getline(line, 256);
int count=0, tries=0;
while (hostfile.good()) {
const char *space = " \t\r";
char *first = strtok(line, space);
if (first) {
if ('#' != first[0]) {
tries++;
char *second = strtok(NULL, space);
if (second) {
char *third = strtok(NULL, space);
if (third && '#'==third[0])
third = NULL;
std::string name(first);
name.resize(7, ' ');
name.push_back('G');
struct hostent *he = gethostbyname(second);
if (he) {
count++;
std::string address(inet_ntoa(*(struct in_addr*)(he->h_addr_list[0])));
m_cache.updateGateway(name, address, dstarProtocol, third?1:0, true);
// printf("reflector:%s, address:%s lock:%s\n", name.c_str(), address.c_str(), third?"true":"false");
}
}
}
}
hostfile.getline(line, 256);
}
printf("Loaded %u of %u %s reflectors\n", count, tries, DP_DEXTRA==dstarProtocol?"DExtra":"DCS");
}

Powered by TurnKey Linux.