2020-08-19 15:59:20 -04:00
#!/usr/bin/env python
#
###############################################################################
2021-07-20 18:14:59 -04:00
# Copyright (C) 2020 Simon Adlem, G7RZU <g7rzu@gb7fr.org.uk>
2020-09-19 09:05:52 -04:00
# Copyright (C) 2016-2019 Cortney T. Buffington, N0MJS <n0mjs@me.com>
2020-08-19 15:59:20 -04: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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
###############################################################################
2021-07-20 18:14:59 -04:00
'''
This application , in conjuction with it ' s rule file (rules.py) will
work like a " conference bridge " . This is similar to what most hams think of as a
reflector . You define conference bridges and any system joined to that conference
bridge will both receive traffic from , and send traffic to any other system
joined to the same conference bridge . It does not provide end - to - end connectivity
as each end system must individually be joined to a conference bridge ( a name
you create in the configuraiton file ) to pass traffic .
This program currently only works with group voice calls .
'''
2020-08-19 15:59:20 -04:00
# Python modules we need
import sys
from bitarray import bitarray
2021-07-23 12:01:16 -04:00
from time import time , sleep , perf_counter
2020-08-19 15:59:20 -04:00
import importlib . util
2020-09-22 15:06:07 -04:00
import re
2021-02-24 17:24:19 -05:00
import copy
2021-09-12 06:19:07 -04:00
from setproctitle import setproctitle
2020-08-19 15:59:20 -04:00
# Twisted is pretty important, so I keep it separate
from twisted . internet . protocol import Factory , Protocol
from twisted . protocols . basic import NetstringReceiver
from twisted . internet import reactor , task
# Things we import from the main hblink module
2021-07-04 20:10:26 -04:00
from hblink import HBSYSTEM , OPENBRIDGE , systems , hblink_handler , reportFactory , REPORT_OPCODES , mk_aliases , acl_check
2020-09-13 13:04:11 -04:00
from dmr_utils3 . utils import bytes_3 , int_id , get_alias , bytes_4
2020-08-19 15:59:20 -04:00
from dmr_utils3 import decode , bptc , const
import config
2020-09-30 15:46:35 -04:00
from config import acl_build
2020-08-19 15:59:20 -04:00
import log
from const import *
2020-09-13 13:04:11 -04:00
from mk_voice import pkt_gen
2020-09-17 15:34:50 -04:00
#from voice_lib import words
#Read voices
from read_ambe import readAMBE
2021-02-14 10:42:43 -05:00
#Remap some words for certain languages
from i8n_voice_map import voiceMap
2020-09-17 15:34:50 -04:00
2020-09-29 09:43:17 -04:00
#MySQL
from mysql_config import useMYSQL
2020-08-19 15:59:20 -04:00
# Stuff for socket reporting
import pickle
# REMOVE LATER from datetime import datetime
# The module needs logging, but handlers, etc. are controlled by the parent
import logging
logger = logging . getLogger ( __name__ )
2020-12-27 12:32:38 -05:00
#REGEX
import re
2020-08-19 15:59:20 -04:00
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
2020-08-20 11:47:17 -04:00
__author__ = ' Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU '
2021-04-29 19:04:10 -04:00
__copyright__ = ' Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem, G7RZU 2020,2021 '
2021-05-24 17:32:54 -04:00
__credits__ = ' Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT; Jon Lee, G4TSN; Norman Williams, M6NBP '
2020-08-19 15:59:20 -04:00
__license__ = ' GNU GPLv3 '
2020-08-20 11:47:17 -04:00
__maintainer__ = ' Simon Adlem G7RZU '
__email__ = ' simon@gb7fr.org.uk '
2020-08-19 15:59:20 -04:00
# Module gobal varaibles
# Timed loop used for reporting HBP status
#
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
def config_reports ( _config , _factory ) :
if True : #_config['REPORTS']['REPORT']:
def reporting_loop ( logger , _server ) :
logger . debug ( ' (REPORT) Periodic reporting loop started ' )
_server . send_config ( )
_server . send_bridge ( )
2021-09-12 06:19:07 -04:00
i = 0
for system in CONFIG [ ' SYSTEMS ' ] :
if ' PEERS ' in CONFIG [ ' SYSTEMS ' ] [ system ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PEERS ' ] :
i = i + 1
logger . info ( ' (REPORT) %s systems have at least one peer ' , i )
2020-08-19 15:59:20 -04:00
logger . info ( ' (REPORT) HBlink TCP reporting server configured ' )
report_server = _factory ( _config )
report_server . clients = [ ]
reactor . listenTCP ( _config [ ' REPORTS ' ] [ ' REPORT_PORT ' ] , report_server )
reporting = task . LoopingCall ( reporting_loop , logger , report_server )
reporting . start ( _config [ ' REPORTS ' ] [ ' REPORT_INTERVAL ' ] )
return report_server
# Import Bridging rules
# Note: A stanza *must* exist for any MASTER or CLIENT configured in the main
# configuration file and listed as "active". It can be empty,
# but it has to exist.
def make_bridges ( _rules ) :
# Convert integer GROUP ID numbers from the config into hex strings
# we need to send in the actual data packets.
for _bridge in _rules :
for _system in _rules [ _bridge ] :
if _system [ ' SYSTEM ' ] not in CONFIG [ ' SYSTEMS ' ] :
sys . exit ( ' ERROR: Conference bridge " {} " references a system named " {} " that is not enabled in the main configuration ' . format ( _bridge , _system [ ' SYSTEM ' ] ) )
_system [ ' TGID ' ] = bytes_3 ( _system [ ' TGID ' ] )
for i , e in enumerate ( _system [ ' ON ' ] ) :
_system [ ' ON ' ] [ i ] = bytes_3 ( _system [ ' ON ' ] [ i ] )
for i , e in enumerate ( _system [ ' OFF ' ] ) :
_system [ ' OFF ' ] [ i ] = bytes_3 ( _system [ ' OFF ' ] [ i ] )
_system [ ' TIMEOUT ' ] = _system [ ' TIMEOUT ' ] * 60
if _system [ ' ACTIVE ' ] == True :
_system [ ' TIMER ' ] = time ( ) + _system [ ' TIMEOUT ' ]
else :
_system [ ' TIMER ' ] = time ( )
2020-08-31 06:03:51 -04:00
2020-08-31 11:05:25 -04:00
# if _bridge[0:1] == '#':
# continue
2020-08-31 06:03:51 -04:00
2020-08-23 15:19:23 -04:00
for _confsystem in CONFIG [ ' SYSTEMS ' ] :
2021-02-28 07:05:03 -05:00
#if _confsystem[0:3] == 'OBP':
if CONFIG [ ' SYSTEMS ' ] [ _confsystem ] [ ' MODE ' ] != ' MASTER ' :
2020-08-23 15:19:23 -04:00
continue
2020-08-26 05:48:44 -04:00
ts1 = False
2020-08-23 15:19:23 -04:00
ts2 = False
for i , e in enumerate ( _rules [ _bridge ] ) :
if e [ ' SYSTEM ' ] == _confsystem and e [ ' TS ' ] == 1 :
ts1 = True
if e [ ' SYSTEM ' ] == _confsystem and e [ ' TS ' ] == 2 :
ts2 = True
2020-08-31 11:05:25 -04:00
if _bridge [ 0 : 1 ] != ' # ' :
_tmout = CONFIG [ ' SYSTEMS ' ] [ _confsystem ] [ ' DEFAULT_UA_TIMER ' ]
if ts1 == False :
_rules [ _bridge ] . append ( { ' SYSTEM ' : _confsystem , ' TS ' : 1 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if ts2 == False :
_rules [ _bridge ] . append ( { ' SYSTEM ' : _confsystem , ' TS ' : 2 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
else :
_tmout = CONFIG [ ' SYSTEMS ' ] [ _confsystem ] [ ' DEFAULT_UA_TIMER ' ]
if ts2 == False :
_rules [ _bridge ] . append ( { ' SYSTEM ' : _confsystem , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ bytes_3 ( 4000 ) ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-08-26 05:48:44 -04:00
2020-08-19 15:59:20 -04:00
return _rules
2020-08-20 11:47:17 -04:00
#Make a single bridge - used for on-the-fly UA bridges
2020-10-06 17:20:57 -04:00
def make_single_bridge ( _tgid , _sourcesystem , _slot , _tmout ) :
2020-08-22 14:18:26 -04:00
_tgid_s = str ( int_id ( _tgid ) )
2021-03-28 07:10:44 -04:00
#Always a 1 min timeout for Echo
2021-03-28 07:14:57 -04:00
if _tgid_s == ' 9990 ' :
2021-03-28 07:10:44 -04:00
_tmout = 1
2020-08-22 14:18:26 -04:00
BRIDGES [ _tgid_s ] = [ ]
2020-08-22 06:49:09 -04:00
for _system in CONFIG [ ' SYSTEMS ' ] :
2020-11-28 10:56:02 -05:00
if _system [ 0 : 3 ] != ' OBP ' :
2020-10-04 11:16:48 -04:00
#if CONFIG['SYSTEMS'][system]['MODE'] == 'MASTER':
2020-10-06 17:20:57 -04:00
#_tmout = CONFIG['SYSTEMS'][_system]['DEFAULT_UA_TIMER']
2020-08-23 08:27:26 -04:00
if _system == _sourcesystem :
if _slot == 1 :
2020-11-07 09:57:32 -05:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : True , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
2020-09-02 13:34:25 -04:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-08-23 08:27:26 -04:00
else :
2020-11-07 09:57:32 -05:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : _tgid , ' ACTIVE ' : True , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
2020-09-02 13:34:25 -04:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-08-23 08:27:26 -04:00
else :
2020-09-02 13:34:25 -04:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-08-23 11:58:29 -04:00
2021-07-29 17:52:29 -04:00
if _system [ 0 : 3 ] == ' OBP ' and ( int_id ( _tgid ) > = 79 and ( int_id ( _tgid ) < 9990 or int_id ( _tgid ) > 9999 ) ) :
2020-08-23 08:27:26 -04:00
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : True , ' TIMEOUT ' : ' ' , ' TO_TYPE ' : ' NONE ' , ' OFF ' : [ ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-10-05 18:29:00 -04:00
2021-01-10 08:50:37 -05:00
#Make static bridge - used for on-the-fly relay bridges
def make_stat_bridge ( _tgid ) :
_tgid_s = str ( int_id ( _tgid ) )
BRIDGES [ _tgid_s ] = [ ]
for _system in CONFIG [ ' SYSTEMS ' ] :
if _system [ 0 : 3 ] != ' OBP ' :
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' MODE ' ] == ' MASTER ' :
_tmout = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_UA_TIMER ' ]
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : _tgid , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if _system [ 0 : 3 ] == ' OBP ' :
BRIDGES [ _tgid_s ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : True , ' TIMEOUT ' : ' ' , ' TO_TYPE ' : ' STAT ' , ' OFF ' : [ ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-10-04 10:19:46 -04:00
2020-10-06 17:20:57 -04:00
def make_default_reflector ( reflector , _tmout , system ) :
2020-10-04 10:19:46 -04:00
bridge = ' # ' + str ( reflector )
2020-10-06 17:20:57 -04:00
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
2020-10-04 10:19:46 -04:00
if bridge not in BRIDGES :
BRIDGES [ bridge ] = [ ]
2020-10-06 17:20:57 -04:00
make_single_reflector ( bytes_3 ( reflector ) , _tmout , system )
2020-10-04 10:19:46 -04:00
bridgetemp = [ ]
for bridgesystem in BRIDGES [ bridge ] :
if bridgesystem [ ' SYSTEM ' ] == system and bridgesystem [ ' TS ' ] == 2 :
bridgetemp . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : True , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' OFF ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( reflector ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
else :
bridgetemp . append ( bridgesystem )
BRIDGES [ bridge ] = bridgetemp
2020-10-06 17:20:57 -04:00
def make_static_tg ( tg , ts , _tmout , system ) :
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
if str ( tg ) not in BRIDGES :
make_single_bridge ( bytes_3 ( tg ) , system , ts , _tmout )
2020-10-05 18:29:00 -04:00
bridgetemp = [ ]
for bridgesystem in BRIDGES [ str ( tg ) ] :
if bridgesystem [ ' SYSTEM ' ] == system and bridgesystem [ ' TS ' ] == ts :
bridgetemp . append ( { ' SYSTEM ' : system , ' TS ' : ts , ' TGID ' : bytes_3 ( tg ) , ' ACTIVE ' : True , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' OFF ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( tg ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
else :
bridgetemp . append ( bridgesystem )
BRIDGES [ str ( tg ) ] = bridgetemp
2020-10-05 18:32:36 -04:00
2020-10-06 17:20:57 -04:00
def reset_static_tg ( tg , ts , _tmout , system ) :
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
2020-10-05 18:32:36 -04:00
bridgetemp = [ ]
2021-04-21 14:13:28 -04:00
try :
for bridgesystem in BRIDGES [ str ( tg ) ] :
if bridgesystem [ ' SYSTEM ' ] == system and bridgesystem [ ' TS ' ] == ts :
bridgetemp . append ( { ' SYSTEM ' : system , ' TS ' : ts , ' TGID ' : bytes_3 ( tg ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( tg ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
else :
bridgetemp . append ( bridgesystem )
BRIDGES [ str ( tg ) ] = bridgetemp
except KeyError :
2021-04-29 19:04:10 -04:00
logger . exception ( ' (ERROR) KeyError in reset_static_tg() - bridge gone away? ' )
2021-04-21 14:13:28 -04:00
return
2020-10-05 18:29:00 -04:00
2020-10-06 17:20:57 -04:00
def reset_default_reflector ( reflector , _tmout , system ) :
2020-10-04 10:19:46 -04:00
bridge = ' # ' + str ( reflector )
2020-10-06 17:20:57 -04:00
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
2020-10-04 10:19:46 -04:00
if bridge not in BRIDGES :
BRIDGES [ bridge ] = [ ]
2020-10-06 17:20:57 -04:00
make_single_reflector ( bytes_3 ( reflector ) , _tmout , system )
2020-10-04 10:19:46 -04:00
bridgetemp = [ ]
for bridgesystem in BRIDGES [ bridge ] :
if bridgesystem [ ' SYSTEM ' ] == system and bridgesystem [ ' TS ' ] == 2 :
bridgetemp . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( reflector ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
else :
bridgetemp . append ( bridgesystem )
BRIDGES [ bridge ] = bridgetemp
2020-09-02 13:34:25 -04:00
2020-10-06 17:20:57 -04:00
def make_single_reflector ( _tgid , _tmout , _sourcesystem ) :
2020-09-02 13:34:25 -04:00
_tgid_s = str ( int_id ( _tgid ) )
_bridge = ' # ' + _tgid_s
2021-03-28 10:28:47 -04:00
#1 min timeout for echo
if _tgid_s == ' 9990 ' :
_tmout = 1
2020-09-02 13:34:25 -04:00
BRIDGES [ _bridge ] = [ ]
for _system in CONFIG [ ' SYSTEMS ' ] :
2021-02-28 07:05:03 -05:00
#if _system[0:3] != 'OBP':
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' MODE ' ] == ' MASTER ' :
2020-10-06 17:20:57 -04:00
#_tmout = CONFIG['SYSTEMS'][_system]['DEFAULT_UA_TIMER']
2020-09-02 13:34:25 -04:00
if _system == _sourcesystem :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : True , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) + ( _tmout * 60 ) } )
else :
2020-11-07 12:36:15 -05:00
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_UA_TIMER ' ] * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ _tgid , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2021-07-29 18:12:19 -04:00
if _system [ 0 : 3 ] == ' OBP ' and ( int_id ( _tgid ) > = 79 and ( int_id ( _tgid ) < 9990 or int_id ( _tgid ) > 9999 ) ) :
2020-09-03 12:09:53 -04:00
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : _tgid , ' ACTIVE ' : True , ' TIMEOUT ' : ' ' , ' TO_TYPE ' : ' NONE ' , ' OFF ' : [ ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2020-10-18 08:41:38 -04:00
def remove_bridge_system ( system ) :
_bridgestemp = { }
_bridgetemp = { }
for _bridge in BRIDGES :
for _bridgesystem in BRIDGES [ _bridge ] :
if _bridgesystem [ ' SYSTEM ' ] != system :
if _bridge not in _bridgestemp :
_bridgestemp [ _bridge ] = [ ]
_bridgestemp [ _bridge ] . append ( _bridgesystem )
BRIDGES . update ( _bridgestemp )
2020-08-19 15:59:20 -04:00
# Run this every minute for rule timer updates
def rule_timer_loop ( ) :
logger . debug ( ' (ROUTER) routerHBP Rule timer loop started ' )
_now = time ( )
2020-10-24 18:35:54 -04:00
_remove_bridges = [ ]
2020-08-19 15:59:20 -04:00
for _bridge in BRIDGES :
2020-10-24 18:35:54 -04:00
_bridge_used = False
2020-08-19 15:59:20 -04:00
for _system in BRIDGES [ _bridge ] :
if _system [ ' TO_TYPE ' ] == ' ON ' :
if _system [ ' ACTIVE ' ] == True :
2020-10-24 18:43:08 -04:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
if _system [ ' TIMER ' ] < _now :
_system [ ' ACTIVE ' ] = False
logger . info ( ' (ROUTER) Conference Bridge TIMEOUT: DEACTIVATE System: %s , Bridge: %s , TS: %s , TGID: %s ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) )
2020-10-03 20:40:44 -04:00
if _bridge [ 0 : 1 ] == ' # ' :
2020-10-04 10:19:46 -04:00
reactor . callInThread ( disconnectedVoice , _system [ ' SYSTEM ' ] )
2020-08-19 15:59:20 -04:00
else :
timeout_in = _system [ ' TIMER ' ] - _now
2020-10-25 19:40:54 -04:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
logger . info ( ' (ROUTER) Conference Bridge ACTIVE (ON timer running): System: %s Bridge: %s , TS: %s , TGID: %s , Timeout in: %.2f s, ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) , timeout_in )
elif _system [ ' ACTIVE ' ] == False :
logger . debug ( ' (ROUTER) Conference Bridge INACTIVE (no change): System: %s Bridge: %s , TS: %s , TGID: %s ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) )
elif _system [ ' TO_TYPE ' ] == ' OFF ' :
if _system [ ' ACTIVE ' ] == False :
if _system [ ' TIMER ' ] < _now :
_system [ ' ACTIVE ' ] = True
2020-11-08 15:12:53 -05:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
logger . info ( ' (ROUTER) Conference Bridge TIMEOUT: ACTIVATE System: %s , Bridge: %s , TS: %s , TGID: %s ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) )
else :
timeout_in = _system [ ' TIMER ' ] - _now
2020-10-24 18:35:54 -04:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
logger . info ( ' (ROUTER) Conference Bridge INACTIVE (OFF timer running): System: %s Bridge: %s , TS: %s , TGID: %s , Timeout in: %.2f s, ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) , timeout_in )
elif _system [ ' ACTIVE ' ] == True :
2020-10-24 18:50:31 -04:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
logger . debug ( ' (ROUTER) Conference Bridge ACTIVE (no change): System: %s Bridge: %s , TS: %s , TGID: %s ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) )
else :
2020-11-28 10:56:02 -05:00
if _system [ ' SYSTEM ' ] [ 0 : 3 ] != ' OBP ' :
2020-10-24 18:38:42 -04:00
_bridge_used = True
2020-12-03 15:28:23 -05:00
elif _system [ ' SYSTEM ' ] [ 0 : 3 ] == ' OBP ' and _system [ ' TO_TYPE ' ] == ' STAT ' :
2020-11-30 17:09:51 -05:00
_bridge_used = True
2020-08-19 15:59:20 -04:00
logger . debug ( ' (ROUTER) Conference Bridge NO ACTION: System: %s , Bridge: %s , TS: %s , TGID: %s ' , _system [ ' SYSTEM ' ] , _bridge , _system [ ' TS ' ] , int_id ( _system [ ' TGID ' ] ) )
2020-10-24 18:35:54 -04:00
if _bridge_used == False :
_remove_bridges . append ( _bridge )
for _bridgerem in _remove_bridges :
del BRIDGES [ _bridgerem ]
logger . debug ( ' (ROUTER) Unused conference bridge %s removed ' , _bridgerem )
2020-08-19 15:59:20 -04:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
report_server . send_clients ( b ' bridge updated ' )
2021-01-10 08:50:37 -05:00
def statTrimmer ( ) :
logger . debug ( ' (ROUTER) STAT trimmer loop started ' )
_remove_bridges = [ ]
for _bridge in BRIDGES :
_bridge_stat = False
2021-01-16 08:03:20 -05:00
_in_use = False
2021-01-10 08:50:37 -05:00
for _system in BRIDGES [ _bridge ] :
if _system [ ' TO_TYPE ' ] == ' STAT ' :
_bridge_stat = True
2021-01-16 08:03:20 -05:00
if _system [ ' TO_TYPE ' ] == ' ON ' and _system [ ' ACTIVE ' ] :
_in_use = True
elif _system [ ' TO_TYPE ' ] == ' OFF ' and not _system [ ' ACTIVE ' ] :
_in_use = True
if _bridge_stat and not _in_use :
2021-01-16 07:05:09 -05:00
_remove_bridges . append ( _bridge )
2021-01-10 08:50:37 -05:00
for _bridgerem in _remove_bridges :
del BRIDGES [ _bridgerem ]
logger . debug ( ' (ROUTER) STAT bridge %s removed ' , _bridgerem )
2021-01-10 09:03:45 -05:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
report_server . send_clients ( b ' bridge updated ' )
2021-01-10 08:50:37 -05:00
2021-04-20 20:37:07 -04:00
def kaReporting ( ) :
logger . debug ( ' (ROUTER) KeepAlive reporting loop started ' )
2021-05-01 20:44:16 -04:00
for system in systems :
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] == ' OPENBRIDGE ' :
2021-05-03 20:41:35 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENHANCED_OBP ' ] :
if ' _bcka ' not in CONFIG [ ' SYSTEMS ' ] [ system ] :
logger . warning ( ' (ROUTER) not sending to system %s as KeepAlive never seen ' , system )
elif CONFIG [ ' SYSTEMS ' ] [ system ] [ ' _bcka ' ] < time ( ) - 60 :
logger . warning ( ' (ROUTER) not sending to system %s as last KeepAlive was %s seconds ago ' , system , int ( time ( ) - CONFIG [ ' SYSTEMS ' ] [ system ] [ ' _bcka ' ] ) )
2020-08-19 15:59:20 -04:00
2021-05-02 16:04:25 -04:00
# run this every 10 seconds to trim stream ids
2020-08-19 15:59:20 -04:00
def stream_trimmer_loop ( ) :
logger . debug ( ' (ROUTER) Trimming inactive stream IDs from system lists ' )
_now = time ( )
for system in systems :
# HBP systems, master and peer
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' OPENBRIDGE ' :
for slot in range ( 1 , 3 ) :
_slot = systems [ system ] . STATUS [ slot ]
# RX slot check
2021-04-12 18:28:01 -04:00
if _slot [ ' RX_TYPE ' ] != HBPF_SLT_VTERM and _slot [ ' RX_TIME ' ] < _now - 5 :
2020-08-19 15:59:20 -04:00
_slot [ ' RX_TYPE ' ] = HBPF_SLT_VTERM
logger . info ( ' ( %s ) *TIME OUT* RX STREAM ID: %s SUB: %s TGID %s , TS %s , Duration: %.2f ' , \
system , int_id ( _slot [ ' RX_STREAM_ID ' ] ) , int_id ( _slot [ ' RX_RFS ' ] ) , int_id ( _slot [ ' RX_TGID ' ] ) , slot , _slot [ ' RX_TIME ' ] - _slot [ ' RX_START ' ] )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ system ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,RX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( system , int_id ( _slot [ ' RX_STREAM_ID ' ] ) , int_id ( _slot [ ' RX_PEER ' ] ) , int_id ( _slot [ ' RX_RFS ' ] ) , slot , int_id ( _slot [ ' RX_TGID ' ] ) , _slot [ ' RX_TIME ' ] - _slot [ ' RX_START ' ] ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2021-04-12 18:28:01 -04:00
#Null stream_id - for loop control
if _slot [ ' RX_TIME ' ] < _now - 60 :
2021-04-03 13:52:16 -04:00
_slot [ ' RX_STREAM_ID ' ] = b ' \x00 '
2020-08-19 15:59:20 -04:00
# TX slot check
2021-04-12 18:28:01 -04:00
if _slot [ ' TX_TYPE ' ] != HBPF_SLT_VTERM and _slot [ ' TX_TIME ' ] < _now - 5 :
2020-08-19 15:59:20 -04:00
_slot [ ' TX_TYPE ' ] = HBPF_SLT_VTERM
logger . info ( ' ( %s ) *TIME OUT* TX STREAM ID: %s SUB: %s TGID %s , TS %s , Duration: %.2f ' , \
system , int_id ( _slot [ ' TX_STREAM_ID ' ] ) , int_id ( _slot [ ' TX_RFS ' ] ) , int_id ( _slot [ ' TX_TGID ' ] ) , slot , _slot [ ' TX_TIME ' ] - _slot [ ' TX_START ' ] )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ system ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,TX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( system , int_id ( _slot [ ' TX_STREAM_ID ' ] ) , int_id ( _slot [ ' TX_PEER ' ] ) , int_id ( _slot [ ' TX_RFS ' ] ) , slot , int_id ( _slot [ ' TX_TGID ' ] ) , _slot [ ' TX_TIME ' ] - _slot [ ' TX_START ' ] ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# OBP systems
# We can't delete items from a dicationry that's being iterated, so we have to make a temporarly list of entrys to remove later
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] == ' OPENBRIDGE ' :
remove_list = [ ]
2021-03-26 06:45:28 -04:00
fin_list = [ ]
2020-08-19 15:59:20 -04:00
for stream_id in systems [ system ] . STATUS :
2021-03-25 18:51:41 -04:00
#if stream already marked as finished, just remove it
2021-04-14 06:42:31 -04:00
if ' _fin ' in systems [ system ] . STATUS [ stream_id ] and systems [ system ] . STATUS [ stream_id ] [ ' LAST ' ] < _now - 180 :
2021-03-26 07:03:26 -04:00
logger . info ( ' ( %s ) *FINISHED STREAM* STREAM ID: %s ' , system , int_id ( stream_id ) )
2021-03-25 19:02:17 -04:00
fin_list . append ( stream_id )
2021-03-26 06:58:20 -04:00
continue
2021-03-25 18:51:41 -04:00
2021-04-13 11:51:12 -04:00
#try:
if ' _to ' not in systems [ system ] . STATUS [ stream_id ] and ' _fin ' not in systems [ system ] . STATUS [ stream_id ] and systems [ system ] . STATUS [ stream_id ] [ ' LAST ' ] < _now - 5 :
2021-04-13 11:55:45 -04:00
_stream = systems [ system ] . STATUS [ stream_id ]
_sysconfig = CONFIG [ ' SYSTEMS ' ] [ system ]
2021-04-13 11:54:20 -04:00
#systems[system].STATUS[stream_id]['_fin'] = True
2021-07-23 12:01:16 -04:00
if ' _bcsq ' in _sysconfig and _stream [ ' TGID ' ] in _sysconfig [ ' _bcsq ' ] and _sysconfig [ ' _bcsq ' ] [ _stream [ ' TGID ' ] ] == stream_id :
logger . debug ( ' ( %s ) *TIME OUT* STREAM ID: %s SUB: %s PEER: %s TGID: %s TS 1 (BCSQ) ' , \
system , int_id ( stream_id ) , get_alias ( int_id ( _stream [ ' RFS ' ] ) , subscriber_ids ) , get_alias ( int_id ( _stream [ ' RX_PEER ' ] ) , peer_ids ) , get_alias ( int_id ( _stream [ ' TGID ' ] ) , talkgroup_ids ) )
elif ' _bcsq ' in systems [ system ] . STATUS [ stream_id ] :
logger . debug ( ' ( %s ) *TIME OUT* STREAM ID: %s SUB: %s PEER: %s TGID: %s TS 1 (BCSQ) ' , \
system , int_id ( stream_id ) , get_alias ( int_id ( _stream [ ' RFS ' ] ) , subscriber_ids ) , get_alias ( int_id ( _stream [ ' RX_PEER ' ] ) , peer_ids ) , get_alias ( int_id ( _stream [ ' TGID ' ] ) , talkgroup_ids ) )
else :
logger . info ( ' ( %s ) *TIME OUT* STREAM ID: %s SUB: %s PEER: %s TGID: %s TS 1 Duration: %.2f ' , \
system , int_id ( stream_id ) , get_alias ( int_id ( _stream [ ' RFS ' ] ) , subscriber_ids ) , get_alias ( int_id ( _stream [ ' RX_PEER ' ] ) , peer_ids ) , get_alias ( int_id ( _stream [ ' TGID ' ] ) , talkgroup_ids ) , _stream [ ' LAST ' ] - _stream [ ' START ' ] )
2021-04-13 11:51:12 -04:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
2021-06-14 19:24:49 -04:00
systems [ system ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,RX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( system , int_id ( stream_id ) , int_id ( _stream [ ' RX_PEER ' ] ) , int_id ( _stream [ ' RFS ' ] ) , 1 , int_id ( _stream [ ' TGID ' ] ) , _stream [ ' LAST ' ] - _stream [ ' START ' ] ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2021-04-13 11:51:12 -04:00
systems [ system ] . STATUS [ stream_id ] [ ' _to ' ] = True
2021-04-13 11:46:56 -04:00
continue
2021-04-13 11:51:12 -04:00
#except:
#logger.warning("(%s) Keyerror - stream trimmer Stream ID: %s",system,stream_id)
#systems[system].STATUS[stream_id]['LAST'] = _now
#continue
2021-04-13 11:46:56 -04:00
2021-01-17 17:45:45 -05:00
try :
2021-04-14 06:42:31 -04:00
if systems [ system ] . STATUS [ stream_id ] [ ' LAST ' ] < _now - 180 :
2021-01-17 17:45:45 -05:00
remove_list . append ( stream_id )
2021-05-01 18:36:35 -04:00
except Exception as e :
logger . exception ( " ( %s ) Keyerror - stream trimmer Stream ID: %s " , system , stream_id , exc_info = e )
2021-01-18 06:09:05 -05:00
systems [ system ] . STATUS [ stream_id ] [ ' LAST ' ] = _now
2021-01-17 17:45:45 -05:00
continue
2021-01-18 06:09:05 -05:00
2021-03-25 19:02:17 -04:00
#remove finished
for stream_id in fin_list :
removed = systems [ system ] . STATUS . pop ( stream_id )
2020-08-19 15:59:20 -04:00
for stream_id in remove_list :
if stream_id in systems [ system ] . STATUS :
_stream = systems [ system ] . STATUS [ stream_id ]
_sysconfig = CONFIG [ ' SYSTEMS ' ] [ system ]
2021-04-13 11:46:56 -04:00
2020-08-19 15:59:20 -04:00
removed = systems [ system ] . STATUS . pop ( stream_id )
2021-04-12 18:28:01 -04:00
try :
2021-04-13 06:25:35 -04:00
_bcsq_remove = [ ]
2021-04-12 18:28:01 -04:00
for tgid in _sysconfig [ ' _bcsq ' ] :
2021-04-12 19:05:23 -04:00
if _sysconfig [ ' _bcsq ' ] [ tgid ] == stream_id :
2021-04-13 06:57:30 -04:00
_bcsq_remove . append ( tgid )
2021-04-13 06:25:35 -04:00
for bcrm in _bcsq_remove :
removed = _sysconfig [ ' _bcsq ' ] . pop ( bcrm )
2021-04-12 18:28:01 -04:00
except KeyError :
pass
2020-08-19 15:59:20 -04:00
else :
2021-05-01 18:36:35 -04:00
logger . debug ( ' ( %s ) Attemped to remove OpenBridge Stream ID %s not in the Stream ID list: %s ' , system , int_id ( stream_id ) , [ id for id in systems [ system ] . STATUS ] )
2020-08-19 15:59:20 -04:00
2021-01-27 20:27:10 -05:00
def sendVoicePacket ( self , pkt , _source_id , _dest_id , _slot ) :
_stream_id = pkt [ 16 : 20 ]
_pkt_time = time ( )
if _stream_id not in systems [ system ] . STATUS :
systems [ system ] . STATUS [ _stream_id ] = {
' START ' : _pkt_time ,
' CONTENTION ' : False ,
' RFS ' : _source_id ,
' TGID ' : _dest_id ,
' LAST ' : _pkt_time
}
_slot [ ' TX_TGID ' ] = _dest_id
else :
systems [ system ] . STATUS [ _stream_id ] [ ' LAST ' ] = _pkt_time
_slot [ ' TX_TIME ' ] = _pkt_time
self . send_system ( pkt )
2020-10-04 10:19:46 -04:00
def sendSpeech ( self , speech ) :
2021-03-21 13:39:31 -04:00
logger . debug ( ' ( %s ) Inside sendspeech thread ' , self . _system )
2020-10-04 10:19:46 -04:00
sleep ( 1 )
2020-10-12 11:04:01 -04:00
_nine = bytes_3 ( 9 )
2021-01-19 14:42:46 -05:00
_source_id = bytes_3 ( 5000 )
2020-10-12 11:30:32 -04:00
_slot = systems [ system ] . STATUS [ 2 ]
2020-10-04 10:19:46 -04:00
while True :
try :
pkt = next ( speech )
except StopIteration :
break
#Packet every 60ms
sleep ( 0.058 )
2021-01-27 20:27:10 -05:00
reactor . callFromThread ( sendVoicePacket , self , pkt , _source_id , _nine , _slot )
2021-03-21 13:39:31 -04:00
logger . debug ( ' ( %s ) Sendspeech thread ended ' , self . _system )
2020-10-04 10:19:46 -04:00
2020-10-03 20:40:44 -04:00
def disconnectedVoice ( system ) :
2020-10-12 11:30:32 -04:00
_nine = bytes_3 ( 9 )
2021-01-19 14:42:46 -05:00
_source_id = bytes_3 ( 5000 )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_lang = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ]
2021-03-21 13:39:31 -04:00
logger . debug ( ' ( %s ) Sending disconnected voice ' , system )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say = [ words [ _lang ] [ ' silence ' ] ]
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-10-03 20:40:44 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' linkedto ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' to ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-10-04 11:05:13 -04:00
for number in str ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] ) :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ number ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-10-03 20:40:44 -04:00
else :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' notlinked ' ] )
2020-10-03 20:40:44 -04:00
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-11-07 12:03:40 -05:00
2021-01-19 14:42:46 -05:00
speech = pkt_gen ( _source_id , _nine , bytes_4 ( 9 ) , 1 , _say )
2020-10-03 20:40:44 -04:00
sleep ( 1 )
2020-10-12 11:30:32 -04:00
_slot = systems [ system ] . STATUS [ 2 ]
2020-10-03 20:40:44 -04:00
while True :
try :
pkt = next ( speech )
except StopIteration :
break
#Packet every 60ms
sleep ( 0.058 )
2020-10-12 11:04:01 -04:00
_stream_id = pkt [ 16 : 20 ]
_pkt_time = time ( )
2021-08-22 19:10:19 -04:00
reactor . callFromThread ( sendVoicePacket , systems [ system ] , pkt , _source_id , _nine , _slot )
2021-03-21 13:39:31 -04:00
logger . debug ( ' ( %s ) disconnected voice thread end ' , system )
2021-05-14 17:26:50 -04:00
2021-05-24 17:32:54 -04:00
def playFileOnRequest ( self , fileNumber ) :
system = self . _system
_lang = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ]
_nine = bytes_3 ( 9 )
_source_id = bytes_3 ( 5000 )
logger . debug ( ' ( %s ) Sending contents of AMBE file: %s ' , system , fileNumber )
sleep ( 1 )
_say = [ ]
try :
_say . append ( AMBEobj . readSingleFile ( ' / ' + _lang + ' / ' + str ( fileNumber ) + ' .ambe ' ) )
except IOError :
logger . warning ( ' ( %s ) cannot read file for number %s ' , system , fileNumber )
return
speech = pkt_gen ( _source_id , _nine , bytes_4 ( 9 ) , 1 , _say )
sleep ( 1 )
_slot = systems [ system ] . STATUS [ 2 ]
while True :
try :
pkt = next ( speech )
except StopIteration :
break
2021-05-14 17:26:50 -04:00
#Packet every 60ms
2021-05-24 17:32:54 -04:00
sleep ( 0.058 )
_stream_id = pkt [ 16 : 20 ]
_pkt_time = time ( )
reactor . callFromThread ( sendVoicePacket , self , pkt , _source_id , _nine , _slot )
logger . debug ( ' ( %s ) Sending AMBE file %s end ' , system , fileNumber )
2021-05-14 17:26:50 -04:00
2020-10-03 20:40:44 -04:00
2020-10-02 15:47:04 -04:00
def threadIdent ( ) :
logger . debug ( ' (IDENT) starting ident thread ' )
reactor . callInThread ( ident )
def threadedMysql ( ) :
logger . debug ( ' (MYSQL) Starting MySQL thread ' )
2021-01-27 13:47:55 -05:00
reactor . callInThread ( mysqlGetConfig )
2020-10-02 15:47:04 -04:00
2020-09-22 15:06:07 -04:00
def ident ( ) :
for system in systems :
2020-10-02 15:47:04 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' MASTER ' :
2020-09-23 05:50:33 -04:00
continue
2020-09-22 15:06:07 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' VOICE_IDENT ' ] == True :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_lang = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ]
2021-02-26 14:13:58 -05:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MAX_PEERS ' ] > 1 :
2021-03-21 13:39:31 -04:00
logger . debug ( " (IDENT) %s System has MAX_PEERS > 1, skipping " , system )
2021-02-26 14:13:58 -05:00
continue
_callsign = False
for _peerid in CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PEERS ' ] :
2021-09-12 13:02:40 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PEERS ' ] [ _peerid ] [ ' CALLSIGN ' ] :
_callsign = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PEERS ' ] [ _peerid ] [ ' CALLSIGN ' ] . decode ( )
2021-02-26 14:13:58 -05:00
if not _callsign :
2021-03-21 13:26:22 -04:00
logger . debug ( " (IDENT) %s System has no peers or no recorded callsign ( %s ), skipping " , system , _callsign )
2021-02-26 14:13:58 -05:00
continue
2020-09-22 15:06:07 -04:00
_slot = systems [ system ] . STATUS [ 2 ]
#If slot is idle for RX and TX
2020-10-19 17:41:44 -04:00
#print("RX:"+str(_slot['RX_TYPE'])+" TX:"+str(_slot['TX_TYPE'])+" TIME:"+str(time() - _slot['TX_TIME']))
if ( _slot [ ' RX_TYPE ' ] == HBPF_SLT_VTERM ) and ( _slot [ ' TX_TYPE ' ] == HBPF_SLT_VTERM ) and ( time ( ) - _slot [ ' TX_TIME ' ] > CONFIG [ ' SYSTEMS ' ] [ system ] [ ' GROUP_HANGTIME ' ] ) :
2020-09-22 15:06:07 -04:00
#_stream_id = hex_str_4(1234567)
2021-02-28 07:05:03 -05:00
logger . info ( ' ( %s ) System idle. Sending voice ident ' , system )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say = [ words [ _lang ] [ ' silence ' ] ]
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' this-is ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2021-05-09 11:53:38 -04:00
2021-02-26 14:13:58 -05:00
_systemcs = re . sub ( r ' \ W+ ' , ' ' , _callsign )
2020-09-22 15:06:07 -04:00
_systemcs . upper ( )
for character in _systemcs :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ character ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2021-05-09 11:53:38 -04:00
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' freedmr ' ] )
2021-05-09 11:08:54 -04:00
2021-05-09 09:42:42 -04:00
#test
2021-05-09 09:36:02 -04:00
#_say.append(AMBEobj.readSingleFile('alpha.ambe'))
2020-10-12 11:04:01 -04:00
_all_call = bytes_3 ( 16777215 )
2021-01-19 14:42:46 -05:00
_source_id = bytes_3 ( 5000 )
speech = pkt_gen ( _source_id , _all_call , bytes_4 ( 16777215 ) , 1 , _say )
2020-09-22 15:06:07 -04:00
sleep ( 1 )
2020-10-12 11:30:32 -04:00
_slot = systems [ system ] . STATUS [ 2 ]
2020-09-22 15:06:07 -04:00
while True :
try :
pkt = next ( speech )
except StopIteration :
break
#Packet every 60ms
sleep ( 0.058 )
2020-10-12 11:04:01 -04:00
_stream_id = pkt [ 16 : 20 ]
_pkt_time = time ( )
2021-02-20 16:18:08 -05:00
reactor . callFromThread ( sendVoicePacket , systems [ system ] , pkt , _source_id , _all_call , _slot )
2020-09-30 15:46:35 -04:00
2020-12-27 12:32:38 -05:00
def options_config ( ) :
logger . debug ( ' (OPTIONS) Running options parser ' )
for _system in CONFIG [ ' SYSTEMS ' ] :
2021-04-29 11:28:57 -04:00
try :
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' MODE ' ] != ' MASTER ' :
continue
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' ENABLED ' ] == True :
if ' OPTIONS ' in CONFIG [ ' SYSTEMS ' ] [ _system ] :
_options = { }
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] . rstrip ( ' \x00 ' )
2021-06-29 19:55:21 -04:00
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] . encode ( ' ascii ' , ' ignore ' ) . decode ( )
2021-05-29 13:48:34 -04:00
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] = re . sub ( " \' " , " " , CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] )
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] = re . sub ( " \" " , " " , CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] )
2021-04-29 11:28:57 -04:00
for x in CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' OPTIONS ' ] . split ( " ; " ) :
try :
k , v = x . split ( ' = ' )
except ValueError :
logger . debug ( ' (OPTIONS) Value error %s ignoring ' , _system )
continue
_options [ k ] = v
logger . debug ( ' (OPTIONS) Options found for %s ' , _system )
2021-03-07 11:11:30 -05:00
2021-04-29 11:28:57 -04:00
if ' DIAL ' in _options :
_options [ ' DEFAULT_REFLECTOR ' ] = _options . pop ( ' DIAL ' )
if ' TIMER ' in _options :
_options [ ' DEFAULT_UA_TIMER ' ] = _options . pop ( ' TIMER ' )
if ' TS1 ' in _options :
_options [ ' TS1_STATIC ' ] = _options . pop ( ' TS1 ' )
if ' TS2 ' in _options :
_options [ ' TS2_STATIC ' ] = _options . pop ( ' TS2 ' )
#DMR+ style options
if ' StartRef ' in _options :
_options [ ' DEFAULT_REFLECTOR ' ] = _options . pop ( ' StartRef ' )
if ' RelinkTime ' in _options :
_options [ ' DEFAULT_UA_TIMER ' ] = _options . pop ( ' RelinkTime ' )
if ' TS1_1 ' in _options :
_options [ ' TS1_STATIC ' ] = _options . pop ( ' TS1_1 ' )
if ' TS1_2 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_2 ' )
if ' TS1_3 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_3 ' )
if ' TS1_4 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_4 ' )
2021-06-29 20:19:57 -04:00
if ' TS1_5 ' in _options :
2021-04-29 11:28:57 -04:00
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_5 ' )
2021-06-29 20:19:57 -04:00
if ' TS1_6 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_6 ' )
if ' TS1_7 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_7 ' )
if ' TS1_8 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_8 ' )
if ' TS1_9 ' in _options :
_options [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ] + ' , ' + _options . pop ( ' TS1_9 ' )
2021-03-07 11:11:30 -05:00
if ' TS2_2 ' in _options :
2021-04-29 11:28:57 -04:00
_options [ ' TS2_STATIC ' ] = _options . pop ( ' TS2_1 ' )
if ' TS2_2 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_2 ' )
if ' TS2_3 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_3 ' )
if ' TS2_4 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_4 ' )
2021-06-29 20:19:57 -04:00
if ' TS2_5 ' in _options :
2021-04-29 11:28:57 -04:00
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_5 ' )
2021-06-29 20:19:57 -04:00
if ' TS2_6 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_6 ' )
if ' TS2_7 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_7 ' )
if ' TS2_8 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_8 ' )
if ' TS2_9 ' in _options :
_options [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ] + ' , ' + _options . pop ( ' TS2_9 ' )
2021-04-29 11:28:57 -04:00
if ' UserLink ' in _options :
_options . pop ( ' UserLink ' )
2021-03-07 11:11:30 -05:00
2021-04-29 11:28:57 -04:00
if ' TS1_STATIC ' not in _options :
_options [ ' TS1_STATIC ' ] = False
2021-03-07 11:11:30 -05:00
2021-04-29 11:28:57 -04:00
if ' TS2_STATIC ' not in _options :
_options [ ' TS2_STATIC ' ] = False
if ' DEFAULT_REFLECTOR ' not in _options :
_options [ ' DEFAULT_REFLECTOR ' ] = 0
if ' DEFAULT_UA_TIMER ' not in _options :
_options [ ' DEFAULT_UA_TIMER ' ] = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_UA_TIMER ' ]
2021-03-13 06:35:12 -05:00
2021-04-29 11:28:57 -04:00
if ' VOICE ' in _options and ( CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' VOICE_IDENT ' ] != bool ( int ( _options [ ' VOICE ' ] ) ) ) :
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' VOICE_IDENT ' ] = bool ( int ( _options [ ' VOICE ' ] ) )
logger . debug ( " (OPTIONS) %s - Setting voice ident to %s " , _system , CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' VOICE_IDENT ' ] )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
if ' LANG ' in _options and _options [ ' LANG ' ] in words and _options [ ' LANG ' ] != CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] :
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] = _options [ ' LANG ' ]
logger . debug ( " (OPTIONS) %s - Setting voice language to %s " , _system , CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] )
2021-04-29 11:28:57 -04:00
if ' SINGLE ' in _options and ( CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' SINGLE_MODE ' ] != bool ( int ( _options [ ' SINGLE ' ] ) ) ) :
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' SINGLE_MODE ' ] = bool ( int ( _options [ ' SINGLE ' ] ) )
logger . debug ( " (OPTIONS) %s - Setting SINGLE_MODE to %s " , _system , CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' SINGLE_MODE ' ] )
2020-12-27 12:32:38 -05:00
2021-04-29 11:28:57 -04:00
if ' TS1_STATIC ' not in _options or ' TS2_STATIC ' not in _options or ' DEFAULT_REFLECTOR ' not in _options or ' DEFAULT_UA_TIMER ' not in _options :
logger . debug ( ' (OPTIONS) %s - Required field missing, ignoring ' , _system )
2020-12-27 12:32:38 -05:00
continue
2021-04-29 11:28:57 -04:00
if _options [ ' TS1_STATIC ' ] == ' ' :
_options [ ' TS1_STATIC ' ] = False
if _options [ ' TS2_STATIC ' ] == ' ' :
_options [ ' TS2_STATIC ' ] = False
if _options [ ' TS1_STATIC ' ] :
re . sub ( " \ s " , " " , _options [ ' TS1_STATIC ' ] )
if re . search ( " ![ \ d \ ,] " , _options [ ' TS1_STATIC ' ] ) :
logger . debug ( ' (OPTIONS) %s - TS1_STATIC contains characters other than numbers and comma, ignoring ' , _system )
continue
if _options [ ' TS2_STATIC ' ] :
re . sub ( " \ s " , " " , _options [ ' TS2_STATIC ' ] )
if re . search ( " ![ \ d \ ,] " , _options [ ' TS2_STATIC ' ] ) :
logger . debug ( ' (OPTIONS) %s - TS2_STATIC contains characters other than numbers and comma, ignoring ' , _system )
continue
if isinstance ( _options [ ' DEFAULT_REFLECTOR ' ] , str ) and not _options [ ' DEFAULT_REFLECTOR ' ] . isdigit ( ) :
logger . debug ( ' (OPTIONS) %s - DEFAULT_UA_TIMER is not an integer, ignoring ' , _system )
2020-12-27 12:32:38 -05:00
continue
2021-02-14 08:13:12 -05:00
2021-04-29 11:28:57 -04:00
if isinstance ( _options [ ' DEFAULT_UA_TIMER ' ] , str ) and not _options [ ' DEFAULT_UA_TIMER ' ] . isdigit ( ) :
logger . debug ( ' (OPTIONS) %s - DEFAULT_REFLECTOR is not an integer, ignoring ' , _system )
continue
2020-12-27 12:32:38 -05:00
_tmout = int ( _options [ ' DEFAULT_UA_TIMER ' ] )
2021-04-29 11:28:57 -04:00
if int ( _options [ ' DEFAULT_UA_TIMER ' ] ) != CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_UA_TIMER ' ] :
logger . debug ( ' (OPTIONS) %s Updating DEFAULT_UA_TIMER for existing bridges. ' , _system )
remove_bridge_system ( _system )
for _bridge in BRIDGES :
ts1 = False
ts2 = False
for i , e in enumerate ( BRIDGES [ _bridge ] ) :
if e [ ' SYSTEM ' ] == _system and e [ ' TS ' ] == 1 :
ts1 = True
if e [ ' SYSTEM ' ] == _system and e [ ' TS ' ] == 2 :
ts2 = True
if _bridge [ 0 : 1 ] != ' # ' :
if ts1 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 1 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
else :
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : _system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ bytes_3 ( 4000 ) ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if int ( _options [ ' DEFAULT_REFLECTOR ' ] ) != CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_REFLECTOR ' ] :
if int ( _options [ ' DEFAULT_REFLECTOR ' ] ) > 0 :
logger . debug ( ' (OPTIONS) %s default reflector changed, updating ' , _system )
reset_default_reflector ( CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , _system )
make_default_reflector ( int ( _options [ ' DEFAULT_REFLECTOR ' ] ) , _tmout , _system )
else :
logger . debug ( ' (OPTIONS) %s default reflector disabled, updating ' , _system )
reset_default_reflector ( int ( _options [ ' DEFAULT_REFLECTOR ' ] ) , _tmout , _system )
2020-12-27 12:32:38 -05:00
2021-04-29 11:28:57 -04:00
if _options [ ' TS1_STATIC ' ] != CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS1_STATIC ' ] :
_tmout = int ( _options [ ' DEFAULT_UA_TIMER ' ] )
logger . debug ( ' (OPTIONS) %s TS1 static TGs changed, updating ' , _system )
ts1 = [ ]
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS1_STATIC ' ] :
ts1 = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
reset_static_tg ( tg , 1 , _tmout , _system )
ts1 = [ ]
if _options [ ' TS1_STATIC ' ] :
ts1 = _options [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 1 , _tmout , _system )
if _options [ ' TS2_STATIC ' ] != CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS2_STATIC ' ] :
_tmout = int ( _options [ ' DEFAULT_UA_TIMER ' ] )
logger . debug ( ' (OPTIONS) %s TS2 static TGs changed, updating ' , _system )
ts2 = [ ]
if CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS2_STATIC ' ] :
ts2 = CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
reset_static_tg ( tg , 2 , _tmout , _system )
ts2 = [ ]
if _options [ ' TS2_STATIC ' ] :
ts2 = _options [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 2 , _tmout , _system )
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS1_STATIC ' ] = _options [ ' TS1_STATIC ' ]
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' TS2_STATIC ' ] = _options [ ' TS2_STATIC ' ]
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_REFLECTOR ' ] = int ( _options [ ' DEFAULT_REFLECTOR ' ] )
CONFIG [ ' SYSTEMS ' ] [ _system ] [ ' DEFAULT_UA_TIMER ' ] = int ( _options [ ' DEFAULT_UA_TIMER ' ] )
2021-05-06 16:33:33 -04:00
except Exception :
2021-06-29 19:55:21 -04:00
logger . exception ( ' (OPTIONS) caught exception: ' )
2021-04-29 11:30:45 -04:00
continue
2020-12-27 12:32:38 -05:00
2021-01-27 13:47:55 -05:00
def mysqlGetConfig ( ) :
2020-09-30 15:46:35 -04:00
logger . debug ( ' (MYSQL) Periodic config check ' )
2020-12-27 12:32:38 -05:00
SQLGETCONFIG = { }
2020-09-30 15:46:35 -04:00
if sql . con ( ) :
logger . debug ( ' (MYSQL) reading config from database ' )
try :
2020-12-27 12:32:38 -05:00
SQLGETCONFIG = sql . getConfig ( )
2020-09-30 15:46:35 -04:00
except :
logger . debug ( ' (MYSQL) problem with SQL query, aborting ' )
sql . close ( )
2021-01-27 16:30:37 -05:00
return
2020-09-30 15:46:35 -04:00
else :
logger . debug ( ' (MYSQL) problem connecting to SQL server, aborting ' )
2021-01-27 16:30:37 -05:00
sql . close ( )
2020-09-30 15:56:07 -04:00
return
2020-09-30 15:46:35 -04:00
2021-01-27 14:49:02 -05:00
sql . close ( )
2021-01-27 13:47:55 -05:00
reactor . callFromThread ( mysql_config_check , SQLGETCONFIG )
2020-12-27 12:32:38 -05:00
2021-01-27 13:47:55 -05:00
def mysql_config_check ( SQLGETCONFIG ) :
SQLCONFIG = SQLGETCONFIG
2020-12-27 12:32:38 -05:00
for system in SQLGETCONFIG :
2020-09-30 19:22:53 -04:00
if system not in CONFIG [ ' SYSTEMS ' ] :
2021-01-16 10:54:52 -05:00
if SQLCONFIG [ system ] [ ' ENABLED ' ] :
2020-09-30 19:22:53 -04:00
logger . debug ( ' (MYSQL) new enabled system %s , starting HBP listener ' , system )
CONFIG [ ' SYSTEMS ' ] [ system ] = SQLCONFIG [ system ]
systems [ system ] = routerHBP ( system , CONFIG , report_server )
listeningPorts [ system ] = reactor . listenUDP ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PORT ' ] , systems [ system ] , interface = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' IP ' ] )
else :
logger . debug ( ' (MYSQL) new disabled system %s ' , system )
2020-10-06 17:20:57 -04:00
_tmout = SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ]
2020-09-30 15:46:35 -04:00
#Do ACL processing
2020-09-30 19:22:53 -04:00
# Subscriber and TGID ACLs
2020-09-30 15:56:07 -04:00
logger . debug ( ' (MYSQL) building ACLs ' )
2020-10-03 16:51:22 -04:00
# Registration ACLs
SQLCONFIG [ system ] [ ' REG_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' REG_ACL ' ] , PEER_MAX )
2020-09-30 15:46:35 -04:00
for acl in [ ' SUB_ACL ' , ' TG1_ACL ' , ' TG2_ACL ' ] :
SQLCONFIG [ system ] [ acl ] = acl_build ( SQLCONFIG [ system ] [ acl ] , ID_MAX )
2020-10-18 08:41:38 -04:00
2020-09-30 15:46:35 -04:00
#Add system to bridges
2021-01-16 10:54:52 -05:00
if SQLCONFIG [ system ] [ ' ENABLED ' ] :
2020-10-18 08:41:38 -04:00
logger . debug ( ' (MYSQL) adding new system to static bridges ' )
for _bridge in BRIDGES :
ts1 = False
ts2 = False
for i , e in enumerate ( BRIDGES [ _bridge ] ) :
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 1 :
ts1 = True
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 2 :
ts2 = True
if _bridge [ 0 : 1 ] != ' # ' :
if ts1 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 1 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
else :
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ bytes_3 ( 4000 ) ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
logger . debug ( ' (MYSQL) %s setting default reflector ' , system )
make_default_reflector ( SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
if SQLCONFIG [ system ] [ ' TS1_STATIC ' ] :
logger . debug ( ' (MYSQL) %s setting static TGs on TS1 ' , system )
ts1 = SQLCONFIG [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 1 , _tmout , system )
if SQLCONFIG [ system ] [ ' TS2_STATIC ' ] :
logger . debug ( ' (MYSQL) %s setting static TGs on TS2 ' , system )
ts2 = SQLCONFIG [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 2 , _tmout , system )
continue
2020-11-16 15:45:02 -05:00
2020-12-27 12:32:38 -05:00
#Preserve options line
if ' OPTIONS ' in CONFIG [ ' SYSTEMS ' ] [ system ] :
SQLCONFIG [ system ] [ ' OPTIONS ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' OPTIONS ' ]
2021-01-05 20:38:09 -05:00
SQLCONFIG [ system ] [ ' TS1_STATIC ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ]
SQLCONFIG [ system ] [ ' TS2_STATIC ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ]
SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_UA_TIMER ' ]
SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ]
2020-12-27 12:32:38 -05:00
#logger.debug('(MYSQL) %s has HBP Options line - skipping',system)
#continue
2020-10-18 08:41:38 -04:00
if SQLCONFIG [ system ] [ ' ENABLED ' ] == False and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == True :
logger . debug ( ' (MYSQL) %s changed from enabled to disabled, killing HBP listener and removing from bridges ' , system )
systems [ system ] . master_dereg ( )
2020-11-08 15:12:53 -05:00
if systems [ system ] . _system_maintenance is not None and systems [ system ] . _system_maintenance . running == True :
2020-11-07 11:01:41 -05:00
systems [ system ] . _system_maintenance . stop ( )
systems [ system ] . _system_maintenance = None
2020-10-18 08:41:38 -04:00
remove_bridge_system ( system )
listeningPorts [ system ] . stopListening ( )
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == False and SQLCONFIG [ system ] [ ' ENABLED ' ] == True :
logger . debug ( ' (MYSQL) %s changed from disabled to enabled, starting HBP listener ' , system )
systems [ system ] = routerHBP ( system , CONFIG , report_server )
listeningPorts [ system ] = reactor . listenUDP ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PORT ' ] , systems [ system ] , interface = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' IP ' ] )
logger . debug ( ' (GLOBAL) %s instance created: %s , %s ' , CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] , system , systems [ system ] )
2020-09-30 15:46:35 -04:00
logger . debug ( ' (MYSQL) adding new system to static bridges ' )
2020-10-18 08:41:38 -04:00
_tmout = SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ]
2020-09-30 15:46:35 -04:00
for _bridge in BRIDGES :
ts1 = False
ts2 = False
for i , e in enumerate ( BRIDGES [ _bridge ] ) :
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 1 :
ts1 = True
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 2 :
ts2 = True
if _bridge [ 0 : 1 ] != ' # ' :
if ts1 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 1 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
else :
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ bytes_3 ( 4000 ) ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
2021-01-27 13:47:55 -05:00
2020-10-18 08:41:38 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
2020-12-27 12:32:38 -05:00
if ' OPTIONS ' not in SQLCONFIG [ system ] :
2020-10-06 17:20:57 -04:00
logger . debug ( ' (MYSQL) %s setting default reflector ' , system )
make_default_reflector ( SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
2020-10-18 08:41:38 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' TS1_STATIC ' ] :
2020-12-27 12:32:38 -05:00
if ' OPTIONS ' not in SQLCONFIG [ system ] :
logger . debug ( ' (MYSQL) %s setting static TGs on TS1 ' , system )
ts1 = SQLCONFIG [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 1 , _tmout , system )
if SQLCONFIG [ system ] [ ' TS2_STATIC ' ] :
logger . debug ( ' (MYSQL) %s setting static TGs on TS2 ' , system )
ts2 = SQLCONFIG [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 2 , _tmout , system )
2020-10-23 19:04:45 -04:00
if SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_UA_TIMER ' ] :
2021-02-14 07:07:06 -05:00
if ' OPTIONS ' not in CONFIG [ ' SYSTEMS ' ] [ system ] :
2021-02-14 08:13:12 -05:00
logger . debug ( ' (MYSQL) %s DEFAULT_UA_TIMER changed. Updating bridges. ' , system )
2020-12-27 12:32:38 -05:00
remove_bridge_system ( system )
2021-02-14 08:13:12 -05:00
for _bridge in BRIDGES :
ts1 = False
ts2 = False
2021-03-12 12:35:27 -05:00
_tmout = CONFIG [ ' SYSTEMS ' ] [ system ] [ DEFAULT_UA_TIMER ]
2021-02-14 08:13:12 -05:00
for i , e in enumerate ( BRIDGES [ _bridge ] ) :
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 1 :
ts1 = True
if e [ ' SYSTEM ' ] == system and e [ ' TS ' ] == 2 :
ts2 = True
if _bridge [ 0 : 1 ] != ' # ' :
if ts1 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 1 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( int ( _bridge ) ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ ] , ' ON ' : [ bytes_3 ( int ( _bridge ) ) , ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
else :
if ts2 == False :
BRIDGES [ _bridge ] . append ( { ' SYSTEM ' : system , ' TS ' : 2 , ' TGID ' : bytes_3 ( 9 ) , ' ACTIVE ' : False , ' TIMEOUT ' : _tmout * 60 , ' TO_TYPE ' : ' ON ' , ' OFF ' : [ bytes_3 ( 4000 ) ] , ' ON ' : [ ] , ' RESET ' : [ ] , ' TIMER ' : time ( ) } )
if SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
# if 'OPTIONS' not in SQLCONFIG[system]:
logger . debug ( ' (MYSQL) %s setting default reflector ' , system )
make_default_reflector ( SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
if SQLCONFIG [ system ] [ ' TS1_STATIC ' ] :
# if 'OPTIONS' not in SQLCONFIG[system]:
logger . debug ( ' (MYSQL) %s setting static TGs on TS1 ' , system )
ts1 = SQLCONFIG [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 1 , _tmout , system )
if SQLCONFIG [ system ] [ ' TS2_STATIC ' ] :
logger . debug ( ' (MYSQL) %s setting static TGs on TS2 ' , system )
ts2 = SQLCONFIG [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 2 , _tmout , system )
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' IP ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' IP ' ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == True :
2020-09-30 15:46:35 -04:00
logger . debug ( ' (MYSQL) %s IP binding changed on enabled system, killing HBP listener. Will restart in 1 minute ' , system )
2020-09-30 19:14:31 -04:00
systems [ system ] . master_dereg ( )
2020-11-08 15:12:53 -05:00
if systems [ system ] . _system_maintenance is not None and systems [ system ] . _system_maintenance . running == True :
2020-11-07 11:01:41 -05:00
systems [ system ] . _system_maintenance . stop ( )
systems [ system ] . _system_maintenance = None
2020-09-30 15:46:35 -04:00
listeningPorts [ system ] . stopListening ( )
2020-09-30 19:14:31 -04:00
SQLCONFIG [ system ] [ ' ENABLED ' ] = False
2020-09-30 15:46:35 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' PORT ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PORT ' ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == True :
2020-09-30 15:46:35 -04:00
logger . debug ( ' (MYSQL) %s Port binding changed on enabled system, killing HBP listener. Will restart in 1 minute ' , system )
2020-09-30 19:14:31 -04:00
systems [ system ] . master_dereg ( )
2020-11-08 15:12:53 -05:00
if systems [ system ] . _system_maintenance is not None and systems [ system ] . _system_maintenance . running == True :
2020-11-07 11:01:41 -05:00
systems [ system ] . _system_maintenance . stop ( )
systems [ system ] . _system_maintenance = None
2020-09-30 15:46:35 -04:00
listeningPorts [ system ] . stopListening ( )
2020-09-30 19:14:31 -04:00
SQLCONFIG [ system ] [ ' ENABLED ' ] = False
2020-09-30 15:46:35 -04:00
2020-10-18 08:41:38 -04:00
if SQLCONFIG [ system ] [ ' MAX_PEERS ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MAX_PEERS ' ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == True :
logger . debug ( ' (MYSQL) %s MAX_PEERS changed on enabled system, killing HBP listener. Will restart in 1 minute ' , system )
systems [ system ] . master_dereg ( )
2020-11-08 15:12:53 -05:00
if systems [ system ] . _system_maintenance is not None and systems [ system ] . _system_maintenance . running == True :
2020-11-07 11:01:41 -05:00
systems [ system ] . _system_maintenance . stop ( )
systems [ system ] . _system_maintenance = None
2020-10-18 08:41:38 -04:00
listeningPorts [ system ] . stopListening ( )
SQLCONFIG [ system ] [ ' ENABLED ' ] = False
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' PASSPHRASE ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PASSPHRASE ' ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] == True :
2020-09-30 19:14:31 -04:00
logger . debug ( ' (MYSQL) %s Passphrase changed on enabled system. Kicking peers ' , system )
systems [ system ] . master_dereg ( )
2020-10-04 10:19:46 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] :
2020-12-27 12:32:38 -05:00
if ' OPTIONS ' not in SQLCONFIG [ system ] :
_tmout = SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ]
if SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
logger . debug ( ' (MYSQL) %s default reflector changed, updating ' , system )
reset_default_reflector ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
make_default_reflector ( SQLCONFIG [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
else :
logger . debug ( ' (MYSQL) %s default reflector disabled, updating ' , system )
reset_default_reflector ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] , _tmout , system )
2020-10-06 07:12:49 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' TS1_STATIC ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ] :
2021-02-14 07:07:06 -05:00
if ' OPTIONS ' not in CONFIG [ ' SYSTEMS ' ] [ system ] :
2020-12-27 12:32:38 -05:00
_tmout = SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ]
logger . debug ( ' (MYSQL) %s TS1 static TGs changed, updating ' , system )
ts1 = [ ]
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ] :
ts1 = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
reset_static_tg ( tg , 1 , _tmout , system )
ts1 = [ ]
if SQLCONFIG [ system ] [ ' TS1_STATIC ' ] :
ts1 = SQLCONFIG [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 1 , _tmout , system )
2020-10-06 07:12:49 -04:00
2020-10-06 17:20:57 -04:00
if SQLCONFIG [ system ] [ ' TS2_STATIC ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ] :
2021-02-14 07:07:06 -05:00
if ' OPTIONS ' not in CONFIG [ ' SYSTEMS ' ] [ system ] :
2020-12-27 12:32:38 -05:00
_tmout = SQLCONFIG [ system ] [ ' DEFAULT_UA_TIMER ' ]
logger . debug ( ' (MYSQL) %s TS2 static TGs changed, updating ' , system )
ts2 = [ ]
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ] :
ts2 = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
reset_static_tg ( tg , 2 , _tmout , system )
ts2 = [ ]
if SQLCONFIG [ system ] [ ' TS2_STATIC ' ] :
ts2 = SQLCONFIG [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
for tg in ts2 :
if not tg :
continue
tg = int ( tg )
make_static_tg ( tg , 2 , _tmout , system )
2020-10-19 16:02:04 -04:00
2021-05-22 17:56:20 -04:00
if SQLCONFIG [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] :
logger . debug ( ' (MYSQL) %s announcement language changed to %s ' , system , SQLCONFIG [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] )
2020-10-19 16:02:04 -04:00
#Rebuild ACLs
SQLCONFIG [ system ] [ ' REG_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' REG_ACL ' ] , PEER_MAX )
SQLCONFIG [ system ] [ ' SUB_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' SUB_ACL ' ] , ID_MAX )
SQLCONFIG [ system ] [ ' TG1_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' TG1_ACL ' ] , ID_MAX )
SQLCONFIG [ system ] [ ' TG2_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' TG2_ACL ' ] , ID_MAX )
if SQLCONFIG [ system ] [ ' REG_ACL ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' REG_ACL ' ] :
logger . debug ( ' (MYSQL) registration ACL changed ' )
if SQLCONFIG [ system ] [ ' SUB_ACL ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' SUB_ACL ' ] :
logger . debug ( ' (MYSQL) subscriber ACL changed ' )
if SQLCONFIG [ system ] [ ' TG1_ACL ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TG1_ACL ' ] :
logger . debug ( ' (MYSQL) TG1 ACL changed ' )
if SQLCONFIG [ system ] [ ' TG2_ACL ' ] != CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TG2_ACL ' ] :
logger . debug ( ' (MYSQL) TG2 ACL changed ' )
2020-10-06 07:12:49 -04:00
2020-11-16 15:45:02 -05:00
#Preserve peers list
2021-01-05 20:38:09 -05:00
if system in CONFIG [ ' SYSTEMS ' ] and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] and ' PEERS ' in CONFIG [ ' SYSTEMS ' ] [ system ] :
2020-11-16 15:45:02 -05:00
SQLCONFIG [ system ] [ ' PEERS ' ] = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PEERS ' ]
2021-01-05 20:38:09 -05:00
CONFIG [ ' SYSTEMS ' ] [ system ] . update ( SQLCONFIG [ system ] )
else :
CONFIG [ ' SYSTEMS ' ] [ system ] . update ( SQLCONFIG [ system ] )
2021-01-04 18:09:48 -05:00
2020-09-30 15:46:35 -04:00
#Add MySQL config data to config dict
2021-01-04 18:09:48 -05:00
#CONFIG['SYSTEMS'].update(SQLCONFIG)
2020-09-30 19:14:31 -04:00
2020-09-30 19:23:20 -04:00
SQLCONFIG = { }
2020-09-22 15:06:07 -04:00
2020-08-19 15:59:20 -04:00
class routerOBP ( OPENBRIDGE ) :
def __init__ ( self , _name , _config , _report ) :
OPENBRIDGE . __init__ ( self , _name , _config , _report )
self . STATUS = { }
2021-04-16 13:15:41 -04:00
2020-12-20 12:17:07 -05:00
def to_target ( self , _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data , pkt_time , dmrpkt , _bits , _bridge , _system , _noOBP , sysIgnore ) :
_sysIgnore = sysIgnore
2020-11-13 17:24:31 -05:00
for _target in BRIDGES [ _bridge ] :
if ( _target [ ' SYSTEM ' ] != self . _system ) and ( _target [ ' ACTIVE ' ] ) :
_target_status = systems [ _target [ ' SYSTEM ' ] ] . STATUS
_target_system = self . _CONFIG [ ' SYSTEMS ' ] [ _target [ ' SYSTEM ' ] ]
2020-12-19 13:51:52 -05:00
if ( _target [ ' SYSTEM ' ] , _target [ ' TS ' ] ) in _sysIgnore :
2020-12-20 18:28:13 -05:00
#logger.debug("(DEDUP) OBP Source Skipping system %s TS: %s",_target['SYSTEM'],_target['TS'])
2020-12-19 13:51:52 -05:00
continue
2020-11-13 17:24:31 -05:00
if _target_system [ ' MODE ' ] == ' OPENBRIDGE ' :
2020-12-19 13:51:52 -05:00
if _noOBP == True :
2020-12-03 15:28:23 -05:00
continue
2020-12-20 13:05:19 -05:00
#We want to ignore this system and TS combination if it's called again for this packet
_sysIgnore . append ( ( _target [ ' SYSTEM ' ] , _target [ ' TS ' ] ) )
2021-04-15 13:33:38 -04:00
#If target has quenched us, don't send
2021-04-12 18:28:01 -04:00
if ( ' _bcsq ' in _target_system ) and ( _dst_id in _target_system [ ' _bcsq ' ] ) and ( _target_system [ ' _bcsq ' ] [ _dst_id ] == _stream_id ) :
#logger.info('(%s) Conference Bridge: %s, is Source Quenched for Stream ID: %s, skipping system: %s TS: %s, TGID: %s', self._system, _bridge, int_id(_stream_id), _target['SYSTEM'], _target['TS'], int_id(_target['TGID']))
continue
2021-04-15 13:59:56 -04:00
#If target has missed 6 (on 1 min) of keepalives, don't send
2021-05-03 20:41:35 -04:00
if _target_system [ ' ENHANCED_OBP ' ] and ( ' _bcka ' not in _target_system or _target_system [ ' _bcka ' ] < pkt_time - 60 ) :
2021-04-15 13:59:56 -04:00
continue
2021-07-04 20:10:26 -04:00
#If talkgroup is prohibited by ACL
if self . _CONFIG [ ' GLOBAL ' ] [ ' USE_ACL ' ] :
if not acl_check ( _target [ ' TGID ' ] , self . _CONFIG [ ' GLOBAL ' ] [ ' TG1_ACL ' ] ) :
#logger.info('(%s) TGID prohibited by ACL, not sending', _target['SYSTEM'], int_id(_dst_id))
continue
if not acl_check ( _target [ ' TGID ' ] , _target_system [ ' TG1_ACL ' ] ) :
#logger.info('(%s) TGID prohibited by ACL, not sending', _target['SYSTEM'])
continue
2021-04-12 18:28:01 -04:00
2020-11-13 17:24:31 -05:00
# Is this a new call stream on the target?
if ( _stream_id not in _target_status ) :
# This is a new call stream on the target
_target_status [ _stream_id ] = {
' START ' : pkt_time ,
' CONTENTION ' : False ,
' RFS ' : _rf_src ,
' TGID ' : _dst_id ,
2021-06-15 16:02:24 -04:00
' RX_PEER ' : _peer_id ,
2020-11-13 17:24:31 -05:00
}
# Generate LCs (full and EMB) for the TX stream
2021-03-24 14:19:18 -04:00
try :
dst_lc = b ' ' . join ( [ self . STATUS [ _stream_id ] [ ' LC ' ] [ 0 : 3 ] , _target [ ' TGID ' ] , _rf_src ] )
2021-06-15 16:02:24 -04:00
except Exception :
logger . exception ( ' (to_target) caught exception ' )
2021-03-24 14:19:18 -04:00
return
2020-11-13 17:24:31 -05:00
_target_status [ _stream_id ] [ ' H_LC ' ] = bptc . encode_header_lc ( dst_lc )
_target_status [ _stream_id ] [ ' T_LC ' ] = bptc . encode_terminator_lc ( dst_lc )
_target_status [ _stream_id ] [ ' EMB_LC ' ] = bptc . encode_emblc ( dst_lc )
logger . info ( ' ( %s ) Conference Bridge: %s , Call Bridged to OBP System: %s TS: %s , TGID: %s ' , self . _system , _bridge , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,START,TX, {} , {} , {} , {} , {} , {} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Record the time of this packet so we can later identify a stale stream
_target_status [ _stream_id ] [ ' LAST ' ] = pkt_time
# Clear the TS bit -- all OpenBridge streams are effectively on TS1
_tmp_bits = _bits & ~ ( 1 << 7 )
# Assemble transmit HBP packet header
_tmp_data = b ' ' . join ( [ _data [ : 8 ] , _target [ ' TGID ' ] , _data [ 11 : 15 ] , _tmp_bits . to_bytes ( 1 , ' big ' ) , _data [ 16 : 20 ] ] )
# MUST TEST FOR NEW STREAM AND IF SO, RE-WRITE THE LC FOR THE TARGET
# MUST RE-WRITE DESTINATION TGID IF DIFFERENT
# if _dst_id != rule['DST_GROUP']:
dmrbits = bitarray ( endian = ' big ' )
dmrbits . frombytes ( dmrpkt )
# Create a voice header packet (FULL LC)
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
dmrbits = _target_status [ _stream_id ] [ ' H_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _stream_id ] [ ' H_LC ' ] [ 98 : 197 ]
# Create a voice terminator packet (FULL LC)
elif _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VTERM :
dmrbits = _target_status [ _stream_id ] [ ' T_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _stream_id ] [ ' T_LC ' ] [ 98 : 197 ]
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
call_duration = pkt_time - _target_status [ _stream_id ] [ ' START ' ]
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,TX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Create a Burst B-E packet (Embedded LC)
elif _dtype_vseq in [ 1 , 2 , 3 , 4 ] :
dmrbits = dmrbits [ 0 : 116 ] + _target_status [ _stream_id ] [ ' EMB_LC ' ] [ _dtype_vseq ] + dmrbits [ 148 : 264 ]
dmrpkt = dmrbits . tobytes ( )
_tmp_data = b ' ' . join ( [ _tmp_data , dmrpkt ] )
else :
# BEGIN CONTENTION HANDLING
#
# The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is:
# From a different group than last RX from this HBSystem, but it has been less than Group Hangtime
# From a different group than last TX to this HBSystem, but it has been less than Group Hangtime
# From the same group as the last RX from this HBSystem, but from a different subscriber, and it has been less than stream timeout
# From the same group as the last TX to this HBSystem, but from a different subscriber, and it has been less than stream timeout
# The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
#
if ( ( _target [ ' TGID ' ] != _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' RX_TIME ' ] ) < _target_system [ ' GROUP_HANGTIME ' ] ) ) :
if self . STATUS [ _stream_id ] [ ' CONTENTION ' ] == False :
self . STATUS [ _stream_id ] [ ' CONTENTION ' ] = True
logger . info ( ' ( %s ) Call not routed to TGID %s , target active or in group hangtime: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) )
continue
if ( ( _target [ ' TGID ' ] != _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] ) < _target_system [ ' GROUP_HANGTIME ' ] ) ) :
if self . STATUS [ _stream_id ] [ ' CONTENTION ' ] == False :
self . STATUS [ _stream_id ] [ ' CONTENTION ' ] = True
logger . info ( ' ( %s ) Call not routed to TGID %s , target in group hangtime: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) )
continue
if ( _target [ ' TGID ' ] == _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' RX_TIME ' ] ) < STREAM_TO ) :
if self . STATUS [ _stream_id ] [ ' CONTENTION ' ] == False :
self . STATUS [ _stream_id ] [ ' CONTENTION ' ] = True
logger . info ( ' ( %s ) Call not routed to TGID %s , matching call already active on target: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) )
continue
if ( _target [ ' TGID ' ] == _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) and ( _rf_src != _target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] ) < STREAM_TO ) :
if self . STATUS [ _stream_id ] [ ' CONTENTION ' ] == False :
self . STATUS [ _stream_id ] [ ' CONTENTION ' ] = True
logger . info ( ' ( %s ) Call not routed for subscriber %s , call route in progress on target: HBSystem: %s , TS: %s , TGID: %s , SUB: %s ' , self . _system , int_id ( _rf_src ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] ) )
continue
# Is this a new call stream?
if ( _target_status [ _target [ ' TS ' ] ] [ ' TX_STREAM_ID ' ] != _stream_id ) :
# Record the DST TGID and Stream ID
_target_status [ _target [ ' TS ' ] ] [ ' TX_START ' ] = pkt_time
_target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] = _target [ ' TGID ' ]
_target_status [ _target [ ' TS ' ] ] [ ' TX_STREAM_ID ' ] = _stream_id
_target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] = _rf_src
_target_status [ _target [ ' TS ' ] ] [ ' TX_PEER ' ] = _peer_id
# Generate LCs (full and EMB) for the TX stream
dst_lc = b ' ' . join ( [ self . STATUS [ _stream_id ] [ ' LC ' ] [ 0 : 3 ] , _target [ ' TGID ' ] , _rf_src ] )
_target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] = bptc . encode_header_lc ( dst_lc )
_target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] = bptc . encode_terminator_lc ( dst_lc )
_target_status [ _target [ ' TS ' ] ] [ ' TX_EMB_LC ' ] = bptc . encode_emblc ( dst_lc )
logger . debug ( ' ( %s ) Generating TX FULL and EMB LCs for HomeBrew destination: System: %s , TS: %s , TGID: %s ' , self . _system , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
logger . info ( ' ( %s ) Conference Bridge: %s , Call Bridged to HBP System: %s TS: %s , TGID: %s ' , self . _system , _bridge , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,START,TX, {} , {} , {} , {} , {} , {} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Set other values for the contention handler to test next time there is a frame to forward
_target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] = pkt_time
_target_status [ _target [ ' TS ' ] ] [ ' TX_TYPE ' ] = _dtype_vseq
# Handle any necessary re-writes for the destination
if _system [ ' TS ' ] != _target [ ' TS ' ] :
_tmp_bits = _bits ^ 1 << 7
else :
_tmp_bits = _bits
# Assemble transmit HBP packet header
_tmp_data = b ' ' . join ( [ _data [ : 8 ] , _target [ ' TGID ' ] , _data [ 11 : 15 ] , _tmp_bits . to_bytes ( 1 , ' big ' ) , _data [ 16 : 20 ] ] )
# MUST TEST FOR NEW STREAM AND IF SO, RE-WRITE THE LC FOR THE TARGET
# MUST RE-WRITE DESTINATION TGID IF DIFFERENT
# if _dst_id != rule['DST_GROUP']:
dmrbits = bitarray ( endian = ' big ' )
dmrbits . frombytes ( dmrpkt )
# Create a voice header packet (FULL LC)
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
dmrbits = _target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] [ 98 : 197 ]
# Create a voice terminator packet (FULL LC)
elif _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VTERM :
dmrbits = _target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] [ 98 : 197 ]
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
call_duration = pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_START ' ]
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,TX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Create a Burst B-E packet (Embedded LC)
elif _dtype_vseq in [ 1 , 2 , 3 , 4 ] :
dmrbits = dmrbits [ 0 : 116 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_EMB_LC ' ] [ _dtype_vseq ] + dmrbits [ 148 : 264 ]
dmrpkt = dmrbits . tobytes ( )
_tmp_data = b ' ' . join ( [ _tmp_data , dmrpkt , b ' \x00 \x00 ' ] ) # Add two bytes of nothing since OBP doesn't include BER & RSSI bytes #_data[53:55]
# Transmit the packet to the destination system
systems [ _target [ ' SYSTEM ' ] ] . send_system ( _tmp_data )
#logger.debug('(%s) Packet routed by bridge: %s to system: %s TS: %s, TGID: %s', self._system, _bridge, _target['SYSTEM'], _target['TS'], int_id(_target['TGID']))
2020-12-19 13:51:52 -05:00
#Ignore this system and TS pair if it's called again on this packet
return ( _sysIgnore )
2020-08-19 15:59:20 -04:00
def dmrd_received ( self , _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data ) :
pkt_time = time ( )
dmrpkt = _data [ 20 : 53 ]
_bits = _data [ 15 ]
2021-04-16 13:15:41 -04:00
2021-05-31 12:27:44 -04:00
if _call_type == ' group ' or _call_type == ' vcsbk ' :
2020-08-19 15:59:20 -04:00
# Is this a new call stream?
if ( _stream_id not in self . STATUS ) :
2021-03-24 14:19:18 -04:00
2020-08-19 15:59:20 -04:00
# This is a new call stream
self . STATUS [ _stream_id ] = {
' START ' : pkt_time ,
' CONTENTION ' : False ,
' RFS ' : _rf_src ,
' TGID ' : _dst_id ,
2021-07-23 12:01:16 -04:00
' 1ST ' : perf_counter ( ) ,
2021-04-16 13:15:41 -04:00
' lastSeq ' : False ,
2021-06-14 19:24:49 -04:00
' lastData ' : False ,
' RX_PEER ' : _peer_id
2021-04-16 13:15:41 -04:00
2020-08-19 15:59:20 -04:00
}
# If we can, use the LC from the voice header as to keep all options intact
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
decoded = decode . voice_head_term ( dmrpkt )
self . STATUS [ _stream_id ] [ ' LC ' ] = decoded [ ' LC ' ]
# If we don't have a voice header then don't wait to decode the Embedded LC
# just make a new one from the HBP header. This is good enough, and it saves lots of time
else :
self . STATUS [ _stream_id ] [ ' LC ' ] = LC_OPT + _dst_id + _rf_src
logger . info ( ' ( %s ) *CALL START* STREAM ID: %s SUB: %s ( %s ) PEER: %s ( %s ) TGID %s ( %s ), TS %s ' , \
self . _system , int_id ( _stream_id ) , get_alias ( _rf_src , subscriber_ids ) , int_id ( _rf_src ) , get_alias ( _peer_id , peer_ids ) , int_id ( _peer_id ) , get_alias ( _dst_id , talkgroup_ids ) , int_id ( _dst_id ) , _slot )
2021-03-26 06:45:28 -04:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
self . _report . send_bridgeEvent ( ' GROUP VOICE,START,RX, {} , {} , {} , {} , {} , {} ' . format ( self . _system , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _slot , int_id ( _dst_id ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2021-03-24 14:19:18 -04:00
else :
2021-04-16 13:15:41 -04:00
#Finished stream handling#
2021-04-14 15:27:48 -04:00
if ' _fin ' in self . STATUS [ _stream_id ] :
if ' _finlog ' not in self . STATUS [ _stream_id ] :
logger . warning ( " ( %s ) OBP *LoopControl* STREAM ID: %s ALREADY FINISHED FROM THIS SOURCE, IGNORING " , self . _system , int_id ( _stream_id ) )
self . STATUS [ _stream_id ] [ ' _finlog ' ] = True
return
2021-07-23 12:01:16 -04:00
#LoopControl
hr_times = { }
2021-03-24 14:19:18 -04:00
for system in systems :
2021-07-23 12:01:16 -04:00
# if system == self._system:
# continue
if system != self . _system and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' OPENBRIDGE ' :
2021-03-24 14:19:18 -04:00
for _sysslot in systems [ system ] . STATUS :
if ' RX_STREAM_ID ' in systems [ system ] . STATUS [ _sysslot ] and _stream_id == systems [ system ] . STATUS [ _sysslot ] [ ' RX_STREAM_ID ' ] :
if ' LOOPLOG ' not in self . STATUS [ _stream_id ] or not self . STATUS [ _stream_id ] [ ' LOOPLOG ' ] :
2021-04-27 14:45:37 -04:00
logger . warning ( " ( %s ) OBP *LoopControl* FIRST HBP: %s , STREAM ID: %s , TG: %s , TS: %s , IGNORE THIS SOURCE " , self . _system , system , int_id ( _stream_id ) , int_id ( _dst_id ) , _sysslot )
2021-03-24 14:19:18 -04:00
self . STATUS [ _stream_id ] [ ' LOOPLOG ' ] = True
self . STATUS [ _stream_id ] [ ' LAST ' ] = pkt_time
return
else :
#if _stream_id in systems[system].STATUS and systems[system].STATUS[_stream_id]['START'] <= self.STATUS[_stream_id]['START']:
if _stream_id in systems [ system ] . STATUS and ' 1ST ' in systems [ system ] . STATUS [ _stream_id ] and systems [ system ] . STATUS [ _stream_id ] [ ' TGID ' ] == _dst_id :
2021-07-23 12:01:16 -04:00
hr_times [ system ] = systems [ system ] . STATUS [ _stream_id ] [ ' 1ST ' ]
#use the minimum perf_counter to ensure
#We always use only the earliest packet
2021-08-05 12:46:28 -04:00
fi = min ( hr_times , key = hr_times . get , default = False )
2021-07-23 12:01:16 -04:00
hr_times = None
2021-08-05 12:46:28 -04:00
if not fi :
logger . warning ( " ( %s ) OBP *LoopControl* fi is empty for some reason : %s , STREAM ID: %s , TG: %s , TS: %s " , self . _system , int_id ( _stream_id ) , int_id ( _dst_id ) , _sysslot )
return
2021-07-23 12:01:16 -04:00
if self . _system != fi :
if ' LOOPLOG ' not in self . STATUS [ _stream_id ] or not self . STATUS [ _stream_id ] [ ' LOOPLOG ' ] :
logger . warning ( " ( %s ) OBP *LoopControl* FIRST OBP %s , STREAM ID: %s , TG %s , IGNORE THIS SOURCE " , self . _system , fi , int_id ( _stream_id ) , int_id ( _dst_id ) )
self . STATUS [ _stream_id ] [ ' LOOPLOG ' ] = True
self . STATUS [ _stream_id ] [ ' LAST ' ] = pkt_time
if CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' ENHANCED_OBP ' ] and ' _bcsq ' not in self . STATUS [ _stream_id ] :
systems [ self . _system ] . send_bcsq ( _dst_id , _stream_id )
#logger.warning("(%s) OBP *BridgeControl* Sent BCSQ , STREAM ID: %s, TG %s",self._system, int_id(_stream_id), int_id(_dst_id))
self . STATUS [ _stream_id ] [ ' _bcsq ' ] = True
return
2021-04-16 13:15:41 -04:00
#Duplicate handling#
#Duplicate complete packet
if self . STATUS [ _stream_id ] [ ' lastData ' ] and self . STATUS [ _stream_id ] [ ' lastData ' ] == _data and _seq > 1 :
2021-04-16 18:13:19 -04:00
logger . warning ( " ( %s ) *PacketControl* last packet is a complete duplicate of the previous one, disgarding. Stream ID:, %s TGID: %s " , self . _system , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 13:15:41 -04:00
return
#Handle inbound duplicates
if _seq and _seq == self . STATUS [ _stream_id ] [ ' lastSeq ' ] :
2021-04-16 18:13:19 -04:00
logger . warning ( " ( %s ) *PacketControl* Duplicate sequence number %s , disgarding. Stream ID:, %s TGID: %s " , self . _system , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 13:15:41 -04:00
return
#Inbound out-of-order packets
if _seq and self . STATUS [ _stream_id ] [ ' lastSeq ' ] and ( _seq != 1 ) and ( _seq < self . STATUS [ _stream_id ] [ ' lastSeq ' ] ) :
2021-04-16 18:13:19 -04:00
logger . warning ( " %s ) *PacketControl* Out of order packet - last SEQ: %s , this SEQ: %s , disgarding. Stream ID:, %s TGID: %s " , self . _system , self . STATUS [ _stream_id ] [ ' lastSeq ' ] , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 13:15:41 -04:00
return
#Inbound missed packets
if _seq and self . STATUS [ _stream_id ] [ ' lastSeq ' ] and _seq > ( self . STATUS [ _stream_id ] [ ' lastSeq ' ] + 1 ) :
2021-04-27 14:45:38 -04:00
logger . warning ( " ( %s ) *PacketControl* Missed packet(s) - last SEQ: %s , this SEQ: %s . Stream ID:, %s TGID: %s " , self . _system , self . STATUS [ _stream_id ] [ ' lastSeq ' ] , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 13:15:41 -04:00
#Save this sequence number
self . STATUS [ _stream_id ] [ ' lastSeq ' ] = _seq
#Save this packet
self . STATUS [ _stream_id ] [ ' lastData ' ] = _data
2021-03-26 06:45:28 -04:00
2020-08-19 15:59:20 -04:00
self . STATUS [ _stream_id ] [ ' LAST ' ] = pkt_time
2020-12-21 10:19:41 -05:00
2021-01-10 08:50:37 -05:00
#Create STAT bridge for unknown TG
if CONFIG [ ' GLOBAL ' ] [ ' GEN_STAT_BRIDGES ' ] :
if int_id ( _dst_id ) > = 5 and int_id ( _dst_id ) != 9 and ( str ( int_id ( _dst_id ) ) not in BRIDGES ) :
logger . info ( ' ( %s ) Bridge for STAT TG %s does not exist. Creating ' , self . _system , int_id ( _dst_id ) )
make_stat_bridge ( _dst_id )
2020-12-19 13:51:52 -05:00
_sysIgnore = [ ]
2020-08-19 15:59:20 -04:00
for _bridge in BRIDGES :
2020-12-19 14:04:07 -05:00
#if _bridge[0:1] != '#':
2020-12-19 13:51:52 -05:00
#if True:
2020-12-05 15:27:54 -05:00
for _system in BRIDGES [ _bridge ] :
2021-04-12 18:28:01 -04:00
2021-01-10 15:44:26 -05:00
if _system [ ' SYSTEM ' ] == self . _system and _system [ ' TGID ' ] == _dst_id and _system [ ' TS ' ] == _slot and _system [ ' ACTIVE ' ] == True :
2020-12-19 13:51:52 -05:00
_sysIgnore = self . to_target ( _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data , pkt_time , dmrpkt , _bits , _bridge , _system , False , _sysIgnore )
2020-08-19 15:59:20 -04:00
# Final actions - Is this a voice terminator?
if ( _frame_type == HBPF_DATA_SYNC ) and ( _dtype_vseq == HBPF_SLT_VTERM ) :
call_duration = pkt_time - self . STATUS [ _stream_id ] [ ' START ' ]
logger . info ( ' ( %s ) *CALL END* STREAM ID: %s SUB: %s ( %s ) PEER: %s ( %s ) TGID %s ( %s ), TS %s , Duration: %.2f ' , \
self . _system , int_id ( _stream_id ) , get_alias ( _rf_src , subscriber_ids ) , int_id ( _rf_src ) , get_alias ( _peer_id , peer_ids ) , int_id ( _peer_id ) , get_alias ( _dst_id , talkgroup_ids ) , int_id ( _dst_id ) , _slot , call_duration )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
self . _report . send_bridgeEvent ( ' GROUP VOICE,END,RX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( self . _system , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _slot , int_id ( _dst_id ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2021-03-25 18:51:41 -04:00
self . STATUS [ _stream_id ] [ ' _fin ' ] = True
2021-03-24 14:19:18 -04:00
#removed = self.STATUS.pop(_stream_id)
#logger.debug('(%s) OpenBridge sourced call stream end, remove terminated Stream ID: %s', self._system, int_id(_stream_id))
#if not removed:
#selflogger.error('(%s) *CALL END* STREAM ID: %s NOT IN LIST -- THIS IS A REAL PROBLEM', self._system, int_id(_stream_id))
2020-12-21 10:19:41 -05:00
#Reset sequence number
2021-04-16 13:15:41 -04:00
self . STATUS [ _stream_id ] [ ' lastSeq ' ] = False
2020-08-19 15:59:20 -04:00
class routerHBP ( HBSYSTEM ) :
def __init__ ( self , _name , _config , _report ) :
HBSYSTEM . __init__ ( self , _name , _config , _report )
# Status information for the system, TS1 & TS2
# 1 & 2 are "timeslot"
# In TX_EMB_LC, 2-5 are burst B-E
self . STATUS = {
1 : {
' RX_START ' : time ( ) ,
' TX_START ' : time ( ) ,
' RX_SEQ ' : 0 ,
' RX_RFS ' : b ' \x00 ' ,
' TX_RFS ' : b ' \x00 ' ,
' RX_PEER ' : b ' \x00 ' ,
' TX_PEER ' : b ' \x00 ' ,
' RX_STREAM_ID ' : b ' \x00 ' ,
' TX_STREAM_ID ' : b ' \x00 ' ,
' RX_TGID ' : b ' \x00 \x00 \x00 ' ,
' TX_TGID ' : b ' \x00 \x00 \x00 ' ,
' RX_TIME ' : time ( ) ,
' TX_TIME ' : time ( ) ,
' RX_TYPE ' : HBPF_SLT_VTERM ,
' TX_TYPE ' : HBPF_SLT_VTERM ,
' RX_LC ' : b ' \x00 ' ,
' TX_H_LC ' : b ' \x00 ' ,
' TX_T_LC ' : b ' \x00 ' ,
' TX_EMB_LC ' : {
1 : b ' \x00 ' ,
2 : b ' \x00 ' ,
3 : b ' \x00 ' ,
4 : b ' \x00 ' ,
2021-04-16 15:48:14 -04:00
} ,
' lastSeq ' : False ,
' lastData ' : False
2020-08-19 15:59:20 -04:00
} ,
2 : {
' RX_START ' : time ( ) ,
' TX_START ' : time ( ) ,
' RX_SEQ ' : 0 ,
' RX_RFS ' : b ' \x00 ' ,
' TX_RFS ' : b ' \x00 ' ,
' RX_PEER ' : b ' \x00 ' ,
' TX_PEER ' : b ' \x00 ' ,
' RX_STREAM_ID ' : b ' \x00 ' ,
' TX_STREAM_ID ' : b ' \x00 ' ,
' RX_TGID ' : b ' \x00 \x00 \x00 ' ,
' TX_TGID ' : b ' \x00 \x00 \x00 ' ,
' RX_TIME ' : time ( ) ,
' TX_TIME ' : time ( ) ,
' RX_TYPE ' : HBPF_SLT_VTERM ,
' TX_TYPE ' : HBPF_SLT_VTERM ,
' RX_LC ' : b ' \x00 ' ,
' TX_H_LC ' : b ' \x00 ' ,
' TX_T_LC ' : b ' \x00 ' ,
' TX_EMB_LC ' : {
1 : b ' \x00 ' ,
2 : b ' \x00 ' ,
3 : b ' \x00 ' ,
4 : b ' \x00 ' ,
2021-04-16 15:48:14 -04:00
} ,
' lastSeq ' : False ,
' lastData ' : False
2020-08-19 15:59:20 -04:00
}
}
2020-12-20 12:17:07 -05:00
def to_target ( self , _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data , pkt_time , dmrpkt , _bits , _bridge , _system , _noOBP , sysIgnore ) :
_sysIgnore = sysIgnore
2020-11-13 17:24:31 -05:00
for _target in BRIDGES [ _bridge ] :
2021-01-16 10:54:52 -05:00
#if _target['SYSTEM'] != self._system or (_target['SYSTEM'] == self._system and _target['TS'] != _slot):
2021-01-16 11:14:51 -05:00
if _target [ ' SYSTEM ' ] != self . _system and _target [ ' ACTIVE ' ] :
#if _target['ACTIVE']:
2021-01-16 11:16:05 -05:00
_target_status = systems [ _target [ ' SYSTEM ' ] ] . STATUS
_target_system = self . _CONFIG [ ' SYSTEMS ' ] [ _target [ ' SYSTEM ' ] ]
2020-12-20 16:48:48 -05:00
2020-12-19 13:51:52 -05:00
if ( _target [ ' SYSTEM ' ] , _target [ ' TS ' ] ) in _sysIgnore :
2020-12-20 18:28:13 -05:00
#logger.debug("(DEDUP) HBP Source - Skipping system %s TS: %s",_target['SYSTEM'],_target['TS'])
2020-12-19 13:51:52 -05:00
continue
2020-12-06 08:18:10 -05:00
if _target_system [ ' MODE ' ] == ' OPENBRIDGE ' :
2020-12-19 13:51:52 -05:00
if _noOBP == True :
2020-12-06 08:18:10 -05:00
continue
2020-12-20 13:05:19 -05:00
#We want to ignore this system and TS combination if it's called again for this packet
_sysIgnore . append ( ( _target [ ' SYSTEM ' ] , _target [ ' TS ' ] ) )
2021-04-14 17:36:28 -04:00
2021-04-15 13:33:38 -04:00
#If target has quenched us, don't send
2021-04-14 17:37:45 -04:00
if ( ' _bcsq ' in _target_system ) and ( _dst_id in _target_system [ ' _bcsq ' ] ) and ( _target_system [ ' _bcsq ' ] [ _target [ ' TGID ' ] ] == _stream_id ) :
2021-04-14 17:36:28 -04:00
#logger.info('(%s) Conference Bridge: %s, is Source Quenched for Stream ID: %s, skipping system: %s TS: %s, TGID: %s', self._system, _bridge, int_id(_stream_id), _target['SYSTEM'], _target['TS'], int_id(_target['TGID']))
continue
2021-04-15 13:59:56 -04:00
#If target has missed 6 (on 1 min) of keepalives, don't send
2021-04-20 20:12:11 -04:00
if _target_system [ ' ENHANCED_OBP ' ] and ' _bcka ' in _target_system and _target_system [ ' _bcka ' ] < pkt_time - 60 :
2021-04-15 13:59:56 -04:00
continue
2021-07-04 20:10:26 -04:00
#If talkgroup is prohibited by ACL
if self . _CONFIG [ ' GLOBAL ' ] [ ' USE_ACL ' ] :
if not acl_check ( _target [ ' TGID ' ] , self . _CONFIG [ ' GLOBAL ' ] [ ' TG1_ACL ' ] ) :
#logger.info('(%s) TGID prohibited by ACL, not sending', _target['SYSTEM'])
continue
if _target_system [ ' USE_ACL ' ] :
if not acl_check ( _target [ ' TGID ' ] , _target_system [ ' TG1_ACL ' ] ) :
#logger.info('(%s) TGID prohibited by ACL, not sending', _target['SYSTEM'])
continue
2020-12-20 13:05:19 -05:00
2020-12-06 08:18:10 -05:00
# Is this a new call stream on the target?
if ( _stream_id not in _target_status ) :
# This is a new call stream on the target
_target_status [ _stream_id ] = {
' START ' : pkt_time ,
' CONTENTION ' : False ,
' RFS ' : _rf_src ,
' TGID ' : _dst_id ,
2021-06-14 19:24:49 -04:00
' RX_PEER ' : _peer_id
2020-12-06 08:18:10 -05:00
}
2020-12-06 07:27:44 -05:00
# Generate LCs (full and EMB) for the TX stream
2020-12-06 08:18:10 -05:00
dst_lc = b ' ' . join ( [ self . STATUS [ _slot ] [ ' RX_LC ' ] [ 0 : 3 ] , _target [ ' TGID ' ] , _rf_src ] )
_target_status [ _stream_id ] [ ' H_LC ' ] = bptc . encode_header_lc ( dst_lc )
_target_status [ _stream_id ] [ ' T_LC ' ] = bptc . encode_terminator_lc ( dst_lc )
_target_status [ _stream_id ] [ ' EMB_LC ' ] = bptc . encode_emblc ( dst_lc )
logger . info ( ' ( %s ) Conference Bridge: %s , Call Bridged to OBP System: %s TS: %s , TGID: %s ' , self . _system , _bridge , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
2020-11-13 17:24:31 -05:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,START,TX, {} , {} , {} , {} , {} , {} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2020-12-06 08:18:10 -05:00
# Record the time of this packet so we can later identify a stale stream
_target_status [ _stream_id ] [ ' LAST ' ] = pkt_time
# Clear the TS bit -- all OpenBridge streams are effectively on TS1
_tmp_bits = _bits & ~ ( 1 << 7 )
# Assemble transmit HBP packet header
_tmp_data = b ' ' . join ( [ _data [ : 8 ] , _target [ ' TGID ' ] , _data [ 11 : 15 ] , _tmp_bits . to_bytes ( 1 , ' big ' ) , _data [ 16 : 20 ] ] )
# MUST TEST FOR NEW STREAM AND IF SO, RE-WRITE THE LC FOR THE TARGET
# MUST RE-WRITE DESTINATION TGID IF DIFFERENT
# if _dst_id != rule['DST_GROUP']:
dmrbits = bitarray ( endian = ' big ' )
dmrbits . frombytes ( dmrpkt )
# Create a voice header packet (FULL LC)
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
dmrbits = _target_status [ _stream_id ] [ ' H_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _stream_id ] [ ' H_LC ' ] [ 98 : 197 ]
# Create a voice terminator packet (FULL LC)
elif _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VTERM :
dmrbits = _target_status [ _stream_id ] [ ' T_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _stream_id ] [ ' T_LC ' ] [ 98 : 197 ]
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
call_duration = pkt_time - _target_status [ _stream_id ] [ ' START ' ]
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,TX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Create a Burst B-E packet (Embedded LC)
elif _dtype_vseq in [ 1 , 2 , 3 , 4 ] :
dmrbits = dmrbits [ 0 : 116 ] + _target_status [ _stream_id ] [ ' EMB_LC ' ] [ _dtype_vseq ] + dmrbits [ 148 : 264 ]
dmrpkt = dmrbits . tobytes ( )
_tmp_data = b ' ' . join ( [ _tmp_data , dmrpkt ] )
2020-11-13 17:24:31 -05:00
else :
2020-12-06 08:18:10 -05:00
# BEGIN STANDARD CONTENTION HANDLING
#
# The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is:
# From a different group than last RX from this HBSystem, but it has been less than Group Hangtime
# From a different group than last TX to this HBSystem, but it has been less than Group Hangtime
# From the same group as the last RX from this HBSystem, but from a different subscriber, and it has been less than stream timeout
# From the same group as the last TX to this HBSystem, but from a different subscriber, and it has been less than stream timeout
# The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
#
if ( ( _target [ ' TGID ' ] != _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' RX_TIME ' ] ) < _target_system [ ' GROUP_HANGTIME ' ] ) ) :
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD and self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] != _stream_id :
logger . info ( ' ( %s ) Call not routed to TGID %s , target active or in group hangtime: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) )
continue
if ( ( _target [ ' TGID ' ] != _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] ) < _target_system [ ' GROUP_HANGTIME ' ] ) ) :
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD and self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] != _stream_id :
logger . info ( ' ( %s ) Call not routed to TGID %s , target in group hangtime: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) )
continue
if ( _target [ ' TGID ' ] == _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' RX_TIME ' ] ) < STREAM_TO ) :
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD and self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] != _stream_id :
logger . info ( ' ( %s ) Call not routed to TGID %s , matching call already active on target: HBSystem: %s , TS: %s , TGID: %s ' , self . _system , int_id ( _target [ ' TGID ' ] ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' RX_TGID ' ] ) )
continue
if ( _target [ ' TGID ' ] == _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) and ( _rf_src != _target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] ) and ( ( pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] ) < STREAM_TO ) :
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD and self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] != _stream_id :
logger . info ( ' ( %s ) Call not routed for subscriber %s , call route in progress on target: HBSystem: %s , TS: %s , TGID: %s , SUB: %s ' , self . _system , int_id ( _rf_src ) , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] ) , int_id ( _target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] ) )
continue
2020-12-06 07:27:44 -05:00
2020-12-06 08:18:10 -05:00
# Is this a new call stream?
if ( _stream_id != self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] ) :
# Record the DST TGID and Stream ID
_target_status [ _target [ ' TS ' ] ] [ ' TX_START ' ] = pkt_time
_target_status [ _target [ ' TS ' ] ] [ ' TX_TGID ' ] = _target [ ' TGID ' ]
_target_status [ _target [ ' TS ' ] ] [ ' TX_STREAM_ID ' ] = _stream_id
_target_status [ _target [ ' TS ' ] ] [ ' TX_RFS ' ] = _rf_src
_target_status [ _target [ ' TS ' ] ] [ ' TX_PEER ' ] = _peer_id
# Generate LCs (full and EMB) for the TX stream
dst_lc = self . STATUS [ _slot ] [ ' RX_LC ' ] [ 0 : 3 ] + _target [ ' TGID ' ] + _rf_src
_target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] = bptc . encode_header_lc ( dst_lc )
_target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] = bptc . encode_terminator_lc ( dst_lc )
_target_status [ _target [ ' TS ' ] ] [ ' TX_EMB_LC ' ] = bptc . encode_emblc ( dst_lc )
logger . debug ( ' ( %s ) Generating TX FULL and EMB LCs for HomeBrew destination: System: %s , TS: %s , TGID: %s ' , self . _system , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
logger . info ( ' ( %s ) Conference Bridge: %s , Call Bridged to HBP System: %s TS: %s , TGID: %s ' , self . _system , _bridge , _target [ ' SYSTEM ' ] , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,START,TX, {} , {} , {} , {} , {} , {} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Set other values for the contention handler to test next time there is a frame to forward
_target_status [ _target [ ' TS ' ] ] [ ' TX_TIME ' ] = pkt_time
_target_status [ _target [ ' TS ' ] ] [ ' TX_TYPE ' ] = _dtype_vseq
# Handle any necessary re-writes for the destination
if _system [ ' TS ' ] != _target [ ' TS ' ] :
_tmp_bits = _bits ^ 1 << 7
else :
_tmp_bits = _bits
# Assemble transmit HBP packet header
_tmp_data = b ' ' . join ( [ _data [ : 8 ] , _target [ ' TGID ' ] , _data [ 11 : 15 ] , _tmp_bits . to_bytes ( 1 , ' big ' ) , _data [ 16 : 20 ] ] )
# MUST TEST FOR NEW STREAM AND IF SO, RE-WRITE THE LC FOR THE TARGET
# MUST RE-WRITE DESTINATION TGID IF DIFFERENT
# if _dst_id != rule['DST_GROUP']:
dmrbits = bitarray ( endian = ' big ' )
dmrbits . frombytes ( dmrpkt )
# Create a voice header packet (FULL LC)
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
dmrbits = _target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_H_LC ' ] [ 98 : 197 ]
# Create a voice terminator packet (FULL LC)
elif _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VTERM :
dmrbits = _target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] [ 0 : 98 ] + dmrbits [ 98 : 166 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_T_LC ' ] [ 98 : 197 ]
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
call_duration = pkt_time - _target_status [ _target [ ' TS ' ] ] [ ' TX_START ' ]
systems [ _target [ ' SYSTEM ' ] ] . _report . send_bridgeEvent ( ' GROUP VOICE,END,TX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( _target [ ' SYSTEM ' ] , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _target [ ' TS ' ] , int_id ( _target [ ' TGID ' ] ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# Create a Burst B-E packet (Embedded LC)
elif _dtype_vseq in [ 1 , 2 , 3 , 4 ] :
dmrbits = dmrbits [ 0 : 116 ] + _target_status [ _target [ ' TS ' ] ] [ ' TX_EMB_LC ' ] [ _dtype_vseq ] + dmrbits [ 148 : 264 ]
2021-01-24 13:56:33 -05:00
try :
dmrpkt = dmrbits . tobytes ( )
except AttributeError :
2021-04-29 19:04:10 -04:00
logger . exception ( ' ( %s ) Non-fatal AttributeError - dmrbits.tobytes() ' , self . _system )
2021-01-24 13:56:33 -05:00
2020-12-06 08:18:10 -05:00
_tmp_data = b ' ' . join ( [ _tmp_data , dmrpkt , _data [ 53 : 55 ] ] )
# Transmit the packet to the destination system
systems [ _target [ ' SYSTEM ' ] ] . send_system ( _tmp_data )
2020-12-20 13:05:19 -05:00
#logger.debug('(%s) Packet routed by bridge: %s to system: %s TS: %s, TGID: %s', self._system, _bridge, _target['SYSTEM'], _target['TS'], int_id(_target['TGID']))
2020-12-19 13:51:52 -05:00
return _sysIgnore
2020-08-19 15:59:20 -04:00
def dmrd_received ( self , _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data ) :
pkt_time = time ( )
dmrpkt = _data [ 20 : 53 ]
_bits = _data [ 15 ]
2020-08-31 06:03:51 -04:00
2020-10-12 11:30:32 -04:00
_nine = bytes_3 ( 9 )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_lang = CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' ANNOUNCEMENT_LANGUAGE ' ]
2020-11-14 05:45:45 -05:00
_int_dst_id = int_id ( _dst_id )
2020-08-31 06:03:51 -04:00
#Handle private calls (for reflectors)
2021-02-21 13:52:37 -05:00
if _call_type == ' unit ' and _slot == 2 :
2020-08-31 11:05:25 -04:00
if ( _stream_id != self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] ) :
2021-05-24 17:32:54 -04:00
self . STATUS [ _slot ] [ ' _stopTgAnnounce ' ] = False
2020-08-31 11:05:25 -04:00
logger . warning ( ' ( %s ) Reflector: Private call from %s to %s ' , self . _system , int_id ( _rf_src ) , _int_dst_id )
2020-09-02 13:34:25 -04:00
#if _int_dst_id >= 4000 and _int_dst_id <= 5000:
2020-12-09 11:30:20 -05:00
if _int_dst_id > = 5 and _int_dst_id < = 999999 :
2020-09-02 13:34:25 -04:00
_bridgename = ' # ' + str ( _int_dst_id )
2021-05-24 17:32:54 -04:00
if _bridgename not in BRIDGES and not ( _int_dst_id > = 4000 and _int_dst_id < = 5000 ) and not ( _int_dst_id > = 9991 and _int_dst_id < = 9999 ) :
2020-11-07 12:36:15 -05:00
logger . info ( ' ( %s ) [A] Reflector for TG %s does not exist. Creating as User Activated. Timeout: %s ' , self . _system , _int_dst_id , CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' DEFAULT_UA_TIMER ' ] )
2020-11-07 12:20:03 -05:00
make_single_reflector ( _dst_id , CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' DEFAULT_UA_TIMER ' ] , self . _system )
2020-10-12 11:04:01 -04:00
2021-05-24 18:12:02 -04:00
if _int_dst_id > 5 and _int_dst_id != 9 and _int_dst_id != 5000 and not ( _int_dst_id > = 9991 and _int_dst_id < = 9999 ) :
2020-09-17 15:34:50 -04:00
for _bridge in BRIDGES :
if _bridge [ 0 : 1 ] != ' # ' :
continue
for _system in BRIDGES [ _bridge ] :
_dehash_bridge = _bridge [ 1 : ]
if _system [ ' SYSTEM ' ] == self . _system :
# TGID matches a rule source, reset its timer
if _slot == _system [ ' TS ' ] and _dst_id == _system [ ' TGID ' ] and ( ( _system [ ' TO_TYPE ' ] == ' ON ' and ( _system [ ' ACTIVE ' ] == True ) ) or ( _system [ ' TO_TYPE ' ] == ' OFF ' and _system [ ' ACTIVE ' ] == False ) ) :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [B] Transmission match for Reflector: %s . Reset timeout to %s ' , self . _system , _bridge , _system [ ' TIMER ' ] )
2020-09-17 15:34:50 -04:00
# TGID matches an ACTIVATION trigger
if _int_dst_id == int ( _dehash_bridge ) and _system [ ' SYSTEM ' ] == self . _system and _slot == _system [ ' TS ' ] :
# Set the matching rule as ACTIVE
if _system [ ' ACTIVE ' ] == False :
_system [ ' ACTIVE ' ] = True
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [C] Reflector: %s , connection changed to state: %s ' , self . _system , _bridge , _system [ ' ACTIVE ' ] )
2020-09-17 15:34:50 -04:00
# Cancel the timer if we've enabled an "OFF" type timeout
if _system [ ' TO_TYPE ' ] == ' OFF ' :
_system [ ' TIMER ' ] = pkt_time
2020-09-20 11:06:56 -04:00
logger . info ( ' ( %s ) [D] Reflector: %s has an " OFF " timer and set to " ON " : timeout timer cancelled ' , self . _system , _bridge )
2020-09-17 15:34:50 -04:00
# Reset the timer for the rule
if _system [ ' ACTIVE ' ] == True and _system [ ' TO_TYPE ' ] == ' ON ' :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [E] Reflector: %s , timeout timer reset to: %s ' , self . _system , _bridge , _system [ ' TIMER ' ] - pkt_time )
2020-09-17 15:34:50 -04:00
# TGID matches an DE-ACTIVATION trigger
#Single TG mode
if ( _dst_id in _system [ ' OFF ' ] or _dst_id in _system [ ' RESET ' ] or ( _int_dst_id != int ( _dehash_bridge ) ) and _system [ ' SYSTEM ' ] == self . _system and _slot == _system [ ' TS ' ] ) :
# Set the matching rule as ACTIVE
#Single TG mode
if _dst_id in _system [ ' OFF ' ] or _int_dst_id != int ( _dehash_bridge ) :
#if _dst_id in _system['OFF']:
if _system [ ' ACTIVE ' ] == True :
_system [ ' ACTIVE ' ] = False
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [F] Reflector: %s , connection changed to state: %s ' , self . _system , _bridge , _system [ ' ACTIVE ' ] )
2020-09-17 15:34:50 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
if _system [ ' TO_TYPE ' ] == ' ON ' :
_system [ ' TIMER ' ] = pkt_time
2020-09-20 11:06:56 -04:00
logger . info ( ' ( %s ) [G] Reflector: %s has ON timer and set to " OFF " : timeout timer cancelled ' , self . _system , _bridge )
2020-09-17 15:34:50 -04:00
# Reset the timer for the rule
if _system [ ' ACTIVE ' ] == False and _system [ ' TO_TYPE ' ] == ' OFF ' :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [H] Reflector: %s , timeout timer reset to: %s ' , self . _system , _bridge , _system [ ' TIMER ' ] - pkt_time )
2020-09-17 15:34:50 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
if _system [ ' ACTIVE ' ] == True and _system [ ' TO_TYPE ' ] == ' ON ' and _dst_id in _system [ ' OFF ' ] :
_system [ ' TIMER ' ] = pkt_time
2020-09-20 11:06:56 -04:00
logger . info ( ' ( %s ) [I] Reflector: %s has ON timer and set to " OFF " : timeout timer cancelled ' , self . _system , _bridge )
2021-01-27 13:47:55 -05:00
2020-09-17 15:34:50 -04:00
if ( _frame_type == HBPF_DATA_SYNC ) and ( _dtype_vseq == HBPF_SLT_VTERM ) and ( self . STATUS [ _slot ] [ ' RX_TYPE ' ] != HBPF_SLT_VTERM ) :
#Speak callsign before message
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say = [ words [ _lang ] [ ' silence ' ] ]
2020-09-17 19:49:48 -04:00
# _systemcs = re.sub(r'\W+', '', self._system)
# _systemcs.upper()
# for character in _systemcs:
# _say.append(words[character])
# _say.append(words['silence'])
2020-09-17 15:34:50 -04:00
#If disconnection called
if _int_dst_id == 4000 :
2021-01-25 08:21:02 -05:00
logger . info ( ' ( %s ) Reflector: voice called - 4000 " not linked " ' , self . _system )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' notlinked ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-09-17 15:34:50 -04:00
#If status called
elif _int_dst_id == 5000 :
2020-09-20 10:58:29 -04:00
_active = False
2020-08-31 11:05:25 -04:00
for _bridge in BRIDGES :
if _bridge [ 0 : 1 ] != ' # ' :
continue
for _system in BRIDGES [ _bridge ] :
2020-08-31 11:58:30 -04:00
_dehash_bridge = _bridge [ 1 : ]
2020-09-19 09:05:52 -04:00
if _system [ ' SYSTEM ' ] == self . _system and _slot == _system [ ' TS ' ] :
2020-09-17 15:34:50 -04:00
if _system [ ' ACTIVE ' ] == True :
2021-01-25 08:21:02 -05:00
logger . info ( ' ( %s ) Reflector: voice called - 5000 status - " linked to %s " ' , self . _system , _dehash_bridge )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' linkedto ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' to ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-09-17 15:34:50 -04:00
for num in str ( _dehash_bridge ) :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ num ] )
2020-09-19 09:05:52 -04:00
2020-09-17 15:34:50 -04:00
_active = True
2020-09-20 10:58:29 -04:00
break
2020-08-31 11:05:25 -04:00
2020-09-17 15:34:50 -04:00
if _active == False :
2021-01-25 08:21:02 -05:00
logger . info ( ' ( %s ) Reflector: voice called - 5000 status - " not linked " ' , self . _system )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' notlinked ' ] )
2020-09-17 15:34:50 -04:00
2021-05-24 17:32:54 -04:00
#Information services
elif _int_dst_id > = 9991 and _int_dst_id < = 9999 :
self . STATUS [ _slot ] [ ' _stopTgAnnounce ' ] = True
reactor . callInThread ( playFileOnRequest , self , _int_dst_id )
#playFileOnRequest(self,_int_dst_id)
2020-09-17 15:34:50 -04:00
#Speak what TG was requested to link
2021-05-24 17:32:54 -04:00
elif not self . STATUS [ _slot ] [ ' _stopTgAnnounce ' ] :
2021-01-25 08:21:02 -05:00
logger . info ( ' ( %s ) Reflector: voice called (linking) " linked to %s " ' , self . _system , _int_dst_id )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' linkedto ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' to ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
_say . append ( words [ _lang ] [ ' silence ' ] )
2020-09-17 15:34:50 -04:00
for num in str ( _int_dst_id ) :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
_say . append ( words [ _lang ] [ num ] )
2020-09-17 15:34:50 -04:00
2021-05-24 17:32:54 -04:00
if not self . STATUS [ _slot ] [ ' _stopTgAnnounce ' ] :
speech = pkt_gen ( bytes_3 ( 5000 ) , _nine , bytes_4 ( 9 ) , 1 , _say )
#call speech in a thread as it contains sleep() and hence could block the reactor
reactor . callInThread ( sendSpeech , self , speech )
2020-09-17 15:34:50 -04:00
2020-08-31 11:05:25 -04:00
# Mark status variables for use later
self . STATUS [ _slot ] [ ' RX_PEER ' ] = _peer_id
self . STATUS [ _slot ] [ ' RX_SEQ ' ] = _seq
self . STATUS [ _slot ] [ ' RX_RFS ' ] = _rf_src
self . STATUS [ _slot ] [ ' RX_TYPE ' ] = _dtype_vseq
self . STATUS [ _slot ] [ ' RX_TGID ' ] = _dst_id
self . STATUS [ _slot ] [ ' RX_TIME ' ] = pkt_time
self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] = _stream_id
2020-08-31 06:03:51 -04:00
#Handle group calls
2021-05-31 12:27:44 -04:00
if _call_type == ' group ' or _call_type == ' vcsbk ' :
2020-08-19 15:59:20 -04:00
# Is this a new call stream?
if ( _stream_id != self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] ) :
if ( self . STATUS [ _slot ] [ ' RX_TYPE ' ] != HBPF_SLT_VTERM ) and ( pkt_time < ( self . STATUS [ _slot ] [ ' RX_TIME ' ] + STREAM_TO ) ) and ( _rf_src != self . STATUS [ _slot ] [ ' RX_RFS ' ] ) :
logger . warning ( ' ( %s ) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> TGID %s , SLOT %s collided with existing call ' , self . _system , int_id ( _stream_id ) , int_id ( _rf_src ) , int_id ( _peer_id ) , int_id ( _dst_id ) , _slot )
return
# This is a new call stream
self . STATUS [ _slot ] [ ' RX_START ' ] = pkt_time
logger . info ( ' ( %s ) *CALL START* STREAM ID: %s SUB: %s ( %s ) PEER: %s ( %s ) TGID %s ( %s ), TS %s ' , \
self . _system , int_id ( _stream_id ) , get_alias ( _rf_src , subscriber_ids ) , int_id ( _rf_src ) , get_alias ( _peer_id , peer_ids ) , int_id ( _peer_id ) , get_alias ( _dst_id , talkgroup_ids ) , int_id ( _dst_id ) , _slot )
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
self . _report . send_bridgeEvent ( ' GROUP VOICE,START,RX, {} , {} , {} , {} , {} , {} ' . format ( self . _system , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _slot , int_id ( _dst_id ) ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
# If we can, use the LC from the voice header as to keep all options intact
if _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VHEAD :
decoded = decode . voice_head_term ( dmrpkt )
self . STATUS [ _slot ] [ ' RX_LC ' ] = decoded [ ' LC ' ]
# If we don't have a voice header then don't wait to decode it from the Embedded LC
# just make a new one from the HBP header. This is good enough, and it saves lots of time
else :
self . STATUS [ _slot ] [ ' RX_LC ' ] = LC_OPT + _dst_id + _rf_src
2020-08-22 06:49:09 -04:00
#Create default bridge for unknown TG
2021-04-17 12:59:46 -04:00
if int_id ( _dst_id ) > = 5 and int_id ( _dst_id ) != 9 and int_id ( _dst_id ) != 4000 and int_id ( _dst_id ) != 5000 and ( str ( int_id ( _dst_id ) ) not in BRIDGES ) :
2020-11-07 09:57:32 -05:00
logger . info ( ' ( %s ) Bridge for TG %s does not exist. Creating as User Activated. Timeout %s ' , self . _system , int_id ( _dst_id ) , CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' DEFAULT_UA_TIMER ' ] )
make_single_bridge ( _dst_id , self . _system , _slot , CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' DEFAULT_UA_TIMER ' ] )
2021-04-16 18:13:19 -04:00
#LoopControl#
for system in systems :
if system == self . _system :
continue
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' OPENBRIDGE ' :
for _sysslot in systems [ system ] . STATUS :
if ' RX_STREAM_ID ' in systems [ system ] . STATUS [ _sysslot ] and _stream_id == systems [ system ] . STATUS [ _sysslot ] [ ' RX_STREAM_ID ' ] :
if ' LOOPLOG ' not in self . STATUS [ _slot ] or not self . STATUS [ _slot ] [ ' LOOPLOG ' ] :
2021-04-29 11:28:57 -04:00
logger . info ( " ( %s ) OBP *LoopControl* FIRST HBP: %s , STREAM ID: %s , TG: %s , TS: %s , IGNORE THIS SOURCE " , self . _system , system , int_id ( _stream_id ) , int_id ( _dst_id ) , _sysslot )
2021-04-16 18:13:19 -04:00
self . STATUS [ _slot ] [ ' LOOPLOG ' ] = True
self . STATUS [ _slot ] [ ' LAST ' ] = pkt_time
return
else :
#if _stream_id in systems[system].STATUS and systems[system].STATUS[_stream_id]['START'] <= self.STATUS[_stream_id]['START']:
if _stream_id in systems [ system ] . STATUS and ' 1ST ' in systems [ system ] . STATUS [ _stream_id ] and systems [ system ] . STATUS [ _stream_id ] [ ' TGID ' ] == _dst_id :
if ' LOOPLOG ' not in self . STATUS [ _slot ] or not self . STATUS [ _slot ] [ ' LOOPLOG ' ] :
2021-04-29 11:28:57 -04:00
logger . info ( " ( %s ) OBP *LoopControl* FIRST OBP %s , STREAM ID: %s , TG %s , IGNORE THIS SOURCE " , self . _system , system , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 18:13:19 -04:00
self . STATUS [ _slot ] [ ' LOOPLOG ' ] = True
self . STATUS [ _slot ] [ ' LAST ' ] = pkt_time
if CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' ENHANCED_OBP ' ] and ' _bcsq ' not in self . STATUS [ _slot ] :
systems [ self . _system ] . send_bcsq ( _dst_id , _stream_id )
#logger.warning("(%s) OBP *BridgeControl* Sent BCSQ , STREAM ID: %s, TG %s",self._system, int_id(_stream_id), int_id(_dst_id))
self . STATUS [ _slot ] [ ' _bcsq ' ] = True
return
2021-04-17 10:47:53 -04:00
2021-04-16 15:48:14 -04:00
#Duplicate handling#
#Duplicate complete packet
2021-04-17 10:44:20 -04:00
if self . STATUS [ _slot ] [ ' lastData ' ] and self . STATUS [ _slot ] [ ' lastData ' ] == _data and _seq > 1 :
2021-04-16 18:13:19 -04:00
logger . warning ( " ( %s ) *PacketControl* last packet is a complete duplicate of the previous one, disgarding. Stream ID:, %s TGID: %s " , self . _system , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 15:48:14 -04:00
return
#Handle inbound duplicates
2021-04-17 10:44:20 -04:00
if _seq and _seq == self . STATUS [ _slot ] [ ' lastSeq ' ] :
logger . warning ( " ( %s ) *PacketControl* Duplicate sequence number %s , disgarding. Stream ID:, %s TGID: %s " , self . _system , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 15:48:14 -04:00
return
#Inbound out-of-order packets
2021-04-17 10:44:20 -04:00
if _seq and self . STATUS [ _slot ] [ ' lastSeq ' ] and ( _seq != 1 ) and ( _seq < self . STATUS [ _slot ] [ ' lastSeq ' ] ) :
logger . warning ( " %s ) *PacketControl* Out of order packet - last SEQ: %s , this SEQ: %s , disgarding. Stream ID:, %s TGID: %s " , self . _system , self . STATUS [ _slot ] [ ' lastSeq ' ] , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 15:48:14 -04:00
return
#Inbound missed packets
2021-04-17 10:44:20 -04:00
if _seq and self . STATUS [ _slot ] [ ' lastSeq ' ] and _seq > ( self . STATUS [ _slot ] [ ' lastSeq ' ] + 1 ) :
2021-04-27 14:45:39 -04:00
logger . warning ( " ( %s ) *PacketControl* Missed packet(s) - last SEQ: %s , this SEQ: %s . Stream ID:, %s TGID: %s " , self . _system , self . STATUS [ _slot ] [ ' lastSeq ' ] , _seq , int_id ( _stream_id ) , int_id ( _dst_id ) )
2021-04-16 15:48:14 -04:00
#Save this sequence number
2021-04-17 10:44:20 -04:00
self . STATUS [ _slot ] [ ' lastSeq ' ] = _seq
2021-04-16 15:48:14 -04:00
#Save this packet
self . STATUS [ _slot ] [ ' lastData ' ] = _data
2020-12-13 10:17:35 -05:00
2020-12-19 13:51:52 -05:00
_sysIgnore = [ ]
2020-12-05 16:03:44 -05:00
for _bridge in BRIDGES :
2020-12-13 10:17:35 -05:00
#if _bridge[0:1] != '#':
if True :
2021-01-10 14:56:59 -05:00
for _system in BRIDGES [ _bridge ] :
2021-01-10 15:48:45 -05:00
if _system [ ' SYSTEM ' ] == self . _system and _system [ ' TGID ' ] == _dst_id and _system [ ' TS ' ] == _slot and _system [ ' ACTIVE ' ] == True :
2020-12-19 13:51:52 -05:00
_sysIgnore = self . to_target ( _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data , pkt_time , dmrpkt , _bits , _bridge , _system , False , _sysIgnore )
2021-01-16 10:54:52 -05:00
2021-01-16 11:19:12 -05:00
#Send to reflector or TG too, if it exists
if _bridge [ 0 : 1 ] == ' # ' :
_bridge = _bridge [ 1 : ]
else :
_bridge = ' # ' + _bridge
if _bridge in BRIDGES :
_sysIgnore = self . to_target ( _peer_id , _rf_src , _dst_id , _seq , _slot , _call_type , _frame_type , _dtype_vseq , _stream_id , _data , pkt_time , dmrpkt , _bits , _bridge , _system , False , _sysIgnore )
2020-08-19 15:59:20 -04:00
# Final actions - Is this a voice terminator?
if ( _frame_type == HBPF_DATA_SYNC ) and ( _dtype_vseq == HBPF_SLT_VTERM ) and ( self . STATUS [ _slot ] [ ' RX_TYPE ' ] != HBPF_SLT_VTERM ) :
call_duration = pkt_time - self . STATUS [ _slot ] [ ' RX_START ' ]
logger . info ( ' ( %s ) *CALL END* STREAM ID: %s SUB: %s ( %s ) PEER: %s ( %s ) TGID %s ( %s ), TS %s , Duration: %.2f ' , \
self . _system , int_id ( _stream_id ) , get_alias ( _rf_src , subscriber_ids ) , int_id ( _rf_src ) , get_alias ( _peer_id , peer_ids ) , int_id ( _peer_id ) , get_alias ( _dst_id , talkgroup_ids ) , int_id ( _dst_id ) , _slot , call_duration )
2021-03-25 18:42:58 -04:00
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
self . _report . send_bridgeEvent ( ' GROUP VOICE,END,RX, {} , {} , {} , {} , {} , {} , {:.2f} ' . format ( self . _system , int_id ( _stream_id ) , int_id ( _peer_id ) , int_id ( _rf_src ) , _slot , int_id ( _dst_id ) , call_duration ) . encode ( encoding = ' utf-8 ' , errors = ' ignore ' ) )
2021-04-16 15:48:14 -04:00
#Reset back to False
self . STATUS [ _slot ] [ ' lastSeq ' ] = False
self . STATUS [ _slot ] [ ' lastData ' ] = False
2020-08-19 15:59:20 -04:00
#
# Begin in-band signalling for call end. This has nothign to do with routing traffic directly.
#
# Iterate the rules dictionary
for _bridge in BRIDGES :
2021-01-10 16:08:20 -05:00
if ( _bridge [ 0 : 1 ] == ' # ' ) and ( _int_dst_id != 9 ) :
continue
2020-08-19 15:59:20 -04:00
for _system in BRIDGES [ _bridge ] :
if _system [ ' SYSTEM ' ] == self . _system :
# TGID matches a rule source, reset its timer
if _slot == _system [ ' TS ' ] and _dst_id == _system [ ' TGID ' ] and ( ( _system [ ' TO_TYPE ' ] == ' ON ' and ( _system [ ' ACTIVE ' ] == True ) ) or ( _system [ ' TO_TYPE ' ] == ' OFF ' and _system [ ' ACTIVE ' ] == False ) ) :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [1] Transmission match for Bridge: %s . Reset timeout to %s ' , self . _system , _bridge , _system [ ' TIMER ' ] )
2020-08-19 15:59:20 -04:00
# TGID matches an ACTIVATION trigger
if ( _dst_id in _system [ ' ON ' ] or _dst_id in _system [ ' RESET ' ] ) and _slot == _system [ ' TS ' ] :
# Set the matching rule as ACTIVE
if _dst_id in _system [ ' ON ' ] :
if _system [ ' ACTIVE ' ] == False :
_system [ ' ACTIVE ' ] = True
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [2] Bridge: %s , connection changed to state: %s ' , self . _system , _bridge , _system [ ' ACTIVE ' ] )
2020-08-19 15:59:20 -04:00
# Cancel the timer if we've enabled an "OFF" type timeout
if _system [ ' TO_TYPE ' ] == ' OFF ' :
_system [ ' TIMER ' ] = pkt_time
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [3] Bridge: %s set to " OFF " with an on timer rule: timeout timer cancelled ' , self . _system , _bridge )
2020-08-19 15:59:20 -04:00
# Reset the timer for the rule
if _system [ ' ACTIVE ' ] == True and _system [ ' TO_TYPE ' ] == ' ON ' :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [4] Bridge: %s , timeout timer reset to: %s ' , self . _system , _bridge , _system [ ' TIMER ' ] - pkt_time )
2020-08-19 15:59:20 -04:00
# TGID matches an DE-ACTIVATION trigger
2020-08-23 09:48:13 -04:00
#Single TG mode
2021-02-28 18:04:45 -05:00
if ( CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' MODE ' ] == ' MASTER ' and CONFIG [ ' SYSTEMS ' ] [ self . _system ] [ ' SINGLE_MODE ' ] ) == True :
2020-08-26 10:57:42 -04:00
if ( _dst_id in _system [ ' OFF ' ] or _dst_id in _system [ ' RESET ' ] or _dst_id != _system [ ' TGID ' ] ) and _slot == _system [ ' TS ' ] :
#if (_dst_id in _system['OFF'] or _dst_id in _system['RESET']) and _slot == _system['TS']:
# Set the matching rule as ACTIVE
#Single TG mode
if _dst_id in _system [ ' OFF ' ] or _dst_id != _system [ ' TGID ' ] :
#if _dst_id in _system['OFF']:
if _system [ ' ACTIVE ' ] == True :
_system [ ' ACTIVE ' ] = False
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [5] Bridge: %s , connection changed to state: %s ' , self . _system , _bridge , _system [ ' ACTIVE ' ] )
2020-08-26 10:57:42 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
2020-10-23 19:04:45 -04:00
if _system [ ' TO_TYPE ' ] == ' ON ' :
_system [ ' TIMER ' ] = pkt_time
logger . info ( ' ( %s ) [6] Bridge: %s set to ON with an " OFF " timer rule: timeout timer cancelled ' , self . _system , _bridge )
2020-08-26 10:57:42 -04:00
# Reset the timer for the rule
if _system [ ' ACTIVE ' ] == False and _system [ ' TO_TYPE ' ] == ' OFF ' :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [7] Bridge: %s , timeout timer reset to: %s ' , self . _system , _bridge , _system [ ' TIMER ' ] - pkt_time )
2020-08-26 10:57:42 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
if _system [ ' ACTIVE ' ] == True and _system [ ' TO_TYPE ' ] == ' ON ' and _dst_id in _system [ ' OFF ' ] :
_system [ ' TIMER ' ] = pkt_time
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [8] Bridge: %s set to ON with and " OFF " timer rule: timeout timer cancelled ' , self . _system , _bridge )
2020-08-26 10:57:42 -04:00
else :
if ( _dst_id in _system [ ' OFF ' ] or _dst_id in _system [ ' RESET ' ] ) and _slot == _system [ ' TS ' ] :
#if (_dst_id in _system['OFF'] or _dst_id in _system['RESET']) and _slot == _system['TS']:
# Set the matching rule as ACTIVE
if _dst_id in _system [ ' OFF ' ] :
#if _dst_id in _system['OFF']:
if _system [ ' ACTIVE ' ] == True :
_system [ ' ACTIVE ' ] = False
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [9] Bridge: %s , connection changed to state: %s ' , self . _system , _bridge , _system [ ' ACTIVE ' ] )
2020-08-26 10:57:42 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
if _system [ ' TO_TYPE ' ] == ' ON ' :
_system [ ' TIMER ' ] = pkt_time
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [10] Bridge: %s set to ON with and " OFF " timer rule: timeout timer cancelled ' , self . _system , _bridge )
2020-08-26 10:57:42 -04:00
# Reset the timer for the rule
if _system [ ' ACTIVE ' ] == False and _system [ ' TO_TYPE ' ] == ' OFF ' :
_system [ ' TIMER ' ] = pkt_time + _system [ ' TIMEOUT ' ]
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [11] Bridge: %s , timeout timer reset to: %s ' , self . _system , _bridge , _system [ ' TIMER ' ] - pkt_time )
2020-08-26 10:57:42 -04:00
# Cancel the timer if we've enabled an "ON" type timeout
if _system [ ' ACTIVE ' ] == True and _system [ ' TO_TYPE ' ] == ' ON ' and _dst_id in _system [ ' OFF ' ] :
_system [ ' TIMER ' ] = pkt_time
2020-09-19 09:16:19 -04:00
logger . info ( ' ( %s ) [12] Bridge: %s set to ON with and " OFF " timer rule: timeout timer cancelled ' , self . _system , _bridge )
2021-01-27 13:47:55 -05:00
2020-08-19 15:59:20 -04:00
#
# END IN-BAND SIGNALLING
#
# Mark status variables for use later
self . STATUS [ _slot ] [ ' RX_PEER ' ] = _peer_id
self . STATUS [ _slot ] [ ' RX_SEQ ' ] = _seq
self . STATUS [ _slot ] [ ' RX_RFS ' ] = _rf_src
self . STATUS [ _slot ] [ ' RX_TYPE ' ] = _dtype_vseq
self . STATUS [ _slot ] [ ' RX_TGID ' ] = _dst_id
self . STATUS [ _slot ] [ ' RX_TIME ' ] = pkt_time
self . STATUS [ _slot ] [ ' RX_STREAM_ID ' ] = _stream_id
#
# Socket-based reporting section
#
class bridgeReportFactory ( reportFactory ) :
def send_bridge ( self ) :
serialized = pickle . dumps ( BRIDGES , protocol = 2 ) #.decode("utf-8", errors='ignore')
self . send_clients ( REPORT_OPCODES [ ' BRIDGE_SND ' ] + serialized )
def send_bridgeEvent ( self , _data ) :
if isinstance ( _data , str ) :
_data = _data . decode ( ' utf-8 ' , error = ' ignore ' )
self . send_clients ( REPORT_OPCODES [ ' BRDG_EVENT ' ] + _data )
#************************************************
# MAIN PROGRAM LOOP STARTS HERE
#************************************************
if __name__ == ' __main__ ' :
import argparse
import sys
import os
import signal
2020-09-30 15:46:35 -04:00
# Higheset peer ID permitted by HBP
PEER_MAX = 4294967295
ID_MAX = 16776415
2020-08-19 15:59:20 -04:00
2021-09-12 06:19:07 -04:00
#Set process title early
setproctitle ( __file__ )
2020-08-19 15:59:20 -04:00
# Change the current directory to the location of the application
os . chdir ( os . path . dirname ( os . path . realpath ( sys . argv [ 0 ] ) ) )
# CLI argument parser - handles picking up the config file from the command line, and sending a "help" message
parser = argparse . ArgumentParser ( )
parser . add_argument ( ' -c ' , ' --config ' , action = ' store ' , dest = ' CONFIG_FILE ' , help = ' /full/path/to/config.file (usually hblink.cfg) ' )
parser . add_argument ( ' -r ' , ' --rules ' , action = ' store ' , dest = ' RULES_FILE ' , help = ' /full/path/to/rules.file (usually rules.py) ' )
parser . add_argument ( ' -l ' , ' --logging ' , action = ' store ' , dest = ' LOG_LEVEL ' , help = ' Override config file logging level. ' )
cli_args = parser . parse_args ( )
# Ensure we have a path for the config file, if one wasn't specified, then use the default (top of file)
if not cli_args . CONFIG_FILE :
cli_args . CONFIG_FILE = os . path . dirname ( os . path . abspath ( __file__ ) ) + ' /hblink.cfg '
# Call the external routine to build the configuration dictionary
CONFIG = config . build_config ( cli_args . CONFIG_FILE )
# Ensure we have a path for the rules file, if one wasn't specified, then use the default (top of file)
if not cli_args . RULES_FILE :
cli_args . RULES_FILE = os . path . dirname ( os . path . abspath ( __file__ ) ) + ' /rules.py '
# Start the system logger
if cli_args . LOG_LEVEL :
CONFIG [ ' LOGGER ' ] [ ' LOG_LEVEL ' ] = cli_args . LOG_LEVEL
logger = log . config_logging ( CONFIG [ ' LOGGER ' ] )
2021-04-29 19:04:10 -04:00
logger . info ( ' \n \n Copyright (c) 2020, 2021 Simon G7RZU simon@gb7fr.org.uk ' )
logger . info ( ' Copyright (c) 2013, 2014, 2015, 2016, 2018, 2019 \n \t The Regents of the K0USY Group. All rights reserved. \n ' )
2020-08-19 15:59:20 -04:00
logger . debug ( ' (GLOBAL) Logging system started, anything from here on gets logged ' )
2020-09-29 09:43:17 -04:00
#If MySQL is enabled, read master config from MySQL too
if CONFIG [ ' MYSQL ' ] [ ' USE_MYSQL ' ] == True :
logger . debug ( ' (MYSQL) MySQL config enabled ' )
SQLCONFIG = { }
2021-01-22 08:09:28 -05:00
sql = useMYSQL ( CONFIG [ ' MYSQL ' ] [ ' SERVER ' ] , CONFIG [ ' MYSQL ' ] [ ' USER ' ] , CONFIG [ ' MYSQL ' ] [ ' PASS ' ] , CONFIG [ ' MYSQL ' ] [ ' DB ' ] , CONFIG [ ' MYSQL ' ] [ ' TABLE ' ] , logger )
2020-10-03 17:25:30 -04:00
#Run it once immediately
if sql . con ( ) :
logger . debug ( ' (MYSQL) reading config from database ' )
try :
SQLCONFIG = sql . getConfig ( )
2020-09-30 15:56:07 -04:00
#Add MySQL config data to config dict
2020-10-03 17:25:30 -04:00
except :
logger . debug ( ' (MYSQL) problem with SQL query, aborting ' )
sql . close ( )
logger . debug ( ' (MYSQL) building ACLs ' )
2020-10-04 10:19:46 -04:00
# Build ACLs
2020-10-03 17:25:30 -04:00
for system in SQLCONFIG :
SQLCONFIG [ system ] [ ' REG_ACL ' ] = acl_build ( SQLCONFIG [ system ] [ ' REG_ACL ' ] , PEER_MAX )
for acl in [ ' SUB_ACL ' , ' TG1_ACL ' , ' TG2_ACL ' ] :
SQLCONFIG [ system ] [ acl ] = acl_build ( SQLCONFIG [ system ] [ acl ] , ID_MAX )
CONFIG [ ' SYSTEMS ' ] . update ( SQLCONFIG )
else :
logger . debug ( ' (MYSQL) problem connecting to SQL server, aborting ' )
2020-09-29 09:43:17 -04:00
2020-08-19 15:59:20 -04:00
# Set up the signal handler
def sig_handler ( _signal , _frame ) :
logger . info ( ' (GLOBAL) SHUTDOWN: CONFBRIDGE IS TERMINATING WITH SIGNAL %s ' , str ( _signal ) )
hblink_handler ( _signal , _frame )
logger . info ( ' (GLOBAL) SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR ' )
reactor . stop ( )
# Set signal handers so that we can gracefully exit if need be
for sig in [ signal . SIGINT , signal . SIGTERM ] :
signal . signal ( sig , sig_handler )
# Create the name-number mapping dictionaries
peer_ids , subscriber_ids , talkgroup_ids = mk_aliases ( CONFIG )
# Import the ruiles file as a module, and create BRIDGES from it
spec = importlib . util . spec_from_file_location ( " module.name " , cli_args . RULES_FILE )
rules_module = importlib . util . module_from_spec ( spec )
try :
spec . loader . exec_module ( rules_module )
logger . info ( ' (ROUTER) Routing bridges file found and bridges imported: %s ' , cli_args . RULES_FILE )
except ( ImportError , FileNotFoundError ) :
sys . exit ( ' (ROUTER) TERMINATING: Routing bridges file not found or invalid: {} ' . format ( cli_args . RULES_FILE ) )
# Build the routing rules file
BRIDGES = make_bridges ( rules_module . BRIDGES )
2020-10-04 10:19:46 -04:00
2021-04-29 21:10:45 -04:00
#Generator
generator = { }
systemdelete = [ ]
for system in CONFIG [ ' SYSTEMS ' ] :
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] :
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] == ' MASTER ' and ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' GENERATOR ' ] > 1 ) :
for count in range ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' GENERATOR ' ] ) :
_systemname = system + ' - ' + str ( count )
generator [ _systemname ] = copy . deepcopy ( CONFIG [ ' SYSTEMS ' ] [ system ] )
generator [ _systemname ] [ ' PORT ' ] = generator [ _systemname ] [ ' PORT ' ] + count
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
generator [ _systemname ] [ ' _default_options ' ] = " TS1_STATIC= {} ;TS2_STATIC= {} ;SINGLE= {} ;DEFAULT_UA_TIMER= {} ;DEFAULT_REFLECTOR= {} ;VOICE= {} ;LANG= {} " . format ( generator [ _systemname ] [ ' TS1_STATIC ' ] , generator [ _systemname ] [ ' TS2_STATIC ' ] , int ( generator [ _systemname ] [ ' SINGLE_MODE ' ] ) , generator [ _systemname ] [ ' DEFAULT_UA_TIMER ' ] , generator [ _systemname ] [ ' DEFAULT_REFLECTOR ' ] , int ( generator [ _systemname ] [ ' VOICE_IDENT ' ] ) , generator [ _systemname ] [ ' ANNOUNCEMENT_LANGUAGE ' ] )
2021-04-29 21:10:45 -04:00
logger . debug ( ' (GLOBAL) Generator - generated system %s ' , _systemname )
generator [ _systemname ] [ ' _default_options ' ]
systemdelete . append ( system )
for _system in generator :
CONFIG [ ' SYSTEMS ' ] [ _system ] = generator [ _system ]
for _system in systemdelete :
CONFIG [ ' SYSTEMS ' ] . pop ( _system )
del generator
del systemdelete
2020-10-04 10:19:46 -04:00
# Default reflector
logger . debug ( ' (ROUTER) Setting default reflectors ' )
for system in CONFIG [ ' SYSTEMS ' ] :
2020-10-05 18:29:00 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' MASTER ' :
2020-10-04 10:29:10 -04:00
continue
2020-10-04 10:19:46 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] > 0 :
2020-10-06 17:20:57 -04:00
make_default_reflector ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_REFLECTOR ' ] , CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_UA_TIMER ' ] , system )
2020-10-05 18:29:00 -04:00
#static TGs
logger . debug ( ' (ROUTER) setting static TGs ' )
for system in CONFIG [ ' SYSTEMS ' ] :
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] != ' MASTER ' :
continue
2020-10-06 17:20:57 -04:00
_tmout = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' DEFAULT_UA_TIMER ' ]
2020-10-05 18:29:00 -04:00
ts1 = [ ]
ts2 = [ ]
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ] :
ts1 = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS1_STATIC ' ] . split ( ' , ' )
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ] :
ts2 = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' TS2_STATIC ' ] . split ( ' , ' )
#if CONFIG['SYSTEMS'][system]['SINGLE_MODE'] == True:
#if ts1:
#make_static_tg(int(ts1[0]),1,system)
#if ts2:
#make_static_tg(int(ts2[0]),2,system)
#else:
for tg in ts1 :
if not tg :
continue
tg = int ( tg )
2020-10-06 17:20:57 -04:00
make_static_tg ( tg , 1 , _tmout , system )
2020-10-06 07:12:49 -04:00
for tg in ts2 :
2020-10-05 18:29:00 -04:00
if not tg :
continue
tg = int ( tg )
2020-10-06 17:20:57 -04:00
make_static_tg ( tg , 2 , _tmout , system )
2020-08-19 15:59:20 -04:00
# INITIALIZE THE REPORTING LOOP
if CONFIG [ ' REPORTS ' ] [ ' REPORT ' ] :
report_server = config_reports ( CONFIG , bridgeReportFactory )
else :
report_server = None
logger . info ( ' (REPORT) TCP Socket reporting not configured ' )
2020-09-17 15:34:50 -04:00
#Read AMBE
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
AMBEobj = readAMBE ( CONFIG [ ' GLOBAL ' ] [ ' ANNOUNCEMENT_LANGUAGES ' ] , ' ./Audio/ ' )
2020-09-17 15:34:50 -04:00
#global words
words = AMBEobj . readfiles ( )
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
for lang in words . keys ( ) :
logger . info ( ' (AMBE) for language %s , read %s words into voice dict ' , lang , len ( words [ lang ] ) - 1 )
2020-08-19 15:59:20 -04:00
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
#Remap words for internationalisation
if lang in voiceMap :
logger . info ( ' (AMBE) i8n voice map entry for language %s ' , lang )
_map = voiceMap [ lang ]
for _mapword in _map :
logger . info ( ' (AMBE) Mapping \" %s \" to \" %s \" ' , _mapword , _map [ _mapword ] )
words [ lang ] [ _mapword ] = words [ lang ] [ _map [ _mapword ] ]
2021-02-14 10:42:43 -05:00
2020-08-19 15:59:20 -04:00
# HBlink instance creation
2021-02-14 10:42:43 -05:00
logger . info ( ' (GLOBAL) FreeDMR \' bridge_master.py \' -- SYSTEM STARTING... ' )
2020-09-30 15:46:35 -04:00
listeningPorts = { }
2021-04-29 21:10:45 -04:00
2021-02-21 09:33:53 -05:00
2020-08-19 15:59:20 -04:00
for system in CONFIG [ ' SYSTEMS ' ] :
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ENABLED ' ] :
2021-05-02 14:24:47 -04:00
# if CONFIG['SYSTEMS'][system]['MODE'] == 'XLXPEER':
# logger.warning('(GLOBAL) system %s not started - XLXPEER connections currently unsupported ', system)
# continue
2020-08-19 15:59:20 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] == ' OPENBRIDGE ' :
2021-02-21 09:33:53 -05:00
systems [ system ] = routerOBP ( system , CONFIG , report_server )
2020-08-19 15:59:20 -04:00
else :
2021-05-14 17:36:36 -04:00
if CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] == ' MASTER ' and CONFIG [ ' SYSTEMS ' ] [ system ] [ ' ANNOUNCEMENT_LANGUAGE ' ] not in CONFIG [ ' GLOBAL ' ] [ ' ANNOUNCEMENT_LANGUAGES ' ] . split ( ' , ' ) :
Allow loading if multiple languages and selection both via
default in config file and OPTIONS.
Remove ANNOUNCEMENT_LANGUAGE from [GLOBAL]
Replace with ANNOUNCEMENT_LANGUAGES - Comma Separated list
Current list:
ANNOUNCEMENT_LANGUAGES: en_GB,en_GB_2,en_US,es_ES,es_ES_2,fr_FR,de_DE,dk_DK,it_IT,no_NO,pl_PL,se_SE
Add:
ANNOUNCEMENT_LANGUAGE to MASTER definition. If using GENERATOR, this becomes
the template default.
To change via OPTIONS add LANG=<language code>
take codes from list above
2021-05-13 19:07:11 -04:00
logger . warning ( ' (GLOBAL) Invalid language in ANNOUNCEMENT_LANGUAGE, skipping system %s ' , system )
continue
2020-08-19 15:59:20 -04:00
systems [ system ] = routerHBP ( system , CONFIG , report_server )
2020-09-30 15:46:35 -04:00
listeningPorts [ system ] = reactor . listenUDP ( CONFIG [ ' SYSTEMS ' ] [ system ] [ ' PORT ' ] , systems [ system ] , interface = CONFIG [ ' SYSTEMS ' ] [ system ] [ ' IP ' ] )
2020-08-19 15:59:20 -04:00
logger . debug ( ' (GLOBAL) %s instance created: %s , %s ' , CONFIG [ ' SYSTEMS ' ] [ system ] [ ' MODE ' ] , system , systems [ system ] )
def loopingErrHandle ( failure ) :
logger . error ( ' (GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error in timed loop. \n %s ' , failure )
reactor . stop ( )
# Initialize the rule timer -- this if for user activated stuff
rule_timer_task = task . LoopingCall ( rule_timer_loop )
rule_timer = rule_timer_task . start ( 60 )
rule_timer . addErrback ( loopingErrHandle )
# Initialize the stream trimmer
stream_trimmer_task = task . LoopingCall ( stream_trimmer_loop )
stream_trimmer = stream_trimmer_task . start ( 5 )
stream_trimmer . addErrback ( loopingErrHandle )
2020-09-22 15:06:07 -04:00
# Ident
2020-10-02 15:47:04 -04:00
#This runs in a thread so as not to block the reactor
ident_task = task . LoopingCall ( threadIdent )
2021-05-09 11:11:55 -04:00
identa = ident_task . start ( 900 )
2020-10-03 19:01:15 -04:00
identa . addErrback ( loopingErrHandle )
2020-09-22 15:06:07 -04:00
2020-12-27 12:32:38 -05:00
#Options parsing
options_task = task . LoopingCall ( options_config )
options = options_task . start ( 30 )
options . addErrback ( loopingErrHandle )
2020-09-30 15:46:35 -04:00
#Mysql config checker
2020-10-02 15:47:04 -04:00
#This runs in a thread so as not to block the reactor
2020-09-30 15:46:35 -04:00
if CONFIG [ ' MYSQL ' ] [ ' USE_MYSQL ' ] == True :
2020-10-02 15:47:04 -04:00
mysql_task = task . LoopingCall ( threadedMysql )
2020-12-27 12:32:38 -05:00
mysql = mysql_task . start ( 30 )
2020-09-30 15:46:35 -04:00
mysql . addErrback ( loopingErrHandle )
2021-01-10 08:50:37 -05:00
2021-01-16 07:13:47 -05:00
#STAT trimmer - once every hour
2021-01-10 08:50:37 -05:00
if CONFIG [ ' GLOBAL ' ] [ ' GEN_STAT_BRIDGES ' ] :
stat_trimmer_task = task . LoopingCall ( statTrimmer )
2021-01-16 08:05:53 -05:00
stat_trimmer = stat_trimmer_task . start ( 3600 ) #3600
2021-01-10 08:50:37 -05:00
stat_trimmer . addErrback ( loopingErrHandle )
2021-04-20 20:37:07 -04:00
#KA Reporting
ka_task = task . LoopingCall ( kaReporting )
ka = ka_task . start ( 60 )
ka . addErrback ( loopingErrHandle )
2020-09-30 15:46:35 -04:00
2020-12-03 15:28:23 -05:00
#more threads
2021-02-28 07:05:03 -05:00
reactor . suggestThreadPoolSize ( 100 )
2020-12-03 15:28:23 -05:00
2020-08-19 15:59:20 -04:00
reactor . run ( )