2018-06-06 20:33:31 +01:00
/*
2024-08-28 17:50:50 +01:00
* Copyright ( C ) 2018 , 2020 , 2024 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 0213 9 , USA .
*/
# include "DAPNETGateway.h"
# include "StopWatch.h"
# include "Version.h"
# include "Thread.h"
# include "Timer.h"
# include "Log.h"
2024-04-22 13:49:26 +01:00
# include "GitVersion.h"
2018-11-17 14:57:29 +00:00
# include "REGEX.h"
2018-11-14 16:15:07 +00:00
# include <regex>
2018-06-06 20:33:31 +01:00
# if defined(_WIN32) || defined(_WIN64)
# include <Windows.h>
# else
# include <sys/time.h>
# include <unistd.h>
# include <signal.h>
# include <fcntl.h>
# include <pwd.h>
# endif
# if defined(_WIN32) || defined(_WIN64)
const char * DEFAULT_INI_FILE = " DAPNETGateway.ini " ;
# else
const char * DEFAULT_INI_FILE = " /etc/DAPNETGateway.ini " ;
# endif
2024-08-28 17:50:50 +01:00
static bool m_killed = false ;
static int m_signal = 0 ;
# if !defined(_WIN32) && !defined(_WIN64)
static void sigHandler ( int signum )
{
m_killed = true ;
m_signal = signum ;
}
# endif
2018-07-11 08:19:56 +01:00
# include <algorithm>
2018-06-21 18:25:51 +01:00
# include <utility>
2018-06-06 20:33:31 +01:00
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <clocale>
# include <cassert>
# include <cmath>
2018-06-21 19:26:08 +01:00
const unsigned int FRAME_LENGTH_CODEWORDS = 2U ;
const unsigned int BATCH_LENGTH_CODEWORDS = 17U ;
const unsigned int PREAMBLE_LENGTH_CODEWORDS = BATCH_LENGTH_CODEWORDS + 1U ;
2018-06-21 18:25:51 +01:00
const unsigned int CODEWORD_TIME_US = 26667U ; // 26.667ms
2018-06-21 19:26:08 +01:00
const unsigned int FRAME_TIME_US = CODEWORD_TIME_US * FRAME_LENGTH_CODEWORDS ; // 53.333ms
const unsigned int BATCH_TIME_US = CODEWORD_TIME_US * BATCH_LENGTH_CODEWORDS ; // 453.333ms
const unsigned int PREAMBLE_TIME_US = CODEWORD_TIME_US * PREAMBLE_LENGTH_CODEWORDS ; // 480.006ms
2018-06-21 18:25:51 +01:00
const unsigned int SLOT_TIME_US = 6400000U ; // 6.4s
const unsigned int SLOT_TIME_MS = SLOT_TIME_US / 1000U ; // 6.4s
const unsigned int CODEWORDS_PER_SLOT = SLOT_TIME_US / CODEWORD_TIME_US ; // 240
const unsigned int BATCHES_PER_SLOT = SLOT_TIME_US / BATCH_TIME_US ; // 14
2018-06-13 18:53:40 +01:00
const unsigned char FUNCTIONAL_NUMERIC = 0U ;
2018-07-15 19:15:34 +01:00
const unsigned char FUNCTIONAL_ALERT1 = 1U ;
const unsigned char FUNCTIONAL_ALERT2 = 2U ;
2018-06-13 18:53:40 +01:00
const unsigned char FUNCTIONAL_ALPHANUMERIC = 3U ;
2018-07-19 18:07:25 +01:00
const unsigned int MAX_TIME_TO_HOLD_TIME_MESSAGES = 15000U ; // 15s
2018-06-25 19:11:33 +01:00
2018-06-06 20:33:31 +01:00
int main ( int argc , char * * argv )
{
const char * iniFile = DEFAULT_INI_FILE ;
if ( argc > 1 ) {
for ( int currentArg = 1 ; currentArg < argc ; + + currentArg ) {
std : : string arg = argv [ currentArg ] ;
if ( ( arg = = " -v " ) | | ( arg = = " --version " ) ) {
2024-04-22 13:49:26 +01:00
: : fprintf ( stdout , " DAPNETGateway version %s git #%.7s \n " , VERSION , gitversion ) ;
2018-06-06 20:33:31 +01:00
return 0 ;
} else if ( arg . substr ( 0 , 1 ) = = " - " ) {
: : fprintf ( stderr , " Usage: DAPNETGateway [-v|--version] [filename] \n " ) ;
return 1 ;
} else {
iniFile = argv [ currentArg ] ;
}
}
}
2024-08-28 17:50:50 +01:00
# if !defined(_WIN32) && !defined(_WIN64)
: : signal ( SIGINT , sigHandler ) ;
: : signal ( SIGTERM , sigHandler ) ;
: : signal ( SIGHUP , sigHandler ) ;
# endif
int ret = 0 ;
2018-06-06 20:33:31 +01:00
2024-08-28 17:50:50 +01:00
do {
m_signal = 0 ;
m_killed = false ;
2018-06-06 20:33:31 +01:00
2024-08-28 17:50:50 +01:00
CDAPNETGateway * gateway = new CDAPNETGateway ( std : : string ( iniFile ) ) ;
ret = gateway - > run ( ) ;
delete gateway ;
switch ( m_signal ) {
2024-08-31 20:10:13 +01:00
case 0 :
break ;
2024-08-28 17:50:50 +01:00
case 2 :
: : LogInfo ( " DAPNETGateway-%s exited on receipt of SIGINT " , VERSION ) ;
break ;
case 15 :
: : LogInfo ( " DAPNETGateway-%s exited on receipt of SIGTERM " , VERSION ) ;
break ;
case 1 :
: : LogInfo ( " DAPNETGateway-%s is restarting on receipt of SIGHUP " , VERSION ) ;
break ;
default :
: : LogInfo ( " DAPNETGateway-%s exited on receipt of an unknown signal " , VERSION ) ;
break ;
}
} while ( m_signal = = 1 ) ;
: : LogFinalise ( ) ;
2018-06-06 20:33:31 +01:00
return ret ;
}
CDAPNETGateway : : CDAPNETGateway ( const std : : string & configFile ) :
m_conf ( configFile ) ,
m_dapnetNetwork ( NULL ) ,
m_pocsagNetwork ( NULL ) ,
2018-06-21 18:25:51 +01:00
m_queue ( ) ,
2018-06-21 18:56:54 +01:00
m_slotTimer ( ) ,
2018-06-21 18:25:51 +01:00
m_schedule ( NULL ) ,
2018-06-25 19:11:33 +01:00
m_allSlots ( false ) ,
2018-06-21 18:25:51 +01:00
m_currentSlot ( 0U ) ,
m_sentCodewords ( 0U ) ,
2018-11-17 14:57:29 +00:00
m_regexBlacklist ( ) ,
m_regexWhitelist ( ) ,
m_mmdvmFree ( false )
2018-06-06 20:33:31 +01:00
{
2020-09-20 21:54:32 +01:00
CUDPSocket : : startup ( ) ;
2018-06-06 20:33:31 +01:00
}
CDAPNETGateway : : ~ CDAPNETGateway ( )
{
2018-06-07 07:17:55 +01:00
for ( std : : deque < CPOCSAGMessage * > : : iterator it = m_queue . begin ( ) ; it ! = m_queue . end ( ) ; + + it )
delete * it ;
m_queue . clear ( ) ;
2020-09-20 21:54:32 +01:00
CUDPSocket : : shutdown ( ) ;
2018-06-06 20:33:31 +01:00
}
int CDAPNETGateway : : run ( )
{
bool ret = m_conf . read ( ) ;
if ( ! ret ) {
: : fprintf ( stderr , " DAPNETGateway: cannot read the .ini file \n " ) ;
return 1 ;
}
setlocale ( LC_ALL , " C " ) ;
# if !defined(_WIN32) && !defined(_WIN64)
bool m_daemon = m_conf . getDaemon ( ) ;
if ( m_daemon ) {
// Create new process
pid_t pid = : : fork ( ) ;
if ( pid = = - 1 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Couldn't fork() , exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-18 18:53:56 +01:00
} else if ( pid ! = 0 ) {
2018-06-06 20:33:31 +01:00
exit ( EXIT_SUCCESS ) ;
2018-06-18 18:53:56 +01:00
}
2018-06-06 20:33:31 +01:00
// Create new session and process group
if ( : : setsid ( ) = = - 1 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Couldn't setsid(), exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
// Set the working directory to the root directory
if ( : : chdir ( " / " ) = = - 1 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Couldn't cd /, exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
2018-06-18 18:53:56 +01:00
// If we are currently root...
2018-06-06 20:33:31 +01:00
if ( getuid ( ) = = 0 ) {
struct passwd * user = : : getpwnam ( " mmdvm " ) ;
if ( user = = NULL ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Could not get the mmdvm user, exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
uid_t mmdvm_uid = user - > pw_uid ;
gid_t mmdvm_gid = user - > pw_gid ;
2018-06-18 18:53:56 +01:00
// Set user and group ID's to mmdvm:mmdvm
2018-06-06 20:33:31 +01:00
if ( setgid ( mmdvm_gid ) ! = 0 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Could not set mmdvm GID, exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
if ( setuid ( mmdvm_uid ) ! = 0 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " Could not set mmdvm UID, exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
2018-06-18 18:53:56 +01:00
// Double check it worked (AKA Paranoia)
2018-06-06 20:33:31 +01:00
if ( setuid ( 0 ) ! = - 1 ) {
2018-06-18 18:53:56 +01:00
: : fprintf ( stderr , " It's possible to regain root - something is wrong!, exiting \n " ) ;
2024-08-28 17:50:50 +01:00
return 1 ;
2018-06-06 20:33:31 +01:00
}
}
}
# endif
2018-08-27 20:30:25 +01:00
# if !defined(_WIN32) && !defined(_WIN64)
2020-10-31 22:16:07 +00:00
ret = : : LogInitialise ( m_daemon , m_conf . getLogFilePath ( ) , m_conf . getLogFileRoot ( ) , m_conf . getLogFileLevel ( ) , m_conf . getLogDisplayLevel ( ) , m_conf . getLogFileRotate ( ) ) ;
2020-10-05 13:22:45 +01:00
# else
2020-10-31 22:16:07 +00:00
ret = : : LogInitialise ( false , m_conf . getLogFilePath ( ) , m_conf . getLogFileRoot ( ) , m_conf . getLogFileLevel ( ) , m_conf . getLogDisplayLevel ( ) , m_conf . getLogFileRotate ( ) ) ;
2018-08-27 20:30:25 +01:00
# endif
2018-06-18 18:53:56 +01:00
if ( ! ret ) {
: : fprintf ( stderr , " DAPNETGateway: unable to open the log file \n " ) ;
return 1 ;
}
2018-07-26 18:33:08 +01:00
# if !defined(_WIN32) && !defined(_WIN64)
if ( m_daemon ) {
: : close ( STDIN_FILENO ) ;
: : close ( STDOUT_FILENO ) ;
: : close ( STDERR_FILENO ) ;
}
# endif
2018-06-06 20:33:31 +01:00
bool debug = m_conf . getDAPNETDebug ( ) ;
std : : string rptAddress = m_conf . getRptAddress ( ) ;
2021-04-25 07:43:56 +02:00
unsigned short rptPort = m_conf . getRptPort ( ) ;
2018-06-06 20:33:31 +01:00
std : : string myAddress = m_conf . getMyAddress ( ) ;
2021-04-25 07:43:56 +02:00
unsigned short myPort = m_conf . getMyPort ( ) ;
2018-06-06 20:33:31 +01:00
m_pocsagNetwork = new CPOCSAGNetwork ( myAddress , myPort , rptAddress , rptPort , debug ) ;
ret = m_pocsagNetwork - > open ( ) ;
if ( ! ret ) {
: : LogError ( " Cannot open the repeater network port " ) ;
return 1 ;
}
std : : string callsign = m_conf . getCallsign ( ) ;
std : : string dapnetAddress = m_conf . getDAPNETAddress ( ) ;
2021-04-25 07:43:56 +02:00
unsigned short dapnetPort = m_conf . getDAPNETPort ( ) ;
2018-06-06 20:33:31 +01:00
std : : string dapnetAuthKey = m_conf . getDAPNETAuthKey ( ) ;
2018-08-25 17:22:12 +02:00
if ( dapnetAuthKey . length ( ) = = 0 | | dapnetAuthKey = = " TOPSECRET " ) {
: : LogError ( " AuthKey not set or invalid " ) ;
return 1 ;
}
2018-10-22 23:29:12 +02:00
m_dapnetNetwork = new CDAPNETNetwork ( dapnetAddress , dapnetPort , callsign , dapnetAuthKey , VERSION , false , 1 , debug ) ;
2018-06-06 20:33:31 +01:00
ret = m_dapnetNetwork - > open ( ) ;
if ( ! ret ) {
m_pocsagNetwork - > close ( ) ;
delete m_pocsagNetwork ;
delete m_dapnetNetwork ;
: : LogError ( " Cannot open the DAPNET network port " ) ;
return 1 ;
}
2018-10-22 23:05:46 +02:00
LogMessage ( " Starting DAPNETGateway-%s " , VERSION ) ;
2018-06-06 20:33:31 +01:00
ret = m_dapnetNetwork - > login ( ) ;
if ( ! ret ) {
m_pocsagNetwork - > close ( ) ;
m_dapnetNetwork - > close ( ) ;
delete m_pocsagNetwork ;
delete m_dapnetNetwork ;
: : LogError ( " Cannot login to the DAPNET network " ) ;
return 1 ;
}
2018-07-10 19:18:27 +01:00
std : : vector < unsigned int > whiteList = m_conf . getWhiteList ( ) ;
2018-12-10 20:59:40 +01:00
std : : vector < unsigned int > blackList = m_conf . getBlackList ( ) ;
2018-12-05 14:35:13 +00:00
2018-11-17 14:57:29 +00:00
std : : vector < std : : regex > regexBlacklist ;
std : : vector < std : : regex > regexWhitelist ;
2018-12-10 21:07:43 +01:00
LogMessage ( " Initializing blacklist " ) ;
2018-11-17 16:37:11 +00:00
m_regexBlacklist = new CREGEX ( m_conf . getblacklistRegexfile ( ) ) ;
2018-11-17 11:25:45 +00:00
if ( m_regexBlacklist - > load ( ) )
2018-11-17 14:57:29 +00:00
regexBlacklist = m_regexBlacklist - > get ( ) ;
2018-12-10 21:07:43 +01:00
LogMessage ( " Initializing whitelist " ) ;
2018-11-17 16:37:11 +00:00
m_regexWhitelist = new CREGEX ( m_conf . getwhitelistRegexfile ( ) ) ;
2018-11-18 23:35:06 +01:00
if ( m_regexWhitelist - > load ( ) )
regexWhitelist = m_regexWhitelist - > get ( ) ;
2018-11-17 14:57:29 +00:00
2018-11-17 11:25:45 +00:00
2024-08-28 17:50:50 +01:00
while ( ! m_killed ) {
2018-06-06 20:33:31 +01:00
unsigned char buffer [ 200U ] ;
if ( m_pocsagNetwork - > read ( buffer ) > 0U ) {
switch ( buffer [ 0U ] ) {
case 0x00U :
// The MMDVM is idle
2018-06-21 18:25:51 +01:00
if ( ! m_mmdvmFree ) {
2018-06-25 19:11:33 +01:00
// LogDebug("*** MMDVM is free");
2018-06-21 18:25:51 +01:00
m_mmdvmFree = true ;
2018-06-21 18:56:54 +01:00
m_sentCodewords = ( m_slotTimer . elapsed ( ) * 1000U ) / CODEWORD_TIME_US ;
2018-06-21 18:25:51 +01:00
}
2018-06-06 20:33:31 +01:00
break ;
case 0xFFU :
// The MMDVM is busy
2018-06-25 19:11:33 +01:00
// LogDebug("*** MMDVM is busy");
2018-06-21 18:25:51 +01:00
m_mmdvmFree = false ;
2018-06-06 20:33:31 +01:00
break ;
default :
// The MMDVM is sending crap
LogWarning ( " Unknown data from the MMDVM - 0x%02X " , buffer [ 0U ] ) ;
break ;
}
}
bool ok = m_dapnetNetwork - > read ( ) ;
2018-08-24 20:24:42 +02:00
if ( ! ok )
recover ( ) ;
2018-08-25 12:55:10 +02:00
2018-06-06 22:00:43 +01:00
CPOCSAGMessage * message = m_dapnetNetwork - > readMessage ( ) ;
if ( message ! = NULL ) {
2018-07-15 19:59:29 +01:00
bool found = true ;
2018-12-05 14:35:13 +00:00
bool blackListRIC = false ;
2018-11-17 16:37:11 +00:00
bool blacklistRegexmatch = false ;
bool whitelistRegexmatch = true ;
2018-07-10 19:18:27 +01:00
// If we have a white list of RICs, use it.
if ( ! whiteList . empty ( ) )
found = std : : find ( whiteList . begin ( ) , whiteList . end ( ) , message - > m_ric ) ! = whiteList . end ( ) ;
2018-11-18 23:35:06 +01:00
2018-12-10 20:59:40 +01:00
// If we have a black list of RICs, use it.
if ( ! blackList . empty ( ) )
blackListRIC = std : : find ( blackList . begin ( ) , blackList . end ( ) , message - > m_ric ) ! = blackList . end ( ) ;
2018-12-05 14:41:25 +00:00
if ( blackListRIC )
LogDebug ( " Blacklist match: Not queueing message to %07u, type %u, message: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
2018-12-05 14:36:58 +00:00
2018-11-17 14:57:29 +00:00
std : : string messageBody ( reinterpret_cast < char * > ( message - > m_message ) ) ;
2018-11-14 16:26:57 +00:00
//If we have a list of blacklist REGEXes, use them
2018-11-17 14:57:29 +00:00
if ( ! regexBlacklist . empty ( ) ) {
for ( std : : regex regex : regexBlacklist ) {
2018-11-14 16:15:07 +00:00
bool ret = std : : regex_match ( messageBody , regex ) ;
2018-11-14 16:26:57 +00:00
//If the regex matches the message body, don't send the message
2018-11-17 11:25:45 +00:00
if ( ret ) {
2018-11-17 16:37:11 +00:00
blacklistRegexmatch = true ;
2018-11-17 11:25:45 +00:00
LogDebug ( " Blacklist REGEX match: Not queueing message to %07u, type %u, message: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , messageBody . c_str ( ) ) ;
}
2018-11-14 16:15:07 +00:00
}
}
2018-07-10 19:18:27 +01:00
2018-11-17 16:37:11 +00:00
if ( ! regexWhitelist . empty ( ) & & ! blacklistRegexmatch ) {
2018-11-18 23:35:06 +01:00
for ( std : : regex regex : regexWhitelist ) {
bool ret = std : : regex_match ( messageBody , regex ) ;
//If the regex does not match the message body, don't send the message
if ( ! ret ) {
whitelistRegexmatch = false ;
LogDebug ( " No whitelist REGEX match: Not queueing message to %07u, type %u, message: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , messageBody . c_str ( ) ) ;
}
}
2018-11-17 14:57:29 +00:00
}
2018-12-10 20:59:40 +01:00
if ( found & & ! blackListRIC & & ! blacklistRegexmatch & & whitelistRegexmatch ) {
2018-07-15 19:15:34 +01:00
switch ( message - > m_functional ) {
case FUNCTIONAL_ALPHANUMERIC :
LogDebug ( " Queueing message to %07u, type %u, func Alphanumeric: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-26 18:27:39 +01:00
case FUNCTIONAL_ALERT2 :
LogDebug ( " Queueing message to %07u, type %u, func Alert 2: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-15 19:15:34 +01:00
case FUNCTIONAL_NUMERIC :
LogDebug ( " Queueing message to %07u, type %u, func Numeric: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
case FUNCTIONAL_ALERT1 :
LogDebug ( " Queueing message to %07u, type %u, func Alert 1 " , message - > m_ric , message - > m_type ) ;
break ;
default :
break ;
2018-07-15 20:29:00 +02:00
}
2018-07-15 19:15:34 +01:00
2018-07-10 19:18:27 +01:00
m_queue . push_front ( message ) ;
2018-11-27 01:55:16 +01:00
LogDebug ( " Messages in Queue %04u " , m_queue . size ( ) ) ;
2018-07-10 19:18:27 +01:00
}
2018-06-06 22:00:43 +01:00
}
2018-06-21 18:56:54 +01:00
unsigned int t = ( m_slotTimer . time ( ) / 100ULL ) % 1024ULL ;
2018-06-21 18:25:51 +01:00
unsigned int slot = t / 64U ;
if ( slot ! = m_currentSlot ) {
2018-06-25 19:11:33 +01:00
// LogDebug("Start of slot %u", slot);
2018-06-21 18:25:51 +01:00
m_currentSlot = slot ;
if ( m_schedule = = NULL | | m_currentSlot = = 0U )
loadSchedule ( ) ;
m_sentCodewords = 0U ;
2018-06-21 18:56:54 +01:00
m_slotTimer . start ( ) ;
2018-06-06 20:33:31 +01:00
}
2018-06-21 18:25:51 +01:00
sendMessages ( ) ;
2018-06-21 18:56:54 +01:00
CThread : : sleep ( 10U ) ;
2018-06-06 20:33:31 +01:00
}
m_pocsagNetwork - > close ( ) ;
delete m_pocsagNetwork ;
m_dapnetNetwork - > close ( ) ;
delete m_dapnetNetwork ;
return 0 ;
}
2018-06-21 18:25:51 +01:00
void CDAPNETGateway : : sendMessages ( )
2018-06-06 20:33:31 +01:00
{
2018-06-21 18:25:51 +01:00
// If the MMDVM is busy, we can't send anything.
2018-06-25 19:11:33 +01:00
if ( ! m_mmdvmFree )
2018-06-21 18:25:51 +01:00
return ;
2018-06-06 20:33:31 +01:00
2018-06-21 18:25:51 +01:00
// Do we have a schedule?
2018-06-25 19:11:33 +01:00
if ( m_schedule = = NULL )
2018-06-21 18:25:51 +01:00
return ;
// Check to see if we're allowed to send within a slot.
2018-06-25 19:11:33 +01:00
if ( ! m_schedule [ m_currentSlot ] )
2018-06-21 18:25:51 +01:00
return ;
// If we have data to send, see if we have time to do so in the current schedule.
if ( m_queue . empty ( ) )
return ;
CPOCSAGMessage * message = m_queue . back ( ) ;
assert ( message ! = NULL ) ;
2018-06-25 19:11:33 +01:00
// Special case, only test if slots are being used.
if ( m_allSlots ) {
sendMessage ( message ) ;
m_queue . pop_back ( ) ;
delete message ;
return ;
}
2018-06-21 18:56:54 +01:00
unsigned int codewords = calculateCodewords ( message ) ;
// Do we have too much data already sent in this slot?
2018-06-21 19:26:08 +01:00
unsigned int totalCodewords = m_sentCodewords + PREAMBLE_LENGTH_CODEWORDS + codewords ;
2018-06-25 19:11:33 +01:00
if ( totalCodewords > = CODEWORDS_PER_SLOT ) {
// LogDebug("Too many codewords sent in slot %u already %u + %u + %u = %u >= %u", m_currentSlot, m_sentCodewords, PREAMBLE_LENGTH_CODEWORDS, codewords, totalCodewords, CODEWORDS_PER_SLOT);
2018-06-21 18:56:54 +01:00
return ;
2018-06-25 19:11:33 +01:00
}
2018-06-21 18:56:54 +01:00
// Is there enough time to send it in this slot before it ends?
2018-06-21 19:26:08 +01:00
unsigned int sendTime = ( PREAMBLE_TIME_US + codewords * CODEWORD_TIME_US ) / 1000U ;
2018-06-21 18:56:54 +01:00
unsigned int timeLeft = SLOT_TIME_MS - m_slotTimer . elapsed ( ) ;
2018-06-25 19:11:33 +01:00
if ( sendTime > = timeLeft ) {
// LogDebug("Too little time to send the message in slot %u, %u + %u + %u = %u >= %u = %u - %u", m_currentSlot, PREAMBLE_TIME_US, codewords, CODEWORD_TIME_US, sendTime, timeLeft, SLOT_TIME_MS, m_slotTimer.elapsed());
2018-06-21 18:25:51 +01:00
return ;
2018-06-25 19:11:33 +01:00
}
2018-06-21 18:25:51 +01:00
2018-06-25 19:11:33 +01:00
bool ret = sendMessage ( message ) ;
if ( ret )
m_sentCodewords = totalCodewords ;
2018-06-21 18:25:51 +01:00
m_queue . pop_back ( ) ;
delete message ;
2018-06-06 20:33:31 +01:00
}
bool CDAPNETGateway : : recover ( )
{
2018-08-24 20:24:42 +02:00
2018-06-06 20:33:31 +01:00
for ( ; ; ) {
m_dapnetNetwork - > close ( ) ;
bool ok = m_dapnetNetwork - > open ( ) ;
if ( ok ) {
ok = m_dapnetNetwork - > login ( ) ;
if ( ok )
return true ;
}
}
return false ;
}
2018-06-13 19:41:35 +01:00
bool CDAPNETGateway : : isTimeMessage ( const CPOCSAGMessage * message ) const
{
if ( message - > m_type = = 5U & & message - > m_functional = = FUNCTIONAL_NUMERIC )
return true ;
if ( message - > m_type = = 6U & & message - > m_functional = = FUNCTIONAL_ALPHANUMERIC & & : : memcmp ( message - > m_message , " XTIME= " , 6U ) = = 0 )
return true ;
return false ;
}
2018-06-21 18:25:51 +01:00
unsigned int CDAPNETGateway : : calculateCodewords ( const CPOCSAGMessage * message ) const
{
assert ( message ! = NULL ) ;
unsigned int len = 0U ;
2018-07-15 19:15:34 +01:00
switch ( message - > m_functional ) {
case FUNCTIONAL_NUMERIC :
len = message - > m_length / 5U ; // For the number packing, five to a word
break ;
case FUNCTIONAL_ALPHANUMERIC :
2018-07-20 11:16:48 +02:00
case FUNCTIONAL_ALERT2 :
2018-07-15 19:15:34 +01:00
len = ( message - > m_length * 7U ) / 20U ; // For the ASCII packing, 7/20 to a word
break ;
case FUNCTIONAL_ALERT1 :
default :
break ;
}
2018-06-21 18:25:51 +01:00
len + + ; // For the address word
if ( ( len % 2U ) = = 1U ) // Always an even number of words
len + + ;
2018-06-25 19:11:33 +01:00
len + = len % 16U ; // A very long message will include sync words
2018-06-21 18:25:51 +01:00
return len ;
}
void CDAPNETGateway : : loadSchedule ( )
{
bool * schedule = m_dapnetNetwork - > readSchedule ( ) ;
if ( schedule = = NULL )
return ;
delete [ ] m_schedule ;
m_schedule = schedule ;
2018-06-25 19:11:33 +01:00
m_allSlots = true ;
2018-06-21 18:25:51 +01:00
std : : string text ;
for ( unsigned int i = 0U ; i < 16U ; i + + ) {
2018-06-25 19:11:33 +01:00
if ( m_schedule [ i ] ) {
2018-06-21 18:25:51 +01:00
text + = " * " ;
2018-06-25 19:11:33 +01:00
} else {
2018-06-21 18:25:51 +01:00
text + = " - " ;
2018-06-25 19:11:33 +01:00
m_allSlots = false ;
}
2018-06-21 18:25:51 +01:00
}
2018-06-25 19:11:33 +01:00
if ( m_allSlots )
LogMessage ( " All slots are available for transmission " ) ;
else
LogMessage ( " Loaded new schedule: %s " , text . c_str ( ) ) ;
2018-06-21 18:25:51 +01:00
}
2018-06-21 19:26:08 +01:00
2018-06-25 19:11:33 +01:00
bool CDAPNETGateway : : sendMessage ( CPOCSAGMessage * message ) const
2018-06-21 19:26:08 +01:00
{
assert ( message ! = NULL ) ;
2018-06-25 19:11:33 +01:00
bool ret = isTimeMessage ( message ) ;
2018-07-15 19:15:34 +01:00
if ( ret & & message - > m_timeQueued . elapsed ( ) > = MAX_TIME_TO_HOLD_TIME_MESSAGES ) {
switch ( message - > m_functional ) {
case FUNCTIONAL_ALPHANUMERIC :
LogDebug ( " Rejecting message to %07u, type %u, func Alphanumeric: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-26 18:27:39 +01:00
case FUNCTIONAL_ALERT2 :
LogDebug ( " Rejecting message to %07u, type %u, func Alert 2: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-15 19:15:34 +01:00
case FUNCTIONAL_NUMERIC :
LogDebug ( " Rejecting message to %07u, type %u, func Numeric: \" %.*s \" " , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
case FUNCTIONAL_ALERT1 :
LogDebug ( " Rejecting message to %07u, type %u, func Alert 1 " , message - > m_ric , message - > m_type ) ;
break ;
default :
break ;
}
2018-06-25 19:11:33 +01:00
return false ;
} else {
2018-07-15 19:15:34 +01:00
switch ( message - > m_functional ) {
case FUNCTIONAL_ALPHANUMERIC :
LogMessage ( " Sending message in slot %u to %07u, type %u, func Alphanumeric: \" %.*s \" " , m_currentSlot , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-26 18:27:39 +01:00
case FUNCTIONAL_ALERT2 :
LogMessage ( " Sending message in slot %u to %07u, type %u, func Alert 2: \" %.*s \" " , m_currentSlot , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
2018-07-15 19:15:34 +01:00
case FUNCTIONAL_NUMERIC :
LogMessage ( " Sending message in slot %u to %07u, type %u, func Numeric: \" %.*s \" " , m_currentSlot , message - > m_ric , message - > m_type , message - > m_length , message - > m_message ) ;
break ;
case FUNCTIONAL_ALERT1 :
LogMessage ( " Sending message in slot %u to %07u, type %u, func Alert 1 " , m_currentSlot , message - > m_ric , message - > m_type ) ;
break ;
default :
break ;
}
2018-06-25 19:11:33 +01:00
m_pocsagNetwork - > write ( message ) ;
return true ;
2018-06-21 19:26:08 +01:00
}
}