ALL ARE WORKING EXCEPT ambe_audio.py
This commit is contained in:
parent
9c6de3b902
commit
53c4ff5b23
|
@ -12,8 +12,8 @@ gateway = 127.0.0.1 # IP address of DMRGateway app
|
|||
toGatewayPort = 31000 # Port DMRGateway is listening on for AMBE frames to decode
|
||||
remoteControlPort = 31002 # Port that ambe_audio is listening on for remote control commands
|
||||
fromGatewayPort = 31003 # Port to listen on for AMBE frames to transmit to all peers
|
||||
gatewayDmrId = 0 # id to use when transmitting from the gateway
|
||||
tgFilter = 9 # A list of TG IDs to monitor. All TGs will be passed to DMRGateway
|
||||
gatewayDmrId = 1312000 # id to use when transmitting from the gateway
|
||||
tgFilter = 9 # A list of TG IDs to monitor. All TGs will be passed to DMRGateway
|
||||
txTg = 9 # TG to use for all frames received from DMRGateway -> IPSC
|
||||
txTs = 2 # Slot to use for frames received from DMRGateway -> IPSC
|
||||
#
|
||||
|
|
135
ambe_audio.py
135
ambe_audio.py
|
@ -28,7 +28,10 @@ from bitstring import BitArray
|
|||
|
||||
import sys, socket, ConfigParser, thread, traceback
|
||||
import cPickle as pickle
|
||||
from dmrlink import IPSC, NETWORK, networks, logger, int_id, hex_str_3, hex_str_4, get_info, talkgroup_ids, peer_ids, PATH, get_subscriber_info, reread_subscribers
|
||||
|
||||
from dmrlink import IPSC, systems
|
||||
from dmr_utils.utils import int_id, hex_str_3, hex_str_4, get_alias
|
||||
|
||||
from time import time, sleep, clock, localtime, strftime
|
||||
import csv
|
||||
import struct
|
||||
|
@ -90,8 +93,8 @@ class ambeIPSC(IPSC):
|
|||
#_d = None
|
||||
###### DEBUGDEBUGDEBUG
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.CALL_DATA = []
|
||||
|
||||
#
|
||||
|
@ -99,7 +102,7 @@ class ambeIPSC(IPSC):
|
|||
#
|
||||
|
||||
self._currentTG = self._no_tg
|
||||
self._currentNetwork = str(args[0])
|
||||
self._currentNetwork = str(_name)
|
||||
self.readConfigFile(self._configFile, None, self._currentNetwork)
|
||||
|
||||
logger.info('DMRLink ambe server')
|
||||
|
@ -121,7 +124,7 @@ class ambeIPSC(IPSC):
|
|||
|
||||
try:
|
||||
thread.start_new_thread( self.remote_control, (self._remote_control_port, ) ) # Listen for remote control commands
|
||||
thread.start_new_thread( self.launchUDP, (args[0], ) ) # Package AMBE into IPSC frames and send to all peers
|
||||
thread.start_new_thread( self.launchUDP, (_name, ) ) # Package AMBE into IPSC frames and send to all peers
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logger.error( "Error: unable to start thread" )
|
||||
|
@ -178,7 +181,7 @@ class ambeIPSC(IPSC):
|
|||
traceback.print_exc()
|
||||
sys.exit('Configuration file \''+configFileName+'\' is not a valid configuration file! Exiting...')
|
||||
|
||||
def rewriteFrame( self, _frame, _network, _newSlot, _newGroup, _newSouceID, _newPeerID ):
|
||||
def rewriteFrame( self, _frame, _newSlot, _newGroup, _newSouceID, _newPeerID ):
|
||||
|
||||
_peerid = _frame[1:5] # int32 peer who is sending us a packet
|
||||
_src_sub = _frame[6:9] # int32 Id of source
|
||||
|
@ -266,7 +269,7 @@ class ambeIPSC(IPSC):
|
|||
return _ambeAll.tobytes() # Return the 49 * 3 as an array of bytes
|
||||
|
||||
# Set up the socket and run the method to gather the AMBE. Sending it to all peers
|
||||
def launchUDP(self, _network):
|
||||
def launchUDP(self):
|
||||
s = socket.socket() # Create a socket object
|
||||
s.bind(('', self._ambeRxPort)) # Bind to the port
|
||||
|
||||
|
@ -274,16 +277,16 @@ class ambeIPSC(IPSC):
|
|||
s.listen(5) # Now wait for client connection.
|
||||
_sock, addr = s.accept() # Establish connection with client.
|
||||
if int_id(self._tx_tg) > 0: # Test if we are allowed to transmit
|
||||
self.playbackFromUDP(_sock, _network)
|
||||
self.playbackFromUDP(_sock, self._system)
|
||||
else:
|
||||
self.transmitDisabled(_sock, _network) #tg is zero, so just eat the network trafic
|
||||
self.transmitDisabled(_sock, self._system) #tg is zero, so just eat the network trafic
|
||||
_sock.close()
|
||||
|
||||
# This represents a full transmission (HEAD, VOICE and TERM)
|
||||
def playbackFromUDP(self, _sock, _network):
|
||||
def playbackFromUDP(self, _sock):
|
||||
_delay = 0.055 # Yes, I know it should be 0.06, but there seems to be some latency, so this is a hack
|
||||
_src_sub = hex_str_3(self._gateway_dmr_id) # DMR ID to sign this transmission with
|
||||
_src_peer = NETWORK[_network]['LOCAL']['RADIO_ID'] # Use this peers ID as the source repeater
|
||||
_src_peer = NETWORK[self._system]['LOCAL']['RADIO_ID'] # Use this peers ID as the source repeater
|
||||
|
||||
logger.info('Transmit from gateway to TG {}:'.format(int_id(self._tx_tg)) )
|
||||
try:
|
||||
|
@ -310,8 +313,8 @@ class ambeIPSC(IPSC):
|
|||
self._seq = randint(0,32767) # A transmission uses a random number to begin its sequence (16 bit)
|
||||
|
||||
for i in range(0, 3): # Output the 3 HEAD frames to our peers
|
||||
self.rewriteFrame(_tempHead[i], _network, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(_network, _src_sub, self._tx_tg, True, '', hex_str_3(0), _tempHead[i])
|
||||
self.rewriteFrame(_tempHead[i], self._system, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(self._system, _src_sub, self._tx_tg, True, '', hex_str_3(0), _tempHead[i])
|
||||
sleep(_delay)
|
||||
|
||||
i = 0 # Initialize the VOICE template index
|
||||
|
@ -321,21 +324,21 @@ class ambeIPSC(IPSC):
|
|||
i = (i + 1) % 6 # Round robbin with the 6 VOICE templates
|
||||
_frame = _tempVoice[i][:33] + _ambe + _tempVoice[i][52:] # Insert the 3 49 bit AMBE frames
|
||||
|
||||
self.rewriteFrame(_frame, _network, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(_network, _src_sub, self._tx_tg, True, '', hex_str_3(0), _frame)
|
||||
self.rewriteFrame(_frame, self._system, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(self._system, _src_sub, self._tx_tg, True, '', hex_str_3(0), _frame)
|
||||
|
||||
sleep(_delay) # Since this comes from a file we have to add delay between IPSC frames
|
||||
else:
|
||||
_eof = True # There are no more AMBE frames, so terminate the loop
|
||||
|
||||
self.rewriteFrame(_tempTerm, _network, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(_network, _src_sub, self._tx_tg, True, '', hex_str_3(0), _tempTerm)
|
||||
self.rewriteFrame(_tempTerm, self._system, self._tx_ts, self._tx_tg, _src_sub, _src_peer)
|
||||
#self.group_voice(self._system, _src_sub, self._tx_tg, True, '', hex_str_3(0), _tempTerm)
|
||||
|
||||
except IOError:
|
||||
logger.error('Can not transmit to peers')
|
||||
logger.info('Transmit complete')
|
||||
|
||||
def transmitDisabled(self, _sock, _network):
|
||||
def transmitDisabled(self, _sock):
|
||||
_eof = False
|
||||
logger.debug('Transmit disabled begin')
|
||||
while(_eof == False):
|
||||
|
@ -386,20 +389,19 @@ class ambeIPSC(IPSC):
|
|||
#************************************************
|
||||
#
|
||||
|
||||
def group_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def group_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
|
||||
#self.dumpIPSCFrame(_data)
|
||||
|
||||
# THIS FUNCTION IS NOT COMPLETE!!!!
|
||||
_payload_type = _data[30:31]
|
||||
# _ambe_frames = _data[33:52]
|
||||
_ambe_frames = BitArray('0x'+h(_data[33:52]))
|
||||
_ambe_frames = BitArray('0x'+h(_data[33:52]))
|
||||
_ambe_frame1 = _ambe_frames[0:49]
|
||||
_ambe_frame2 = _ambe_frames[50:99]
|
||||
_ambe_frame3 = _ambe_frames[100:149]
|
||||
|
||||
_tg_id = int_id(_dst_sub)
|
||||
_ts = 2 if _ts else 1
|
||||
|
||||
self._busy_slots[_ts] = time()
|
||||
|
||||
|
@ -409,12 +411,12 @@ class ambeIPSC(IPSC):
|
|||
# self._d.write(struct.pack("i", __iLen))
|
||||
# self._d.write(_data)
|
||||
# else:
|
||||
# self.rewriteFrame(_data, _network, 1, 9)
|
||||
# self.rewriteFrame(_data, self._system, 1, 9)
|
||||
###### DEBUGDEBUGDEBUG
|
||||
|
||||
|
||||
if _tg_id in self._tg_filter: #All TGs
|
||||
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||
_dst_sub = get_alias(_dst_sub, talkgroup_ids)
|
||||
if _payload_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
if self._currentTG == self._no_tg:
|
||||
_src_sub = get_subscriber_info(_src_sub)
|
||||
|
@ -459,7 +461,7 @@ class ambeIPSC(IPSC):
|
|||
|
||||
else:
|
||||
if _payload_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||
_dst_sub = get_alias(_dst_sub, talkgroup_ids)
|
||||
logger.warning('Ignored Voice Transmission Start on TS {} and TG {}'.format(_ts, _dst_sub))
|
||||
|
||||
def outputFrames(self, _ambe_frames, _ambe_frame1, _ambe_frame2, _ambe_frame3):
|
||||
|
@ -479,7 +481,7 @@ class ambeIPSC(IPSC):
|
|||
self._sock.sendto(_ambe_frame2.tobytes(), (self._gateway, self._gateway_port))
|
||||
self._sock.sendto(_ambe_frame3.tobytes(), (self._gateway, self._gateway_port))
|
||||
|
||||
def private_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def private_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
print('private voice')
|
||||
# __iLen = len(_data)
|
||||
# self._d.write(struct.pack("i", __iLen))
|
||||
|
@ -528,7 +530,7 @@ class ambeIPSC(IPSC):
|
|||
print('New gateway_dmr_id = ' + str(self._gateway_dmr_id))
|
||||
elif _cmd == 'gateway_peer_id':
|
||||
peerID = int(_tmp.split('=')[1])
|
||||
NETWORK[_network]['LOCAL']['RADIO_ID'] = hex_str_3(peerID)
|
||||
self._config['LOCAL']['RADIO_ID'] = hex_str_3(peerID)
|
||||
print('New peer_id = ' + str(peerID))
|
||||
elif _cmd == 'restart':
|
||||
reactor.callFromThread(reactor.stop)
|
||||
|
@ -540,9 +542,9 @@ class ambeIPSC(IPSC):
|
|||
logger.info( 'New TGs={}'.format(self._tg_filter) )
|
||||
elif _cmd == 'dump_template':
|
||||
self.dumpTemplate('PrivateVoice.bin')
|
||||
elif _cmd == 'get_info':
|
||||
elif _cmd == 'get_alias':
|
||||
self._sock.sendto('reply dmr_info {} {} {} {}'.format(self._currentNetwork,
|
||||
int_id(NETWORK[self._currentNetwork]['LOCAL']['RADIO_ID']),
|
||||
int_id(self._CONFIG[self._currentNetwork]['LOCAL']['RADIO_ID']),
|
||||
self._gateway_dmr_id,
|
||||
get_subscriber_info(hex_str_3(self._gateway_dmr_id))), (self._dmrgui, 34003))
|
||||
elif _cmd == 'eval':
|
||||
|
@ -614,11 +616,80 @@ class ambeIPSC(IPSC):
|
|||
_ambe = _frame[33:52]
|
||||
print('SLOT2:', h(_frame))
|
||||
print("pt={:02X} pid={} seq={:02X} src={} dst={} ct={:02X} uk={} ci={} rsq={}".format(_packettype, _peerid,_ipsc_seq, _src_sub,_dst_sub,_call_type,_call_ctrl_info,_call_info,_rtp_seq))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'ambe_audio.py\' (c) 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = ambeIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
|
||||
# ID ALIAS CREATION
|
||||
# Download
|
||||
if CONFIG['ALIASES']['TRY_DOWNLOAD'] == True:
|
||||
# Try updating peer aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'], CONFIG['ALIASES']['PEER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
# Try updating subscriber aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'], CONFIG['ALIASES']['SUBSCRIBER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
|
||||
# Make Dictionaries
|
||||
peer_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'])
|
||||
if peer_ids:
|
||||
logger.info('ID ALIAS MAPPER: peer_ids dictionary is available')
|
||||
|
||||
subscriber_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'])
|
||||
if subscriber_ids:
|
||||
logger.info('ID ALIAS MAPPER: subscriber_ids dictionary is available')
|
||||
|
||||
talkgroup_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['TGID_FILE'])
|
||||
if talkgroup_ids:
|
||||
logger.info('ID ALIAS MAPPER: talkgroup_ids dictionary is available')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = ambeIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
||||
|
|
108
bridge.py
108
bridge.py
|
@ -114,10 +114,11 @@ def build_bridges(_known_bridges):
|
|||
try:
|
||||
bridges_file = import_module(_known_bridges)
|
||||
logger.info('Known bridges file found and bridge ID list imported ')
|
||||
return bridges_file.BRIDGES
|
||||
except ImportError:
|
||||
logger.critical('\'known_bridges.py\' not found - backup bridge service will not be enabled')
|
||||
bridges_file = []
|
||||
return bridges_file
|
||||
return []
|
||||
|
||||
|
||||
# Import subscriber ACL
|
||||
# ACL may be a single list of subscriber IDs
|
||||
|
@ -180,16 +181,17 @@ def rule_timer_loop():
|
|||
else:
|
||||
logger.debug('Rule timer loop made no rule changes')
|
||||
|
||||
|
||||
class bridgeIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger, _bridges):
|
||||
IPSC.__init__(self, _name, _config, _logger, _bridges)
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.BRIDGES = _bridges
|
||||
if self.BRIDGES:
|
||||
logger.info('Initializing backup/polite bridging')
|
||||
self._logger.info('(%s) Initializing backup/polite bridging', self._system)
|
||||
self.BRIDGE = False
|
||||
else:
|
||||
self.BRIDGE = True
|
||||
logger.info('Initializing standard bridging')
|
||||
self._logger.info('Initializing standard bridging')
|
||||
|
||||
self.IPSC_STATUS = {
|
||||
1: {'RX_GROUP':'\x00', 'TX_GROUP':'\x00', 'RX_TIME':0, 'TX_TIME':0, 'RX_SRC_SUB':'\x00', 'TX_SRC_SUB':'\x00'},
|
||||
|
@ -199,33 +201,34 @@ class bridgeIPSC(IPSC):
|
|||
self.last_seq_id = '\x00'
|
||||
self.call_start = 0
|
||||
|
||||
# Setup the backup/polite bridging maintenance loop (based on keep-alive timer)
|
||||
# Setup the backup/polite bridging maintenance loop (based on keep-alive timer)
|
||||
|
||||
|
||||
def startProtocol(self):
|
||||
IPSC.startProtocol(self)
|
||||
if self.BRIDGES:
|
||||
def startProtocol(self):
|
||||
IPSC.startProtocol(self)
|
||||
|
||||
self._bridge_presence = task.LoopingCall(self.bridge_presence_loop)
|
||||
self._bridge_presence_loop = self._bridge_presence.start(self._local['ALIVE_TIMER'])
|
||||
self._bridge_presence = task.LoopingCall(self.bridge_presence_loop)
|
||||
self._bridge_presence_loop = self._bridge_presence.start(self._local['ALIVE_TIMER'])
|
||||
|
||||
# This is the backup/polite bridge maintenance loop
|
||||
def bridge_presence_loop(self):
|
||||
self._logger.debug('(%s) Bridge presence loop initiated', self._system)
|
||||
_temp_bridge = True
|
||||
for peer in BRIDGES:
|
||||
for peer in self.BRIDGES:
|
||||
_peer = hex_str_4(peer)
|
||||
|
||||
if _peer in self._peers.keys() and (self._peers[_peer]['MODE_DECODE']['TS_1'] or self._peers[_peer]['MODE_DECODE']['TS_2']):
|
||||
_temp_bridge = False
|
||||
logger.debug('(%s) Peer %s is an active bridge', self._network, int_id(_peer))
|
||||
self._logger.debug('(%s) Peer %s is an active bridge', self._system, int_id(_peer))
|
||||
|
||||
if _peer == self._master['RADIO_ID'] \
|
||||
and self._master['STATUS']['CONNECTED'] \
|
||||
and (self._master['MODE_DECODE']['TS_1'] or self._master['MODE_DECODE']['TS_2']):
|
||||
_temp_bridge = False
|
||||
logger.debug('(%s) Master %s is an active bridge',self._network, int_id(_peer))
|
||||
self._logger.debug('(%s) Master %s is an active bridge',self._system, int_id(_peer))
|
||||
|
||||
if self.BRIDGE != _temp_bridge:
|
||||
logger.info('(%s) Changing bridge status to: %s', self._network, _temp_bridge )
|
||||
self._logger.info('(%s) Changing bridge status to: %s', self._system, _temp_bridge )
|
||||
self.BRIDGE = _temp_bridge
|
||||
|
||||
|
||||
|
@ -233,24 +236,23 @@ class bridgeIPSC(IPSC):
|
|||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
#
|
||||
def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
|
||||
def group_voice(self, _src_sub, _dst_group, _ts, _end, _peerid, _data):
|
||||
# Check for ACL match, and return if the subscriber is not allowed
|
||||
if allow_sub(_src_sub) == False:
|
||||
logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
|
||||
self._logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
|
||||
return
|
||||
|
||||
# Process the packet
|
||||
logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
|
||||
self._logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
|
||||
_burst_data_type = _data[30] # Determine the type of voice packet this is (see top of file for possible types)
|
||||
_seq_id = _data[5]
|
||||
_ts += 1
|
||||
|
||||
now = time() # Mark packet arrival time -- we'll need this for call contention handling
|
||||
|
||||
for rule in RULES[_network]['GROUP_VOICE']:
|
||||
for rule in RULES[self._system]['GROUP_VOICE']:
|
||||
_target = rule['DST_NET'] # Shorthand to reduce length and make it easier to read
|
||||
_status = systems[_target].IPSC_STATUS # Shorthand to reduce length and make it easier to read
|
||||
|
||||
|
||||
# This is the primary rule match to determine if the call will be routed.
|
||||
if (rule['SRC_GROUP'] == _dst_group and rule['SRC_TS'] == _ts and rule['ACTIVE'] == True) and (self.BRIDGE == True or systems[_target].BRIDGE == True):
|
||||
|
||||
|
@ -258,7 +260,7 @@ class bridgeIPSC(IPSC):
|
|||
# BEGIN CONTENTION HANDLING
|
||||
#
|
||||
# If this is an inter-DMRlink trunk, this isn't necessary
|
||||
if RULES[_network]['TRUNK'] == False:
|
||||
if RULES[self._system]['TRUNK'] == False:
|
||||
|
||||
# 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 IPSC, but it has been less than Group Hangtime
|
||||
|
@ -267,27 +269,26 @@ class bridgeIPSC(IPSC):
|
|||
# From the same group as the last TX to this IPSC, but from a different subscriber, and it has been less than TS Clear Time
|
||||
# The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
|
||||
#
|
||||
if ((rule['DST_GROUP'] != _status[rule['DST_TS']]['RX_GROUP']) and ((now - _status[rule['DST_TS']]['RX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
|
||||
if ((rule['DST_GROUP'] != _status[rule['DST_TS']]['RX_GROUP']) and ((now - _status[rule['DST_TS']]['RX_TIME']) < RULES[self._system]['GROUP_HANGTIME'])):
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
logger.info('(%s) Call not bridged to TGID%s, target active or in group hangtime: IPSC: %s, TS: %s, TGID: %s', _network, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['RX_GROUP']))
|
||||
self._logger.info('(%s) Call not bridged to TGID%s, target active or in group hangtime: IPSC: %s, TS: %s, TGID: %s', self._system, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['RX_GROUP']))
|
||||
continue
|
||||
if ((rule['DST_GROUP'] != _status[rule['DST_TS']]['TX_GROUP']) and ((now - _status[rule['DST_TS']]['TX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
|
||||
if ((rule['DST_GROUP'] != _status[rule['DST_TS']]['TX_GROUP']) and ((now - _status[rule['DST_TS']]['TX_TIME']) < RULES[self._system]['GROUP_HANGTIME'])):
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
logger.info('(%s) Call not bridged to TGID%s, target in group hangtime: IPSC: %s, TS: %s, TGID: %s', _network, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['TX_GROUP']))
|
||||
self._logger.info('(%s) Call not bridged to TGID%s, target in group hangtime: IPSC: %s, TS: %s, TGID: %s', self._system, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['TX_GROUP']))
|
||||
continue
|
||||
if (rule['DST_GROUP'] == _status[rule['DST_TS']]['RX_GROUP']) and ((now - _status[rule['DST_TS']]['RX_TIME']) < TS_CLEAR_TIME):
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
logger.info('(%s) Call not bridged to TGID%s, matching call already active on target: IPSC: %s, TS: %s, TGID: %s', _network, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['RX_GROUP']))
|
||||
self._logger.info('(%s) Call not bridged to TGID%s, matching call already active on target: IPSC: %s, TS: %s, TGID: %s', self._system, int_id(rule['DST_GROUP']), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['RX_GROUP']))
|
||||
continue
|
||||
if (rule['DST_GROUP'] == _status[rule['DST_TS']]['TX_GROUP']) and (_src_sub != _status[rule['DST_TS']]['TX_SRC_SUB']) and ((now - _status[rule['DST_TS']]['TX_TIME']) < TS_CLEAR_TIME):
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
logger.info('(%s) Call not bridged for subscriber %s, call bridge in progress on target: IPSC: %s, TS: %s, TGID: %s SUB: %s', _network, int_id(_src_sub), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['TX_GROUP']), int_id(_status[rule['DST_TS']]['TX_SRC_SUB']))
|
||||
self._logger.info('(%s) Call not bridged for subscriber %s, call bridge in progress on target: IPSC: %s, TS: %s, TGID: %s SUB: %s', self._system, int_id(_src_sub), _target, rule['DST_TS'], int_id(_status[rule['DST_TS']]['TX_GROUP']), int_id(_status[rule['DST_TS']]['TX_SRC_SUB']))
|
||||
continue
|
||||
#
|
||||
# END CONTENTION HANDLING
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# BEGIN FRAME FORWARDING
|
||||
#
|
||||
|
@ -295,7 +296,7 @@ class bridgeIPSC(IPSC):
|
|||
_tmp_data = _data
|
||||
|
||||
# Re-Write the IPSC SRC to match the target network's ID
|
||||
_tmp_data = _tmp_data.replace(_peerid, NETWORK[_target]['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._CONFIG['SYSTEMS'][_target]['LOCAL']['RADIO_ID'])
|
||||
|
||||
# Re-Write the destination Group ID
|
||||
_tmp_data = _tmp_data.replace(_dst_group, rule['DST_GROUP'])
|
||||
|
@ -352,91 +353,90 @@ class bridgeIPSC(IPSC):
|
|||
if self.last_seq_id != _seq_id:
|
||||
self.last_seq_id = _seq_id
|
||||
self.call_start = time()
|
||||
logger.info('(%s) GROUP VOICE START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s', _network, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group))
|
||||
self._logger.info('(%s) GROUP VOICE START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s', self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group))
|
||||
|
||||
# Action happens on un-key
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_TERM']:
|
||||
if self.last_seq_id == _seq_id:
|
||||
self.call_duration = time() - self.call_start
|
||||
logger.info('(%s) GROUP VOICE END: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s Duration: %.2fs', _network, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group), self.call_duration)
|
||||
self._logger.info('(%s) GROUP VOICE END: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s Duration: %.2fs', self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group), self.call_duration)
|
||||
else:
|
||||
logger.warning('(%s) GROUP VOICE END WITHOUT MATCHING START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s', _network, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group),)
|
||||
self._logger.warning('(%s) GROUP VOICE END WITHOUT MATCHING START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s', self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group),)
|
||||
|
||||
# Iterate the rules dictionary
|
||||
for rule in RULES[_network]['GROUP_VOICE']:
|
||||
for rule in RULES[self._system]['GROUP_VOICE']:
|
||||
_target = rule['DST_NET']
|
||||
|
||||
# TGID matches a rule source, reset its timer
|
||||
if _ts == rule['SRC_TS'] and _dst_group == rule['SRC_GROUP'] and ((rule['TO_TYPE'] == 'ON' and (rule['ACTIVE'] == True)) or (rule['TO_TYPE'] == 'OFF' and rule['ACTIVE'] == False)):
|
||||
rule['TIMER'] = now + rule['TIMEOUT']
|
||||
logger.info('(%s) Source group transmission match for rule \"%s\". Reset timeout to %s', _network, rule['NAME'], rule['TIMER'])
|
||||
self._logger.info('(%s) Source group transmission match for rule \"%s\". Reset timeout to %s', self._system, rule['NAME'], rule['TIMER'])
|
||||
|
||||
# Scan for reciprocal rules and reset their timers as well.
|
||||
for target_rule in RULES[_target]['GROUP_VOICE']:
|
||||
if target_rule['NAME'] == rule['NAME']:
|
||||
target_rule['TIMER'] = now + target_rule['TIMEOUT']
|
||||
logger.info('(%s) Reciprocal group transmission match for rule \"%s\" on IPSC \"%s\". Reset timeout to %s', _network, target_rule['NAME'], _target, rule['TIMER'])
|
||||
self._logger.info('(%s) Reciprocal group transmission match for rule \"%s\" on IPSC \"%s\". Reset timeout to %s', self._system, target_rule['NAME'], _target, rule['TIMER'])
|
||||
|
||||
# TGID matches an ACTIVATION trigger
|
||||
if _dst_group in rule['ON']:
|
||||
# Set the matching rule as ACTIVE
|
||||
rule['ACTIVE'] = True
|
||||
rule['TIMER'] = now + rule['TIMEOUT']
|
||||
logger.info('(%s) Primary Bridge Rule \"%s\" changed to state: %s', _network, rule['NAME'], rule['ACTIVE'])
|
||||
self._logger.info('(%s) Primary Bridge Rule \"%s\" changed to state: %s', self._system, rule['NAME'], rule['ACTIVE'])
|
||||
|
||||
# Set reciprocal rules for other IPSCs as ACTIVE
|
||||
for target_rule in RULES[_target]['GROUP_VOICE']:
|
||||
if target_rule['NAME'] == rule['NAME']:
|
||||
target_rule['ACTIVE'] = True
|
||||
target_rule['TIMER'] = now + target_rule['TIMEOUT']
|
||||
logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', _network, target_rule['NAME'], _target, rule['ACTIVE'])
|
||||
self._logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', self._system, target_rule['NAME'], _target, rule['ACTIVE'])
|
||||
|
||||
# TGID matches an DE-ACTIVATION trigger
|
||||
if _dst_group in rule['OFF']:
|
||||
# Set the matching rule as ACTIVE
|
||||
rule['ACTIVE'] = False
|
||||
logger.info('(%s) Bridge Rule \"%s\" changed to state: %s', _network, rule['NAME'], rule['ACTIVE'])
|
||||
self._logger.info('(%s) Bridge Rule \"%s\" changed to state: %s', self._system, rule['NAME'], rule['ACTIVE'])
|
||||
|
||||
# Set reciprocal rules for other IPSCs as ACTIVE
|
||||
_target = rule['DST_NET']
|
||||
for target_rule in RULES[_target]['GROUP_VOICE']:
|
||||
if target_rule['NAME'] == rule['NAME']:
|
||||
target_rule['ACTIVE'] = False
|
||||
logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', _network, target_rule['NAME'], _target, rule['ACTIVE'])
|
||||
self._logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', self._system, target_rule['NAME'], _target, rule['ACTIVE'])
|
||||
#
|
||||
# END IN-BAND SIGNALLING
|
||||
#
|
||||
|
||||
|
||||
def group_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Group Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def group_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Group Data Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
for target in RULES[_network]['GROUP_DATA']:
|
||||
for target in RULES[self._system]['GROUP_DATA']:
|
||||
|
||||
if self.BRIDGE == True or systems[target].BRIDGE == True:
|
||||
_tmp_data = _data
|
||||
# Re-Write the IPSC SRC to match the target network's ID
|
||||
_tmp_data = _tmp_data.replace(_peerid, NETWORK[target]['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._CONFIG[target]['LOCAL']['RADIO_ID'])
|
||||
|
||||
# Send the packet to all peers in the target IPSC
|
||||
systems[target].send_to_ipsc(_tmp_data)
|
||||
|
||||
def private_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def private_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
for target in RULES[_network]['PRIVATE_DATA']:
|
||||
for target in RULES[self._system]['PRIVATE_DATA']:
|
||||
|
||||
if self.BRIDGE == True or systems[target].BRIDGE == True:
|
||||
_tmp_data = _data
|
||||
# Re-Write the IPSC SRC to match the target network's ID
|
||||
_tmp_data = _tmp_data.replace(_peerid, NETWORK[target]['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._CONFIG[target]['LOCAL']['RADIO_ID'])
|
||||
|
||||
# Send the packet to all peers in the target IPSC
|
||||
systems[target].send_to_ipsc(_tmp_data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import signal
|
||||
|
@ -486,7 +486,7 @@ if __name__ == '__main__':
|
|||
|
||||
# Build list of known bridge IDs
|
||||
BRIDGES = build_bridges('known_bridges')
|
||||
|
||||
|
||||
# Build the Access Control List
|
||||
ACL = build_acl('sub_acl')
|
||||
|
||||
|
@ -494,13 +494,13 @@ if __name__ == '__main__':
|
|||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = IPSC(system, CONFIG, logger)
|
||||
systems[system] = bridgeIPSC(system, CONFIG, logger, BRIDGES)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP IF CONFIGURED
|
||||
if CONFIG['REPORTS']['REPORT_NETWORKS']:
|
||||
config_reporting_loop(CONFIG['REPORTS']['REPORT_NETWORKS'])
|
||||
reporting = task.LoopingCall(reporting_loop)
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP IF CONFIGURED
|
||||
|
|
449
dmrlink.py
449
dmrlink.py
|
@ -65,64 +65,32 @@ __email__ = 'n0mjs@me.com'
|
|||
# Global variables used whether we are a module or __main__
|
||||
systems = {}
|
||||
|
||||
|
||||
# Utility functions
|
||||
def config_reporting_loop(_type):
|
||||
# Timed loop used for reporting IPSC status
|
||||
#
|
||||
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
|
||||
global reporting_loop
|
||||
if _type == 'PICKLE':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PICKLE)')
|
||||
# Timed loop used for reporting IPSC status
|
||||
#
|
||||
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
|
||||
def config_reports(_config):
|
||||
if _config['REPORTS']['REPORT_NETWORKS'] == 'PICKLE':
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (PICKLE)')
|
||||
try:
|
||||
with open(CONFIG['REPORTS']['REPORT_PATH']+'dmrlink_stats.pickle', 'wb') as file:
|
||||
pickle_dump(CONFIG['SYSTEMS'], file, 2)
|
||||
with open(_config['REPORTS']['REPORT_PATH']+'dmrlink_stats.pickle', 'wb') as file:
|
||||
pickle_dump(_config['SYSTEMS'], file, 2)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
logger.error('I/O Error: %s', detail)
|
||||
_logger.error('I/O Error: %s', detail)
|
||||
|
||||
elif _type == 'PRINT':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PRINT)')
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
print_master(system)
|
||||
print_peer_list(system)
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'PRINT':
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (PRINT)')
|
||||
for system in _config['SYSTEMS']:
|
||||
print_master(_config, system)
|
||||
print_peer_list(_config, system)
|
||||
|
||||
else:
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
|
||||
# Determine if the provided peer ID is valid for the provided network
|
||||
#
|
||||
def valid_peer(_peer_list, _peerid):
|
||||
if _peerid in _peer_list:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Determine if the provided master ID is valid for the provided network
|
||||
#
|
||||
def valid_master(_network, _peerid):
|
||||
if CONFIG['SYSTEMS'][_network]['MASTER']['RADIO_ID'] == _peerid:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# De-register a peer from an IPSC by removing it's information
|
||||
#
|
||||
def de_register_peer(_network, _peerid):
|
||||
# Iterate for the peer in our data
|
||||
if _peerid in CONFIG['SYSTEMS'][_network]['PEERS'].keys():
|
||||
del CONFIG['SYSTEMS'][_network]['PEERS'][_peerid]
|
||||
logger.info('(%s) Peer De-Registration Requested for: %s', _network, int_id(_peerid))
|
||||
return
|
||||
else:
|
||||
logger.warning('(%s) Peer De-Registration Requested for: %s, but we don\'t have a listing for this peer', _network, int_id(_peerid))
|
||||
pass
|
||||
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
return reporting_loop
|
||||
|
||||
# Process the MODE byte in registration/peer list packets for determining master and peer capabilities
|
||||
#
|
||||
|
@ -153,7 +121,6 @@ def process_mode_byte(_hex_mode):
|
|||
'TS_2': _ts2
|
||||
}
|
||||
|
||||
|
||||
# Process the FLAGS bytes in registration replies for determining what services are available
|
||||
#
|
||||
def process_flags_bytes(_hex_flags):
|
||||
|
@ -184,73 +151,6 @@ def process_flags_bytes(_hex_flags):
|
|||
'MASTER': _master
|
||||
}
|
||||
|
||||
|
||||
# Take a received peer list and the network it belongs to, process and populate the
|
||||
# data structure in my_ipsc_config with the results, and return a simple list of peers.
|
||||
#
|
||||
def process_peer_list(_data, _network):
|
||||
# Create a temporary peer list to track who we should have in our list -- used to find old peers we should remove.
|
||||
_temp_peers = []
|
||||
# Determine the length of the peer list for the parsing iterator
|
||||
_peer_list_length = int(ahex(_data[5:7]), 16)
|
||||
# Record the number of peers in the data structure... we'll use it later (11 bytes per peer entry)
|
||||
CONFIG['SYSTEMS'][_network]['LOCAL']['NUM_PEERS'] = _peer_list_length/11
|
||||
logger.info('(%s) Peer List Received from Master: %s peers in this IPSC', _network, CONFIG['SYSTEMS'][_network]['LOCAL']['NUM_PEERS'])
|
||||
|
||||
# Iterate each peer entry in the peer list. Skip the header, then pull the next peer, the next, etc.
|
||||
for i in range(7, _peer_list_length +7, 11):
|
||||
# Extract various elements from each entry...
|
||||
_hex_radio_id = (_data[i:i+4])
|
||||
_hex_address = (_data[i+4:i+8])
|
||||
_ip_address = IPAddr(_hex_address)
|
||||
_hex_port = (_data[i+8:i+10])
|
||||
_port = int(ahex(_hex_port), 16)
|
||||
_hex_mode = (_data[i+10:i+11])
|
||||
|
||||
# Add this peer to a temporary PeerID list - used to remove any old peers no longer with us
|
||||
_temp_peers.append(_hex_radio_id)
|
||||
|
||||
# This is done elsewhere for the master too, so we use a separate function
|
||||
_decoded_mode = process_mode_byte(_hex_mode)
|
||||
|
||||
# If this entry WAS already in our list, update everything except the stats
|
||||
# in case this was a re-registration with a different mode, flags, etc.
|
||||
if _hex_radio_id in CONFIG['SYSTEMS'][_network]['PEERS'].keys():
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['IP'] = _ip_address
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['PORT'] = _port
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['MODE'] = _hex_mode
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['MODE_DECODE'] = _decoded_mode
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['FLAGS'] = ''
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id]['FLAGS_DECODE'] = ''
|
||||
logger.debug('(%s) Peer Updated: %s', _network, CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id])
|
||||
|
||||
# If this entry was NOT already in our list, add it.
|
||||
if _hex_radio_id not in CONFIG['SYSTEMS'][_network]['PEERS'].keys():
|
||||
CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id] = {
|
||||
'IP': _ip_address,
|
||||
'PORT': _port,
|
||||
'MODE': _hex_mode,
|
||||
'MODE_DECODE': _decoded_mode,
|
||||
'FLAGS': '',
|
||||
'FLAGS_DECODE': '',
|
||||
'STATUS': {
|
||||
'CONNECTED': False,
|
||||
'KEEP_ALIVES_SENT': 0,
|
||||
'KEEP_ALIVES_MISSED': 0,
|
||||
'KEEP_ALIVES_OUTSTANDING': 0,
|
||||
'KEEP_ALIVES_RECEIVED': 0,
|
||||
'KEEP_ALIVE_RX_TIME': 0
|
||||
}
|
||||
}
|
||||
logger.debug('(%s) Peer Added: %s', _network, CONFIG['SYSTEMS'][_network]['PEERS'][_hex_radio_id])
|
||||
|
||||
# Finally, check to see if there's a peer already in our list that was not in this peer list
|
||||
# and if so, delete it.
|
||||
for peer in CONFIG['SYSTEMS'][_network]['PEERS'].keys():
|
||||
if peer not in _temp_peers:
|
||||
de_register_peer(_network, peer)
|
||||
logger.warning('(%s) Peer Deleted (not in new peer list): %s', _network, int_id(peer))
|
||||
|
||||
# Build a peer list - used when a peer registers, re-regiseters or times out
|
||||
#
|
||||
def build_peer_list(_peers):
|
||||
|
@ -266,13 +166,13 @@ def build_peer_list(_peers):
|
|||
|
||||
# Gratuitous print-out of the peer list.. Pretty much debug stuff.
|
||||
#
|
||||
def print_peer_list(_network):
|
||||
_peers = CONFIG['SYSTEMS'][_network]['PEERS']
|
||||
def print_peer_list(_config, _network):
|
||||
_peers = _config['SYSTEMS'][_network]['PEERS']
|
||||
|
||||
_status = CONFIG['SYSTEMS'][_network]['MASTER']['STATUS']['PEER_LIST']
|
||||
_status = _config['SYSTEMS'][_network]['MASTER']['STATUS']['PEER_LIST']
|
||||
#print('Peer List Status for {}: {}' .format(_network, _status))
|
||||
|
||||
if _status and not CONFIG['SYSTEMS'][_network]['PEERS']:
|
||||
if _status and not _config['SYSTEMS'][_network]['PEERS']:
|
||||
print('We are the only peer for: %s' % _network)
|
||||
print('')
|
||||
return
|
||||
|
@ -282,18 +182,18 @@ def print_peer_list(_network):
|
|||
_this_peer = _peers[peer]
|
||||
_this_peer_stat = _this_peer['STATUS']
|
||||
|
||||
if peer == CONFIG['SYSTEMS'][_network]['LOCAL']['RADIO_ID']:
|
||||
if peer == _config['SYSTEMS'][_network]['LOCAL']['RADIO_ID']:
|
||||
me = '(self)'
|
||||
else:
|
||||
me = ''
|
||||
|
||||
print('\tRADIO ID: {} {}' .format(int_id(peer), me))
|
||||
print('\t\tIP Address: {}:{}' .format(_this_peer['IP'], _this_peer['PORT']))
|
||||
if _this_peer['MODE_DECODE'] and CONFIG['REPORTS']['PRINT_PEERS_INC_MODE']:
|
||||
if _this_peer['MODE_DECODE'] and _config['REPORTS']['PRINT_PEERS_INC_MODE']:
|
||||
print('\t\tMode Values:')
|
||||
for name, value in _this_peer['MODE_DECODE'].items():
|
||||
print('\t\t\t{}: {}' .format(name, value))
|
||||
if _this_peer['FLAGS_DECODE'] and CONFIG['REPORTS']['PRINT_PEERS_INC_FLAGS']:
|
||||
if _this_peer['FLAGS_DECODE'] and _config['REPORTS']['PRINT_PEERS_INC_FLAGS']:
|
||||
print('\t\tService Flags:')
|
||||
for name, value in _this_peer['FLAGS_DECODE'].items():
|
||||
print('\t\t\t{}: {}' .format(name, value))
|
||||
|
@ -304,51 +204,24 @@ def print_peer_list(_network):
|
|||
|
||||
# Gratuitous print-out of Master info.. Pretty much debug stuff.
|
||||
#
|
||||
def print_master(_network):
|
||||
if CONFIG['SYSTEMS'][_network]['LOCAL']['MASTER_PEER']:
|
||||
print('DMRlink is the Master for %s'.format(_network))
|
||||
def print_master(_config, _network):
|
||||
if _config['SYSTEMS'][_network]['LOCAL']['MASTER_PEER']:
|
||||
print('DMRlink is the Master for %s' % _network)
|
||||
else:
|
||||
_master = CONFIG['SYSTEMS'][_network]['MASTER']
|
||||
_master = _config['SYSTEMS'][_network]['MASTER']
|
||||
print('Master for %s' % _network)
|
||||
print('\tRADIO ID: {}'.format(int(ahex(_master['RADIO_ID']), 16)))
|
||||
if _master['MODE_DECODE'] and CONFIG['REPORTS']['PRINT_PEERS_INC_MODE']:
|
||||
print('\tRADIO ID: {}' .format(int(ahex(_master['RADIO_ID']), 16)))
|
||||
if _master['MODE_DECODE'] and _config['REPORTS']['PRINT_PEERS_INC_MODE']:
|
||||
print('\t\tMode Values:')
|
||||
for name, value in _master['MODE_DECODE'].items():
|
||||
print('\t\t\t{}: {}'.format(name, value))
|
||||
if _master['FLAGS_DECODE'] and CONFIG['REPORTS']['PRINT_PEERS_INC_FLAGS']:
|
||||
print('\t\t\t{}: {}' .format(name, value))
|
||||
if _master['FLAGS_DECODE'] and _config['REPORTS']['PRINT_PEERS_INC_FLAGS']:
|
||||
print('\t\tService Flags:')
|
||||
for name, value in _master['FLAGS_DECODE'].items():
|
||||
print('\t\t\t{}: {}'.format(name, value))
|
||||
print('\t\t\t{}: {}' .format(name, value))
|
||||
print('\t\tStatus: {}, KeepAlives Sent: {}, KeepAlives Outstanding: {}, KeepAlives Missed: {}' .format(_master['STATUS']['CONNECTED'], _master['STATUS']['KEEP_ALIVES_SENT'], _master['STATUS']['KEEP_ALIVES_OUTSTANDING'], _master['STATUS']['KEEP_ALIVES_MISSED']))
|
||||
print('\t\t KeepAlives Received: {}, Last KeepAlive Received at: {}' .format(_master['STATUS']['KEEP_ALIVES_RECEIVED'], _master['STATUS']['KEEP_ALIVE_RX_TIME']))
|
||||
|
||||
|
||||
# Timed loop used for reporting IPSC status
|
||||
#
|
||||
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
|
||||
def config_reports(_config):
|
||||
global reporting_loop
|
||||
|
||||
if _config['REPORTS']['REPORT_NETWORKS'] == 'PICKLE':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PICKLE)')
|
||||
try:
|
||||
with open(_config['REPORTS']['REPORT_PATH']+'dmrlink_stats.pickle', 'wb') as file:
|
||||
pickle_dump(_config['SYSTEMS'], file, 2)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
logger.error('I/O Error: %s', detail)
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'PRINT':
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (PRINT)')
|
||||
for system in _config['SYSTEMS']:
|
||||
print_master(system)
|
||||
print_peer_list(system)
|
||||
|
||||
else:
|
||||
def reporting_loop():
|
||||
logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
|
||||
#************************************************
|
||||
|
@ -407,42 +280,140 @@ class IPSC(DatagramProtocol):
|
|||
self.DE_REG_REQ_PKT = (DE_REG_REQ + self._local_id)
|
||||
self.DE_REG_REPLY_PKT = (DE_REG_REPLY + self._local_id)
|
||||
#
|
||||
logger.info('(%s) IPSC Instance Created: %s, %s:%s', self._system, int_id(self._local['RADIO_ID']), self._local['IP'], self._local['PORT'])
|
||||
self._logger.info('(%s) IPSC Instance Created: %s, %s:%s', self._system, int_id(self._local['RADIO_ID']), self._local['IP'], self._local['PORT'])
|
||||
|
||||
|
||||
#******************************************************
|
||||
# SUPPORT FUNCTIONS FOR HANDLING IPSC OPERATIONS
|
||||
#******************************************************
|
||||
|
||||
# Determine if the provided peer ID is valid for the provided network
|
||||
#
|
||||
def valid_peer(self, _peerid):
|
||||
if _peerid in self._peers:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Determine if the provided master ID is valid for the provided network
|
||||
#
|
||||
def valid_master(self, _peerid):
|
||||
if self._master['RADIO_ID'] == _peerid:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# De-register a peer from an IPSC by removing it's information
|
||||
#
|
||||
def de_register_peer(self, _peerid):
|
||||
# Iterate for the peer in our data
|
||||
if _peerid in self._peers.keys():
|
||||
del self._peers[_peerid]
|
||||
logger.info('(%s) Peer De-Registration Requested for: %s', self._system, int_id(_peerid))
|
||||
return
|
||||
else:
|
||||
logger.warning('(%s) Peer De-Registration Requested for: %s, but we don\'t have a listing for this peer', self._system, int_id(_peerid))
|
||||
pass
|
||||
|
||||
# Take a received peer list and the network it belongs to, process and populate the
|
||||
# data structure in my_ipsc_config with the results, and return a simple list of peers.
|
||||
#
|
||||
def process_peer_list(self, _data):
|
||||
# Create a temporary peer list to track who we should have in our list -- used to find old peers we should remove.
|
||||
_temp_peers = []
|
||||
# Determine the length of the peer list for the parsing iterator
|
||||
_peer_list_length = int(ahex(_data[5:7]), 16)
|
||||
# Record the number of peers in the data structure... we'll use it later (11 bytes per peer entry)
|
||||
self._local['NUM_PEERS'] = _peer_list_length/11
|
||||
self._logger.info('(%s) Peer List Received from Master: %s peers in this IPSC', self._system, self._local['NUM_PEERS'])
|
||||
|
||||
# Iterate each peer entry in the peer list. Skip the header, then pull the next peer, the next, etc.
|
||||
for i in range(7, _peer_list_length +7, 11):
|
||||
# Extract various elements from each entry...
|
||||
_hex_radio_id = (_data[i:i+4])
|
||||
_hex_address = (_data[i+4:i+8])
|
||||
_ip_address = IPAddr(_hex_address)
|
||||
_hex_port = (_data[i+8:i+10])
|
||||
_port = int(ahex(_hex_port), 16)
|
||||
_hex_mode = (_data[i+10:i+11])
|
||||
|
||||
# Add this peer to a temporary PeerID list - used to remove any old peers no longer with us
|
||||
_temp_peers.append(_hex_radio_id)
|
||||
|
||||
# This is done elsewhere for the master too, so we use a separate function
|
||||
_decoded_mode = process_mode_byte(_hex_mode)
|
||||
|
||||
# If this entry WAS already in our list, update everything except the stats
|
||||
# in case this was a re-registration with a different mode, flags, etc.
|
||||
if _hex_radio_id in self._peers.keys():
|
||||
self._peers[_hex_radio_id]['IP'] = _ip_address
|
||||
self._peers[_hex_radio_id]['PORT'] = _port
|
||||
self._peers[_hex_radio_id]['MODE'] = _hex_mode
|
||||
self._peers[_hex_radio_id]['MODE_DECODE'] = _decoded_mode
|
||||
self._peers[_hex_radio_id]['FLAGS'] = ''
|
||||
self._peers[_hex_radio_id]['FLAGS_DECODE'] = ''
|
||||
self._logger.debug('(%s) Peer Updated: %s', self._system, self._peers[_hex_radio_id])
|
||||
|
||||
# If this entry was NOT already in our list, add it.
|
||||
if _hex_radio_id not in self._peers.keys():
|
||||
self._peers[_hex_radio_id] = {
|
||||
'IP': _ip_address,
|
||||
'PORT': _port,
|
||||
'MODE': _hex_mode,
|
||||
'MODE_DECODE': _decoded_mode,
|
||||
'FLAGS': '',
|
||||
'FLAGS_DECODE': '',
|
||||
'STATUS': {
|
||||
'CONNECTED': False,
|
||||
'KEEP_ALIVES_SENT': 0,
|
||||
'KEEP_ALIVES_MISSED': 0,
|
||||
'KEEP_ALIVES_OUTSTANDING': 0,
|
||||
'KEEP_ALIVES_RECEIVED': 0,
|
||||
'KEEP_ALIVE_RX_TIME': 0
|
||||
}
|
||||
}
|
||||
self._logger.debug('(%s) Peer Added: %s', self._system, self._peers[_hex_radio_id])
|
||||
|
||||
# Finally, check to see if there's a peer already in our list that was not in this peer list
|
||||
# and if so, delete it.
|
||||
for peer in self._peers.keys():
|
||||
if peer not in _temp_peers:
|
||||
self.de_register_peer(peer)
|
||||
self._logger.warning('(%s) Peer Deleted (not in new peer list): %s', self._system, int_id(peer))
|
||||
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
|
||||
def call_mon_status(self, _network, _data):
|
||||
logger.debug('(%s) Repeater Call Monitor Origin Packet Received: %s',_network, ahex(_data))
|
||||
def call_mon_status(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor Origin Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
def call_mon_rpt(self, _network, _data):
|
||||
logger.debug('(%s) Repeater Call Monitor Repeating Packet Received: %s', _network, ahex(_data))
|
||||
def call_mon_rpt(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor Repeating Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
def call_mon_nack(self, _network, _data):
|
||||
logger.debug('(%s) Repeater Call Monitor NACK Packet Received: %s', _network, ahex(_data))
|
||||
def call_mon_nack(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor NACK Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
def xcmp_xnl(self, _network, _data):
|
||||
logger.debug('(%s) XCMP/XNL Packet Received: %s', _network, ahex(_data))
|
||||
def xcmp_xnl(self, _data):
|
||||
self._logger.debug('(%s) XCMP/XNL Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
def repeater_wake_up(self, _network, _data):
|
||||
logger.debug('(%s) Repeater Wake-Up Packet Received: %s', _network, ahex(_data))
|
||||
def repeater_wake_up(self, _data):
|
||||
self._logger.debug('(%s) Repeater Wake-Up Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
def group_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def group_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
def private_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Private Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def private_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Private Voice Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
def group_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Group Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def group_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Group Data Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
def private_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
def private_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
self._logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', self._system, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
|
||||
|
||||
def unknown_message(self, _network, _packettype, _peerid, _data):
|
||||
logger.error('(%s) Unknown Message - Type: %s From: %s Packet: %s', _network, ahex(_packettype), int_id(_peerid), ahex(_data))
|
||||
def unknown_message(self, _packettype, _peerid, _data):
|
||||
self._logger.error('(%s) Unknown Message - Type: %s From: %s Packet: %s', ahex(_packettype), self._system, int_id(_peerid), ahex(_data))
|
||||
|
||||
|
||||
#************************************************
|
||||
|
@ -457,7 +428,7 @@ class IPSC(DatagramProtocol):
|
|||
_packet = _packet + _hash
|
||||
self.transport.write(_packet, (_host, _port))
|
||||
# USE THE FOLLOWING ONLY UNDER DIRE CIRCUMSTANCES -- PERFORMANCE IS ADVERSLY AFFECTED!
|
||||
#logger.debug('(%s) TX Packet to %s on port %s: %s', self._system, _host, _port, ahex(_packet))
|
||||
#self._logger.debug('(%s) TX Packet to %s on port %s: %s', self._system, _host, _port, ahex(_packet))
|
||||
|
||||
# Accept a complete packet, ready to be sent, and send it to all active peers + master in an IPSC
|
||||
#
|
||||
|
@ -489,12 +460,12 @@ class IPSC(DatagramProtocol):
|
|||
self._peers[_peerid]['FLAGS_DECODE'] = _decoded_flags
|
||||
self.send_packet(self.PEER_ALIVE_REPLY_PKT, (_host, _port))
|
||||
self.reset_keep_alive(_peerid) # Might as well reset our own counter, we know it's out there...
|
||||
logger.debug('(%s) Keep-Alive reply sent to Peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
self._logger.debug('(%s) Keep-Alive reply sent to Peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
|
||||
# SOMEONE WANTS TO REGISTER WITH US - WE'RE COOL WITH THAT
|
||||
def peer_reg_req(self, _peerid, _host, _port):
|
||||
self.send_packet(self.PEER_REG_REPLY_PKT, (_host, _port))
|
||||
logger.info('(%s) Peer Registration Request From: %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
self._logger.info('(%s) Peer Registration Request From: %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
|
||||
|
||||
# SOMEONE HAS ANSWERED OUR KEEP-ALIVE REQUEST - KEEP TRACK OF IT
|
||||
|
@ -502,27 +473,27 @@ class IPSC(DatagramProtocol):
|
|||
self.reset_keep_alive(_peerid)
|
||||
self._peers[_peerid]['STATUS']['KEEP_ALIVES_RECEIVED'] += 1
|
||||
self._peers[_peerid]['STATUS']['KEEP_ALIVE_RX_TIME'] = int(time())
|
||||
logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from Peer %s, %s:%s', self._system, int_id(_peerid), self._peers[_peerid]['IP'], self._peers[_peerid]['PORT'])
|
||||
self._logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from Peer %s, %s:%s', self._system, int_id(_peerid), self._peers[_peerid]['IP'], self._peers[_peerid]['PORT'])
|
||||
|
||||
# SOMEONE HAS ANSWERED OUR REQEST TO REGISTER WITH THEM - KEEP TRACK OF IT
|
||||
def peer_reg_reply(self, _peerid):
|
||||
if _peerid in self._peers.keys():
|
||||
self._peers[_peerid]['STATUS']['CONNECTED'] = True
|
||||
logger.info('(%s) Registration Reply From: %s, %s:%s', self._system, int_id(_peerid), self._peers[_peerid]['IP'], self._peers[_peerid]['PORT'])
|
||||
self._logger.info('(%s) Registration Reply From: %s, %s:%s', self._system, int_id(_peerid), self._peers[_peerid]['IP'], self._peers[_peerid]['PORT'])
|
||||
|
||||
# OUR MASTER HAS ANSWERED OUR KEEP-ALIVE REQUEST - KEEP TRACK OF IT
|
||||
def master_alive_reply(self, _peerid):
|
||||
self.reset_keep_alive(_peerid)
|
||||
self._master['STATUS']['KEEP_ALIVES_RECEIVED'] += 1
|
||||
self._master['STATUS']['KEEP_ALIVE_RX_TIME'] = int(time())
|
||||
logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from the Master %s, %s:%s', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'])
|
||||
self._logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from the Master %s, %s:%s', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'])
|
||||
|
||||
# OUR MASTER HAS SENT US A PEER LIST - PROCESS IT
|
||||
def peer_list_reply(self, _data, _peerid):
|
||||
CONFIG['SYSTEMS'][self._system]['MASTER']['STATUS']['PEER_LIST'] = True
|
||||
self._master['STATUS']['PEER_LIST'] = True
|
||||
if len(_data) > 18:
|
||||
process_peer_list(_data, self._system)
|
||||
logger.debug('(%s) Peer List Reply Received From Master %s, %s:%s', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'])
|
||||
self.process_peer_list(_data)
|
||||
self._logger.debug('(%s) Peer List Reply Received From Master %s, %s:%s', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'])
|
||||
|
||||
# OUR MASTER HAS ANSWERED OUR REQUEST TO REGISTER - LOTS OF INFORMATION TO TRACK
|
||||
def master_reg_reply(self, _data, _peerid):
|
||||
|
@ -540,7 +511,7 @@ class IPSC(DatagramProtocol):
|
|||
self._master['FLAGS_DECODE'] = _decoded_flags
|
||||
self._master_stat['CONNECTED'] = True
|
||||
self._master_stat['KEEP_ALIVES_OUTSTANDING'] = 0
|
||||
logger.warning('(%s) Registration response (we requested reg) from the Master: %s, %s:%s (%s peers)', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'], self._local['NUM_PEERS'])
|
||||
self._logger.warning('(%s) Registration response (we requested reg) from the Master: %s, %s:%s (%s peers)', self._system, int_id(_peerid), self._master['IP'], self._master['PORT'], self._local['NUM_PEERS'])
|
||||
|
||||
# WE ARE MASTER AND SOMEONE HAS REQUESTED REGISTRATION FROM US - ANSWER IT
|
||||
def master_reg_req(self, _data, _peerid, _host, _port):
|
||||
|
@ -553,7 +524,7 @@ class IPSC(DatagramProtocol):
|
|||
|
||||
self.MASTER_REG_REPLY_PKT = (MASTER_REG_REPLY + self._local_id + self.TS_FLAGS + hex_str_2(self._local['NUM_PEERS']) + IPSC_VER)
|
||||
self.send_packet(self.MASTER_REG_REPLY_PKT, (_host, _port))
|
||||
logger.info('(%s) Master Registration Packet Received from peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
self._logger.info('(%s) Master Registration Packet Received from peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
|
||||
# If this entry was NOT already in our list, add it.
|
||||
if _peerid not in self._peers.keys():
|
||||
|
@ -574,7 +545,7 @@ class IPSC(DatagramProtocol):
|
|||
}
|
||||
}
|
||||
self._local['NUM_PEERS'] = len(self._peers)
|
||||
logger.debug('(%s) Peer Added To Peer List: %s, %s:%s (IPSC now has %s Peers)', self._system, self._peers[_peerid], _host, _port, self._local['NUM_PEERS'])
|
||||
self._logger.debug('(%s) Peer Added To Peer List: %s, %s:%s (IPSC now has %s Peers)', self._system, self._peers[_peerid], _host, _port, self._local['NUM_PEERS'])
|
||||
|
||||
# WE ARE MASTER AND SOEMONE SENT US A KEEP-ALIVE - ANSWER IT, TRACK IT
|
||||
def master_alive_req(self, _peerid, _host, _port):
|
||||
|
@ -582,17 +553,17 @@ class IPSC(DatagramProtocol):
|
|||
self._peers[_peerid]['STATUS']['KEEP_ALIVES_RECEIVED'] += 1
|
||||
self._peers[_peerid]['STATUS']['KEEP_ALIVE_RX_TIME'] = int(time())
|
||||
self.send_packet(self.MASTER_ALIVE_REPLY_PKT, (_host, _port))
|
||||
logger.debug('(%s) Master Keep-Alive Request Received from peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
self._logger.debug('(%s) Master Keep-Alive Request Received from peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
else:
|
||||
logger.warning('(%s) Master Keep-Alive Request Received from *UNREGISTERED* peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
self._logger.warning('(%s) Master Keep-Alive Request Received from *UNREGISTERED* peer %s, %s:%s', self._system, int_id(_peerid), _host, _port)
|
||||
|
||||
# WE ARE MASTER AND A PEER HAS REQUESTED A PEER LIST - SEND THEM ONE
|
||||
def peer_list_req(self, _peerid):
|
||||
if _peerid in self._peers.keys():
|
||||
logger.debug('(%s) Peer List Request from peer %s', self._system, int_id(_peerid))
|
||||
self._logger.debug('(%s) Peer List Request from peer %s', self._system, int_id(_peerid))
|
||||
self.send_to_ipsc(self.PEER_LIST_REPLY_PKT + build_peer_list(self._peers))
|
||||
else:
|
||||
logger.warning('(%s) Peer List Request Received from *UNREGISTERED* peer %s', self._system, int_id(_peerid))
|
||||
self._logger.warning('(%s) Peer List Request Received from *UNREGISTERED* peer %s', self._system, int_id(_peerid))
|
||||
|
||||
|
||||
# Reset the outstanding keep-alive counter for _peerid...
|
||||
|
@ -661,45 +632,45 @@ class IPSC(DatagramProtocol):
|
|||
# Timed loop used for IPSC connection Maintenance when we are the MASTER
|
||||
#
|
||||
def master_maintenance_loop(self):
|
||||
logger.debug('(%s) MASTER Connection Maintenance Loop Started', self._system)
|
||||
self._logger.debug('(%s) MASTER Connection Maintenance Loop Started', self._system)
|
||||
update_time = int(time())
|
||||
|
||||
for peer in self._peers.keys():
|
||||
keep_alive_delta = update_time - self._peers[peer]['STATUS']['KEEP_ALIVE_RX_TIME']
|
||||
logger.debug('(%s) Time Since Last KeepAlive Request from Peer %s: %s seconds', self._system, int_id(peer), keep_alive_delta)
|
||||
self._logger.debug('(%s) Time Since Last KeepAlive Request from Peer %s: %s seconds', self._system, int_id(peer), keep_alive_delta)
|
||||
|
||||
if keep_alive_delta > 120:
|
||||
de_register_peer(self._system, peer)
|
||||
self.de_register_peer(peer)
|
||||
self.send_to_ipsc(self.PEER_LIST_REPLY_PKT + build_peer_list(self._peers))
|
||||
logger.warning('(%s) Timeout Exceeded for Peer %s, De-registering', self._system, int_id(peer))
|
||||
self._logger.warning('(%s) Timeout Exceeded for Peer %s, De-registering', self._system, int_id(peer))
|
||||
|
||||
# Timed loop used for IPSC connection Maintenance when we are a PEER
|
||||
#
|
||||
def peer_maintenance_loop(self):
|
||||
logger.debug('(%s) PEER Connection Maintenance Loop Started', self._system)
|
||||
self._logger.debug('(%s) PEER Connection Maintenance Loop Started', self._system)
|
||||
|
||||
# If the master isn't connected, we have to do that before we can do anything else!
|
||||
#
|
||||
if not self._master_stat['CONNECTED']:
|
||||
self.send_packet(self.MASTER_REG_REQ_PKT, self._master_sock)
|
||||
logger.info('(%s) Registering with the Master: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
self._logger.info('(%s) Registering with the Master: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
|
||||
# Once the master is connected, we have to send keep-alives.. and make sure we get them back
|
||||
elif self._master_stat['CONNECTED']:
|
||||
# Send keep-alive to the master
|
||||
self.send_packet(self.MASTER_ALIVE_PKT, self._master_sock)
|
||||
logger.debug('(%s) Keep Alive Sent to the Master: %s, %s:%s', self._system, int_id(self._master['RADIO_ID']) ,self._master['IP'], self._master['PORT'])
|
||||
self._logger.debug('(%s) Keep Alive Sent to the Master: %s, %s:%s', self._system, int_id(self._master['RADIO_ID']) ,self._master['IP'], self._master['PORT'])
|
||||
|
||||
# If we had a keep-alive outstanding by the time we send another, mark it missed.
|
||||
if (self._master_stat['KEEP_ALIVES_OUTSTANDING']) > 0:
|
||||
self._master_stat['KEEP_ALIVES_MISSED'] += 1
|
||||
logger.info('(%s) Master Keep-Alive Missed: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
self._logger.info('(%s) Master Keep-Alive Missed: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
|
||||
# If we have missed too many keep-alives, de-register the master and start over.
|
||||
if self._master_stat['KEEP_ALIVES_OUTSTANDING'] >= self._local['MAX_MISSED']:
|
||||
self._master_stat['CONNECTED'] = False
|
||||
self._master_stat['KEEP_ALIVES_OUTSTANDING'] = 0
|
||||
logger.error('(%s) Maximum Master Keep-Alives Missed -- De-registering the Master: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
self._logger.error('(%s) Maximum Master Keep-Alives Missed -- De-registering the Master: %s:%s', self._system, self._master['IP'], self._master['PORT'])
|
||||
|
||||
# Update our stats before we move on...
|
||||
self._master_stat['KEEP_ALIVES_SENT'] += 1
|
||||
|
@ -707,7 +678,7 @@ class IPSC(DatagramProtocol):
|
|||
|
||||
else:
|
||||
# This is bad. If we get this message, we need to reset the state and try again
|
||||
logger.error('->> (%s) Master in UNKOWN STATE: %s:%s', self._system, self._master_sock)
|
||||
self._logger.error('->> (%s) Master in UNKOWN STATE: %s:%s', self._system, self._master_sock)
|
||||
self._master_stat['CONNECTED'] = False
|
||||
|
||||
|
||||
|
@ -717,10 +688,10 @@ class IPSC(DatagramProtocol):
|
|||
# Ask the master for a peer-list
|
||||
if self._local['NUM_PEERS']:
|
||||
self.send_packet(self.PEER_LIST_REQ_PKT, self._master_sock)
|
||||
logger.info('(%s), No Peer List - Requesting One From the Master', self._system)
|
||||
self._logger.info('(%s), No Peer List - Requesting One From the Master', self._system)
|
||||
else:
|
||||
self._master_stat['PEER_LIST'] = True
|
||||
logger.debug('(%s), Skip asking for a Peer List, we are the only Peer', self._system)
|
||||
self._logger.debug('(%s), Skip asking for a Peer List, we are the only Peer', self._system)
|
||||
|
||||
|
||||
# If we do have a peer-list, we need to register with the peers and send keep-alives...
|
||||
|
@ -736,23 +707,23 @@ class IPSC(DatagramProtocol):
|
|||
# If we haven't registered to a peer, send a registration
|
||||
if not self._peers[peer]['STATUS']['CONNECTED']:
|
||||
self.send_packet(self.PEER_REG_REQ_PKT, (self._peers[peer]['IP'], self._peers[peer]['PORT']))
|
||||
logger.info('(%s) Registering with Peer %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
self._logger.info('(%s) Registering with Peer %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
|
||||
# If we have registered with the peer, then send a keep-alive
|
||||
elif self._peers[peer]['STATUS']['CONNECTED']:
|
||||
self.send_packet(self.PEER_ALIVE_REQ_PKT, (self._peers[peer]['IP'], self._peers[peer]['PORT']))
|
||||
logger.debug('(%s) Keep-Alive Sent to the Peer %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
self._logger.debug('(%s) Keep-Alive Sent to the Peer %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
|
||||
# If we have a keep-alive outstanding by the time we send another, mark it missed.
|
||||
if self._peers[peer]['STATUS']['KEEP_ALIVES_OUTSTANDING'] > 0:
|
||||
self._peers[peer]['STATUS']['KEEP_ALIVES_MISSED'] += 1
|
||||
logger.info('(%s) Peer Keep-Alive Missed for %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
self._logger.info('(%s) Peer Keep-Alive Missed for %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
|
||||
# If we have missed too many keep-alives, de-register the peer and start over.
|
||||
if self._peers[peer]['STATUS']['KEEP_ALIVES_OUTSTANDING'] >= self._local['MAX_MISSED']:
|
||||
self._peers[peer]['STATUS']['CONNECTED'] = False
|
||||
#del peer # Becuase once it's out of the dictionary, you can't use it for anything else.
|
||||
logger.warning('(%s) Maximum Peer Keep-Alives Missed -- De-registering the Peer: %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
self._logger.warning('(%s) Maximum Peer Keep-Alives Missed -- De-registering the Peer: %s, %s:%s', self._system, int_id(peer), self._peers[peer]['IP'], self._peers[peer]['PORT'])
|
||||
|
||||
# Update our stats before moving on...
|
||||
self._peers[peer]['STATUS']['KEEP_ALIVES_SENT'] += 1
|
||||
|
@ -781,7 +752,7 @@ class IPSC(DatagramProtocol):
|
|||
# AUTHENTICATE THE PACKET
|
||||
if self._local['AUTH_ENABLED']:
|
||||
if not self.validate_auth(self._local['AUTH_KEY'], data):
|
||||
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer: %s, %s:%s', self._system, ahex(_packettype), int_id(_peerid), host, port)
|
||||
self._logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer: %s, %s:%s', self._system, ahex(_packettype), int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
# REMOVE SHA-1 AUTHENTICATION HASH: WE NO LONGER NEED IT
|
||||
|
@ -790,8 +761,8 @@ class IPSC(DatagramProtocol):
|
|||
|
||||
# PACKETS THAT WE RECEIVE FROM ANY VALID PEER OR VALID MASTER
|
||||
if _packettype in ANY_PEER_REQUIRED:
|
||||
if not(valid_master(self._system, _peerid) == False or valid_peer(self._peers.keys(), _peerid) == False):
|
||||
logger.warning('(%s) PeerError: Peer not in peer-list: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
if not(self.valid_master(_peerid) == False or valid_peer(self._peers.keys(), _peerid) == False):
|
||||
self._logger.warning('(%s) PeerError: Peer not in peer-list: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
# ORIGINATED BY SUBSCRIBER UNITS - a.k.a someone transmitted
|
||||
|
@ -802,7 +773,7 @@ class IPSC(DatagramProtocol):
|
|||
_call_type = data[12:13]
|
||||
_unknown_1 = data[13:17]
|
||||
_call_info = int_id(data[17:18])
|
||||
_ts = bool(_call_info & TS_CALL_MSK)
|
||||
_ts = bool(_call_info & TS_CALL_MSK) + 1
|
||||
_end = bool(_call_info & END_MSK)
|
||||
|
||||
# Extract RTP Header Fields
|
||||
|
@ -823,59 +794,59 @@ class IPSC(DatagramProtocol):
|
|||
# User Voice and Data Call Types:
|
||||
if _packettype == GROUP_VOICE:
|
||||
self.reset_keep_alive(_peerid)
|
||||
self.group_voice(self._system, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
self.group_voice(_src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
return
|
||||
|
||||
elif _packettype == PVT_VOICE:
|
||||
self.reset_keep_alive(_peerid)
|
||||
self.private_voice(self._system, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
self.private_voice(_src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
return
|
||||
|
||||
elif _packettype == GROUP_DATA:
|
||||
self.reset_keep_alive(_peerid)
|
||||
self.group_data(self._system, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
self.group_data(_src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
return
|
||||
|
||||
elif _packettype == PVT_DATA:
|
||||
self.reset_keep_alive(_peerid)
|
||||
self.private_data(self._system, _src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
self.private_data(_src_sub, _dst_sub, _ts, _end, _peerid, data)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
# MOTOROLA XCMP/XNL CONTROL PROTOCOL: We don't process these (yet)
|
||||
elif _packettype == XCMP_XNL:
|
||||
self.xcmp_xnl(self._system, data)
|
||||
self.xcmp_xnl(data)
|
||||
return
|
||||
|
||||
|
||||
# ORIGINATED BY PEERS, NOT IPSC MAINTENANCE: Call monitoring is all we've found here so far
|
||||
elif _packettype == CALL_MON_STATUS:
|
||||
self.call_mon_status(self._system, data)
|
||||
self.call_mon_status(data)
|
||||
return
|
||||
|
||||
elif _packettype == CALL_MON_RPT:
|
||||
self.call_mon_rpt(self._system, data)
|
||||
self.call_mon_rpt(data)
|
||||
return
|
||||
|
||||
elif _packettype == CALL_MON_NACK:
|
||||
self.call_mon_nack(self._system, data)
|
||||
self.call_mon_nack(data)
|
||||
return
|
||||
|
||||
|
||||
# IPSC CONNECTION MAINTENANCE MESSAGES
|
||||
elif _packettype == DE_REG_REQ:
|
||||
de_register_peer(self._system, _peerid)
|
||||
logger.warning('(%s) Peer De-Registration Request From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
self.de_register_peer(_peerid)
|
||||
self._logger.warning('(%s) Peer De-Registration Request From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
elif _packettype == DE_REG_REPLY:
|
||||
logger.warning('(%s) Peer De-Registration Reply From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
self._logger.warning('(%s) Peer De-Registration Reply From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
elif _packettype == RPT_WAKE_UP:
|
||||
self.repeater_wake_up(self._system, data)
|
||||
logger.debug('(%s) Repeater Wake-Up Packet From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
self.repeater_wake_up(data)
|
||||
self._logger.debug('(%s) Repeater Wake-Up Packet From: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
return
|
||||
|
||||
|
@ -884,8 +855,8 @@ class IPSC(DatagramProtocol):
|
|||
|
||||
# ONLY ACCEPT FROM A PREVIOUSLY VALIDATED PEER
|
||||
if _packettype in PEER_REQUIRED:
|
||||
if not valid_peer(self._peers.keys(), _peerid):
|
||||
logger.warning('(%s) PeerError: Peer not in peer-list: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
if not self.valid_peer(_peerid):
|
||||
self._logger.warning('(%s) PeerError: Peer not in peer-list: %s, %s:%s', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
# REQUESTS FROM PEERS: WE MUST REPLY IMMEDIATELY FOR IPSC MAINTENANCE
|
||||
|
@ -912,8 +883,8 @@ class IPSC(DatagramProtocol):
|
|||
|
||||
# PACKETS WE ONLY ACCEPT IF WE HAVE FINISHED REGISTERING WITH OUR MASTER
|
||||
if _packettype in MASTER_REQUIRED:
|
||||
if not valid_master(self._system, _peerid):
|
||||
logger.warning('(%s) MasterError: %s, %s:%s is not the master peer', self._system, int_id(_peerid), host, port)
|
||||
if not self.valid_master(_peerid):
|
||||
self._logger.warning('(%s) MasterError: %s, %s:%s is not the master peer', self._system, int_id(_peerid), host, port)
|
||||
return
|
||||
|
||||
# ANSWERS FROM REQUESTS WE SENT TO THE MASTER: WE DO NOT REPLY
|
||||
|
@ -1008,8 +979,8 @@ if __name__ == '__main__':
|
|||
|
||||
# INITIALIZE THE REPORTING LOOP IF CONFIGURED
|
||||
if CONFIG['REPORTS']['REPORT_NETWORKS']:
|
||||
config_reporting_loop(CONFIG['REPORTS']['REPORT_NETWORKS'])
|
||||
reporting = task.LoopingCall(reporting_loop)
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
reactor.run()
|
||||
|
|
|
@ -64,6 +64,23 @@ LOG_LEVEL: INFO
|
|||
LOG_NAME: DMRlink
|
||||
|
||||
|
||||
# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES
|
||||
# Ok, not the TGID, there's no master list I know of to download
|
||||
# This is intended as a facility for other applcations built on top of
|
||||
# HBlink to use, and will NOT be used in HBlink directly.
|
||||
# STALE_DAYS is the number of days since the last download before we
|
||||
# download again. Don't be an ass and change this to less than a few days.
|
||||
[ALIASES]
|
||||
TRY_DOWNLOAD: True
|
||||
PATH: ./
|
||||
PEER_FILE: peer_ids.csv
|
||||
SUBSCRIBER_FILE: subscriber_ids.csv
|
||||
TGID_FILE: talkgroup_ids.csv
|
||||
PEER_URL: http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=repeaters&format=csv&header=0
|
||||
SUBSCRIBER_URL: http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0
|
||||
STALE_DAYS: 7
|
||||
|
||||
|
||||
# CONFIGURATION FOR IPSC NETWORKS
|
||||
# Please read these closely - catastrophic results could result by setting
|
||||
# certain flags for things DMRlink cannot do.
|
||||
|
|
|
@ -41,6 +41,7 @@ def build_config(_config_file):
|
|||
CONFIG['GLOBAL'] = {}
|
||||
CONFIG['REPORTS'] = {}
|
||||
CONFIG['LOGGER'] = {}
|
||||
CONFIG['ALIASES'] = {}
|
||||
CONFIG['SYSTEMS'] = {}
|
||||
|
||||
try:
|
||||
|
@ -67,6 +68,18 @@ def build_config(_config_file):
|
|||
'LOG_NAME': config.get(section, 'LOG_NAME')
|
||||
})
|
||||
|
||||
elif section == 'ALIASES':
|
||||
CONFIG['ALIASES'].update({
|
||||
'TRY_DOWNLOAD': config.getboolean(section, 'TRY_DOWNLOAD'),
|
||||
'PATH': config.get(section, 'PATH'),
|
||||
'PEER_FILE': config.get(section, 'PEER_FILE'),
|
||||
'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE'),
|
||||
'TGID_FILE': config.get(section, 'TGID_FILE'),
|
||||
'PEER_URL': config.get(section, 'PEER_URL'),
|
||||
'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL'),
|
||||
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
|
||||
})
|
||||
|
||||
elif config.getboolean(section, 'ENABLED'):
|
||||
CONFIG['SYSTEMS'].update({section: {'LOCAL': {}, 'MASTER': {}, 'PEERS': {}}})
|
||||
|
||||
|
|
129
log.py
129
log.py
|
@ -25,7 +25,8 @@ from twisted.internet import reactor
|
|||
from binascii import b2a_hex as h
|
||||
|
||||
import time
|
||||
from dmrlink import IPSC, NETWORK, networks, get_info, int_id, subscriber_ids, peer_ids, talkgroup_ids, logger
|
||||
from dmrlink import IPSC, systems
|
||||
from dmr_utils.utils import hex_str_3, hex_str_4, int_id, get_alias
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2013, 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -36,39 +37,33 @@ __email__ = 'n0mjs@me.com'
|
|||
|
||||
|
||||
class logIPSC(IPSC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.ACTIVE_CALLS = []
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
|
||||
def group_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
# _log = logger.debug
|
||||
def group_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if (_ts not in self.ACTIVE_CALLS) or _end:
|
||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
||||
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||
_peerid = get_info(int_id(_peerid), peer_ids)
|
||||
_src_sub = get_info(int_id(_src_sub), subscriber_ids)
|
||||
_dst_sub = get_alias(_dst_sub, talkgroup_ids)
|
||||
_peerid = get_alias(_peerid, peer_ids)
|
||||
_src_sub = get_alias(_src_sub, subscriber_ids)
|
||||
if not _end: self.ACTIVE_CALLS.append(_ts)
|
||||
if _end: self.ACTIVE_CALLS.remove(_ts)
|
||||
|
||||
if _ts: _ts = 2
|
||||
else: _ts = 1
|
||||
if _end: _end = 'END'
|
||||
else: _end = 'START'
|
||||
|
||||
print('{} ({}) Call {} Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, _network, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||
print('{} ({}) Call {} Group Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, self._system, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||
|
||||
def private_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
# _log = logger.debug
|
||||
def private_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if (_ts not in self.ACTIVE_CALLS) or _end:
|
||||
_time = time.strftime('%m/%d/%y %H:%M:%S')
|
||||
_dst_sub = get_info(int_id(_dst_sub), subscriber_ids)
|
||||
_peerid = get_info(int_id(_peerid), peer_ids)
|
||||
_src_sub = get_info(int_id(_src_sub), subscriber_ids)
|
||||
_dst_sub = get_alias(_dst_sub, subscriber_ids)
|
||||
_peerid = get_alias(_peerid, peer_ids)
|
||||
_src_sub = get_alias(_src_sub, subscriber_ids)
|
||||
if not _end: self.ACTIVE_CALLS.append(_ts)
|
||||
if _end: self.ACTIVE_CALLS.remove(_ts)
|
||||
|
||||
|
@ -77,25 +72,93 @@ class logIPSC(IPSC):
|
|||
if _end: _end = 'END'
|
||||
else: _end = 'START'
|
||||
|
||||
print('{} ({}) Call {} Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, _network, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||
print('{} ({}) Call {} Private Voice: \n\tIPSC Source:\t{}\n\tSubscriber:\t{}\n\tDestination:\t{}\n\tTimeslot\t{}' .format(_time, self._system, _end, _peerid, _src_sub, _dst_sub, _ts))
|
||||
|
||||
def group_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
_dst_sub = get_info(int_id(_dst_sub), talkgroup_ids)
|
||||
_peerid = get_info(int_id(_peerid), peer_ids)
|
||||
_src_sub = get_info(int_id(_src_sub), subscriber_ids)
|
||||
print('({}) Group Data Packet Received From: {}' .format(_network, _src_sub))
|
||||
def group_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
_dst_sub = get_alias(_dst_sub, talkgroup_ids)
|
||||
_peerid = get_alias(_peerid, peer_ids)
|
||||
_src_sub = get_alias(_src_sub, subscriber_ids)
|
||||
print('({}) Group Data Packet Received From: {}' .format(self._system, _src_sub))
|
||||
|
||||
def private_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
_dst_sub = get_info(int_id(_dst_sub), subscriber_ids)
|
||||
_peerid = get_info(int_id(_peerid), peer_ids)
|
||||
_src_sub = get_info(int_id(_src_sub), subscriber_ids)
|
||||
print('({}) Private Data Packet Received From: {} To: {}' .format(_network, _src_sub, _dst_sub))
|
||||
def private_data(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
_dst_sub = get_alias(_dst_sub, subscriber_ids)
|
||||
_peerid = get_alias(_peerid, peer_ids)
|
||||
_src_sub = get_alias(_src_sub, subscriber_ids)
|
||||
print('({}) Private Data Packet Received From: {} To: {}' .format(self._system, _src_sub, _dst_sub))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'log.py\' (c) 2013, 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = logIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
|
||||
# ID ALIAS CREATION
|
||||
# Download
|
||||
if CONFIG['ALIASES']['TRY_DOWNLOAD'] == True:
|
||||
# Try updating peer aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'], CONFIG['ALIASES']['PEER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
# Try updating subscriber aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'], CONFIG['ALIASES']['SUBSCRIBER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
|
||||
# Make Dictionaries
|
||||
peer_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'])
|
||||
if peer_ids:
|
||||
logger.info('ID ALIAS MAPPER: peer_ids dictionary is available')
|
||||
|
||||
subscriber_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'])
|
||||
if subscriber_ids:
|
||||
logger.info('ID ALIAS MAPPER: subscriber_ids dictionary is available')
|
||||
|
||||
talkgroup_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['TGID_FILE'])
|
||||
if talkgroup_ids:
|
||||
logger.info('ID ALIAS MAPPER: talkgroup_ids dictionary is available')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = logIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
4719
peer_ids.csv
4719
peer_ids.csv
File diff suppressed because it is too large
Load Diff
|
@ -32,11 +32,14 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from twisted.internet import reactor
|
||||
from binascii import b2a_hex as h
|
||||
|
||||
import sys, time
|
||||
import cPickle as pickle
|
||||
from dmrlink import IPSC, NETWORK, networks, logger, int_id, hex_str_3
|
||||
|
||||
from dmrlink import IPSC, systems
|
||||
|
||||
from dmr_utils.utils import int_id, hex_str_3
|
||||
from ipsc.ipsc_const import BURST_DATA_TYPE
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2014 - 2015 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -45,14 +48,6 @@ __license__ = 'GNU GPLv3'
|
|||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
# Constants for this application
|
||||
#
|
||||
BURST_DATA_TYPE = {
|
||||
'VOICE_HEAD': '\x01',
|
||||
'VOICE_TERM': '\x02',
|
||||
'SLOT1_VOICE': '\x0A',
|
||||
'SLOT2_VOICE': '\x8A'
|
||||
}
|
||||
|
||||
# path+filename for the transmission to play back
|
||||
filename = '../test.pickle'
|
||||
|
@ -66,9 +61,8 @@ trigger_groups_1 = ['\x00\x00\x01', '\x00\x00\x0D', '\x00\x00\x64']
|
|||
trigger_groups_2 = ['\x00\x0C\x30',]
|
||||
|
||||
class playIPSC(IPSC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.CALL_DATA = []
|
||||
self.event_id = 1
|
||||
|
||||
|
@ -76,30 +70,30 @@ class playIPSC(IPSC):
|
|||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
#
|
||||
def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
|
||||
def group_voice(self, _src_sub, _dst_group, _ts, _end, _peerid, _data):
|
||||
if _end:
|
||||
_self_peer = NETWORK[_network]['LOCAL']['RADIO_ID']
|
||||
_self_peer = self._config['LOCAL']['RADIO_ID']
|
||||
_self_src = _self_peer[1:]
|
||||
|
||||
if (_peerid == _self_peer) or (_src_sub == _self_src):
|
||||
logger.error('(%s) Just received a packet that appears to have been originated by us. PeerID: %s Subscriber: %s TS: %s, TGID: %s', _network, int_id(_peerid), int_id(_src_sub), int(_ts)+1, int_id(_dst_group))
|
||||
self._logger.error('(%s) Just received a packet that appears to have been originated by us. PeerID: %s Subscriber: %s TS: %s, TGID: %s', self._system, int_id(_peerid), int_id(_src_sub), int(_ts), int_id(_dst_group))
|
||||
return
|
||||
|
||||
if trigger == False:
|
||||
if (_ts == 0 and _dst_group not in trigger_groups_1) or (_ts == 1 and _dst_group not in trigger_groups_2):
|
||||
if (_ts == 1 and _dst_group not in trigger_groups_1) or (_ts == 2 and _dst_group not in trigger_groups_2):
|
||||
return
|
||||
else:
|
||||
if (_ts == 0 and _dst_group in trigger_groups_1) or (_ts == 1 and _dst_group in trigger_groups_2):
|
||||
if (_ts == 1 and _dst_group not in trigger_groups_1) or (_ts == 2 and _dst_group not in trigger_groups_2):
|
||||
return
|
||||
|
||||
logger.info('(%s) Event ID: %s - Playback triggered from SourceID: %s, TS: %s, TGID: %s, PeerID: %s', _network, self.event_id, int_id(_src_sub), _ts+1, int_id(_dst_group), int_id(_peerid))
|
||||
self._logger.info('(%s) Event ID: %s - Playback triggered from SourceID: %s, TS: %s, TGID: %s, PeerID: %s', self._system, self.event_id, int_id(_src_sub), _ts, int_id(_dst_group), int_id(_peerid))
|
||||
|
||||
# Determine the type of voice packet this is (see top of file for possible types)
|
||||
_burst_data_type = _data[30]
|
||||
|
||||
time.sleep(2)
|
||||
self.CALL_DATA = pickle.load(open(filename, 'rb'))
|
||||
logger.info('(%s) Event ID: %s - Playing back file: %s', _network, self.event_id, filename)
|
||||
self._logger.info('(%s) Event ID: %s - Playing back file: %s', self._system, self.event_id, filename)
|
||||
|
||||
for i in self.CALL_DATA:
|
||||
_tmp_data = i
|
||||
|
@ -113,9 +107,9 @@ class playIPSC(IPSC):
|
|||
|
||||
# Re-Write IPSC timeslot value
|
||||
_call_info = int_id(_tmp_data[17:18])
|
||||
if _ts == 0:
|
||||
if _ts == 1:
|
||||
_call_info &= ~(1 << 5)
|
||||
elif _ts == 1:
|
||||
elif _ts == 2:
|
||||
_call_info |= 1 << 5
|
||||
_call_info = chr(_call_info)
|
||||
_tmp_data = _tmp_data[:17] + _call_info + _tmp_data[18:]
|
||||
|
@ -124,9 +118,9 @@ class playIPSC(IPSC):
|
|||
# Determine if the slot is present, so we can translate if need be
|
||||
if _burst_data_type == BURST_DATA_TYPE['SLOT1_VOICE'] or _burst_data_type == BURST_DATA_TYPE['SLOT2_VOICE']:
|
||||
# Re-Write timeslot if necessary...
|
||||
if _ts == 0:
|
||||
if _ts == 1:
|
||||
_burst_data_type = BURST_DATA_TYPE['SLOT1_VOICE']
|
||||
elif _ts == 1:
|
||||
elif _ts == 2:
|
||||
_burst_data_type = BURST_DATA_TYPE['SLOT2_VOICE']
|
||||
_tmp_data = _tmp_data[:30] + _burst_data_type + _tmp_data[31:]
|
||||
|
||||
|
@ -134,13 +128,58 @@ class playIPSC(IPSC):
|
|||
self.send_to_ipsc(_tmp_data)
|
||||
time.sleep(0.06)
|
||||
self.CALL_DATA = []
|
||||
logger.info('(%s) Event ID: %s - Playback Completed', _network, self.event_id)
|
||||
self._logger.info('(%s) Event ID: %s - Playback Completed', self._system, self.event_id)
|
||||
self.event_id = self.event_id + 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'record.py\' (c) 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = playIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
reactor.run()
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = playIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
98
playback.py
98
playback.py
|
@ -22,10 +22,11 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from twisted.internet import reactor
|
||||
from binascii import b2a_hex as h
|
||||
from binascii import b2a_hex as ahex
|
||||
|
||||
import sys, time
|
||||
from dmrlink import IPSC, NETWORK, networks, logger, dmr_nat, int_id, hex_str_3
|
||||
from dmrlink import IPSC, systems
|
||||
from dmr_utils.utils import int_id, hex_str_3
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -43,62 +44,66 @@ except ImportError:
|
|||
HEX_TGID = hex_str_3(TGID)
|
||||
HEX_SUB = hex_str_3(SUB)
|
||||
BOGUS_SUB = '\xFF\xFF\xFF'
|
||||
if GROUP_SRC_SUB:
|
||||
logger.info('Playback: USING SUBSCRIBER ID: %s FOR GROUP REPEAT', GROUP_SRC_SUB)
|
||||
HEX_GRP_SUB = hex_str_3(GROUP_SRC_SUB)
|
||||
|
||||
class playbackIPSC(IPSC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.CALL_DATA = []
|
||||
|
||||
if GROUP_SRC_SUB:
|
||||
self._logger.info('Playback: USING SUBSCRIBER ID: %s FOR GROUP REPEAT', GROUP_SRC_SUB)
|
||||
self.GROUP_SRC_SUB = hex_str_3(GROUP_SRC_SUB)
|
||||
|
||||
if GROUP_REPEAT:
|
||||
self._logger.info('Playback: GROUP REPEAT ENABLED')
|
||||
|
||||
if PRIVATE_REPEAT:
|
||||
self._logger.info('Playback: PRIVATE REPEAT ENABLED')
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
#
|
||||
if GROUP_REPEAT:
|
||||
logger.info('Playback: DEFINING GROUP REPEAT FUNCTION')
|
||||
def group_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def group_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if HEX_TGID == _dst_sub and _ts in GROUP_TS:
|
||||
if not _end:
|
||||
if not self.CALL_DATA:
|
||||
logger.info('(%s) Receiving transmission to be played back from subscriber: %s', _network, int_id(_src_sub))
|
||||
self._logger.info('(%s) Receiving transmission to be played back from subscriber: %s', self._system, int_id(_src_sub))
|
||||
_tmp_data = _data
|
||||
#_tmp_data = dmr_nat(_data, _src_sub, NETWORK[_network]['LOCAL']['RADIO_ID'])
|
||||
#_tmp_data = dmr_nat(_data, _src_sub, self._config['LOCAL']['RADIO_ID'])
|
||||
self.CALL_DATA.append(_tmp_data)
|
||||
if _end:
|
||||
self.CALL_DATA.append(_data)
|
||||
time.sleep(2)
|
||||
logger.info('(%s) Playing back transmission from subscriber: %s', _network, int_id(_src_sub))
|
||||
self._logger.info('(%s) Playing back transmission from subscriber: %s', self._system, int_id(_src_sub))
|
||||
for i in self.CALL_DATA:
|
||||
_tmp_data = i
|
||||
_tmp_data = _tmp_data.replace(_peerid, NETWORK[_network]['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._config['LOCAL']['RADIO_ID'])
|
||||
if GROUP_SRC_SUB:
|
||||
_tmp_data = _tmp_data.replace(_src_sub, HEX_GRP_SUB)
|
||||
_tmp_data = _tmp_data.replace(_src_sub, self.GROUP_SRC_SUB)
|
||||
# Send the packet to all peers in the target IPSC
|
||||
self.send_to_ipsc(_tmp_data)
|
||||
time.sleep(0.06)
|
||||
self.CALL_DATA = []
|
||||
|
||||
if PRIVATE_REPEAT:
|
||||
logger.info('Playback: DEFINING PRIVATE REPEAT FUNCTION')
|
||||
def private_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def private_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if HEX_SUB == _dst_sub and _ts in PRIVATE_TS:
|
||||
if not _end:
|
||||
if not self.CALL_DATA:
|
||||
logger.info('(%s) Receiving transmission to be played back from subscriber: %s, to subscriber: %s', _network, int_id(_src_sub), int_id(_dst_sub))
|
||||
self._logger.info('(%s) Receiving transmission to be played back from subscriber: %s, to subscriber: %s', self._system, int_id(_src_sub), int_id(_dst_sub))
|
||||
_tmp_data = _data
|
||||
self.CALL_DATA.append(_tmp_data)
|
||||
if _end:
|
||||
self.CALL_DATA.append(_data)
|
||||
time.sleep(1)
|
||||
logger.info('(%s) Playing back transmission from subscriber: %s, to subscriber %s', _network, int_id(_src_sub), int_id(_dst_sub))
|
||||
self._logger.info('(%s) Playing back transmission from subscriber: %s, to subscriber %s', self._system, int_id(_src_sub), int_id(_dst_sub))
|
||||
_orig_src = _src_sub
|
||||
_orig_dst = _dst_sub
|
||||
for i in self.CALL_DATA:
|
||||
_tmp_data = i
|
||||
_tmp_data = _tmp_data.replace(_peerid, NETWORK[_network]['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._config['LOCAL']['RADIO_ID'])
|
||||
_tmp_data = _tmp_data.replace(_dst_sub, BOGUS_SUB)
|
||||
_tmp_data = _tmp_data.replace(_src_sub, _orig_dst)
|
||||
_tmp_data = _tmp_data.replace(BOGUS_SUB, _orig_src)
|
||||
|
@ -107,10 +112,55 @@ class playbackIPSC(IPSC):
|
|||
time.sleep(0.06)
|
||||
self.CALL_DATA = []
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'playback.py\' (c) 2013, 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = playbackIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = playbackIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
||||
|
|
115
rcm.py
115
rcm.py
|
@ -26,12 +26,13 @@ from __future__ import print_function
|
|||
from twisted.internet.protocol import DatagramProtocol
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
from binascii import b2a_hex as h
|
||||
from binascii import b2a_hex as ahex
|
||||
|
||||
import datetime
|
||||
import binascii
|
||||
import dmrlink
|
||||
from dmrlink import IPSC, NETWORK, networks, get_info, int_id, subscriber_ids, peer_ids, talkgroup_ids, logger
|
||||
from dmrlink import IPSC, systems
|
||||
from dmr_utils.utils import get_alias, int_id
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2013, 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -51,15 +52,14 @@ rpt = True
|
|||
nack = True
|
||||
|
||||
class rcmIPSC(IPSC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
#
|
||||
def call_mon_status(self, _network, _data):
|
||||
def call_mon_status(self, _data):
|
||||
if not status:
|
||||
return
|
||||
_source = _data[1:5]
|
||||
|
@ -73,19 +73,19 @@ class rcmIPSC(IPSC):
|
|||
_prio = _data[23]
|
||||
_sec = _data[24]
|
||||
|
||||
_source = str(int_id(_source)) + ', ' + str(get_info(int_id(_source), peer_ids))
|
||||
_ipsc_src = str(int_id(_ipsc_src)) + ', ' + str(get_info(int_id(_ipsc_src), peer_ids))
|
||||
_rf_src = str(int_id(_rf_src)) + ', ' + str(get_info(int_id(_rf_src), subscriber_ids))
|
||||
_source = str(int_id(_source)) + ', ' + str(get_alias(_source, peer_ids))
|
||||
_ipsc_src = str(int_id(_ipsc_src)) + ', ' + str(get_alias(_ipsc_src, peer_ids))
|
||||
_rf_src = str(int_id(_rf_src)) + ', ' + str(get_alias(_rf_src, subscriber_ids))
|
||||
|
||||
if _type == '\x4F' or '\x51':
|
||||
_rf_tgt = 'TGID: ' + str(int_id(_rf_tgt)) + ', ' + str(get_info(int_id(_rf_tgt), talkgroup_ids))
|
||||
_rf_tgt = 'TGID: ' + str(int_id(_rf_tgt)) + ', ' + str(get_alias(_rf_tgt, talkgroup_ids))
|
||||
else:
|
||||
_rf_tgt = 'SID: ' + str(int_id(_rf_tgt)) + ', ' + str(get_info(int_id(_rf_tgt), subscriber_ids))
|
||||
_rf_tgt = 'SID: ' + str(int_id(_rf_tgt)) + ', ' + str(get_alias(_rf_tgt, subscriber_ids))
|
||||
|
||||
print('Call Monitor - Call Status')
|
||||
print('TIME: ', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
print('DATA SOURCE: ', _source)
|
||||
print('IPSC: ', _network)
|
||||
print('IPSC: ', self._system)
|
||||
print('IPSC Source: ', _ipsc_src)
|
||||
print('Timeslot: ', TS[_ts])
|
||||
try:
|
||||
|
@ -100,14 +100,14 @@ class rcmIPSC(IPSC):
|
|||
print('Target Sub: ', _rf_tgt)
|
||||
print()
|
||||
|
||||
def call_mon_rpt(self, _network, _data):
|
||||
def call_mon_rpt(self, _data):
|
||||
if not rpt:
|
||||
return
|
||||
_source = _data[1:5]
|
||||
_ts1_state = _data[5]
|
||||
_ts2_state = _data[6]
|
||||
|
||||
_source = str(int_id(_source)) + ', ' + str(get_info(int_id(_source), peer_ids))
|
||||
_source = str(int_id(_source)) + ', ' + str(get_alias(_source, peer_ids))
|
||||
|
||||
print('Call Monitor - Repeater State')
|
||||
print('TIME: ', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
|
@ -123,13 +123,13 @@ class rcmIPSC(IPSC):
|
|||
print('TS2 State (unknown): ', h(_ts2_state))
|
||||
print()
|
||||
|
||||
def call_mon_nack(self, _network, _data):
|
||||
def call_mon_nack(self, _data):
|
||||
if not nack:
|
||||
return
|
||||
_source = _data[1:5]
|
||||
_nack = _data[5]
|
||||
|
||||
_source = get_info(int_id(_source), peer_ids)
|
||||
_source = get_alias(_source, peer_ids)
|
||||
|
||||
print('Call Monitor - Transmission NACK')
|
||||
print('TIME: ', datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
|
@ -140,17 +140,84 @@ class rcmIPSC(IPSC):
|
|||
print('NACK Cause (unknown): ', h(_nack))
|
||||
print()
|
||||
|
||||
def repeater_wake_up(self, _network, _data):
|
||||
def repeater_wake_up(self, _data):
|
||||
_source = _data[1:5]
|
||||
_source_dec = int_id(_source)
|
||||
_source_name = get_info(_source_dec, peer_ids)
|
||||
#print('({}) Repeater Wake-Up Packet Received: {} ({})' .format(_network, _source_name, _source_dec))
|
||||
_source_name = get_alias(_source, peer_ids)
|
||||
print('({}) Repeater Wake-Up Packet Received: {} ({})' .format(self._system, _source_name, int_id(_source)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'rcm.py\' (c) 2013, 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = rcmIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
|
||||
# ID ALIAS CREATION
|
||||
# Download
|
||||
if CONFIG['ALIASES']['TRY_DOWNLOAD'] == True:
|
||||
# Try updating peer aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'], CONFIG['ALIASES']['PEER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
# Try updating subscriber aliases file
|
||||
result = try_download(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'], CONFIG['ALIASES']['SUBSCRIBER_URL'], CONFIG['ALIASES']['STALE_TIME'])
|
||||
logger.info(result)
|
||||
|
||||
# Make Dictionaries
|
||||
peer_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['PEER_FILE'])
|
||||
if peer_ids:
|
||||
logger.info('ID ALIAS MAPPER: peer_ids dictionary is available')
|
||||
|
||||
subscriber_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['SUBSCRIBER_FILE'])
|
||||
if subscriber_ids:
|
||||
logger.info('ID ALIAS MAPPER: subscriber_ids dictionary is available')
|
||||
|
||||
talkgroup_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['TGID_FILE'])
|
||||
if talkgroup_ids:
|
||||
logger.info('ID ALIAS MAPPER: talkgroup_ids dictionary is available')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = rcmIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
80
record.py
80
record.py
|
@ -27,7 +27,8 @@ from binascii import b2a_hex as h
|
|||
|
||||
import sys
|
||||
import cPickle as pickle
|
||||
from dmrlink import IPSC, NETWORK, networks, logger, int_id, hex_str_3
|
||||
from dmrlink import IPSC, systems
|
||||
from dmr_utils.utils import hex_str_3, int_id
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -49,11 +50,11 @@ while True:
|
|||
ts = raw_input('Which timeslot (1, 2 or \'both\')? ')
|
||||
if ts == '1' or ts == '2' or ts =='both':
|
||||
if ts == '1':
|
||||
ts = (0,)
|
||||
if ts == '2':
|
||||
ts = (1,)
|
||||
if ts == '2':
|
||||
ts = (2,)
|
||||
if ts == 'both':
|
||||
ts = (0,1)
|
||||
ts = (1,2)
|
||||
break
|
||||
print('...input must be \'1\', \'2\' or \'both\'')
|
||||
|
||||
|
@ -64,9 +65,8 @@ id = hex_str_3(id)
|
|||
filename = raw_input('Filename to use for this recording? ')
|
||||
|
||||
class recordIPSC(IPSC):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
IPSC.__init__(self, *args, **kwargs)
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.CALL_DATA = []
|
||||
|
||||
#************************************************
|
||||
|
@ -75,39 +75,83 @@ class recordIPSC(IPSC):
|
|||
#
|
||||
if tx_type == 'g':
|
||||
print('Initializing to record GROUP VOICE transmission')
|
||||
def group_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def group_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if id == _dst_sub and _ts in ts:
|
||||
if not _end:
|
||||
if not self.CALL_DATA:
|
||||
print('({}) Recording transmission from subscriber: {}' .format(_network, int_id(_src_sub)))
|
||||
print('({}) Recording transmission from subscriber: {}' .format(self._system, int_id(_src_sub)))
|
||||
self.CALL_DATA.append(_data)
|
||||
if _end:
|
||||
self.CALL_DATA.append(_data)
|
||||
print('({}) Transmission ended, writing to disk: {}' .format(_network, filename))
|
||||
print('({}) Transmission ended, writing to disk: {}' .format(self._system, filename))
|
||||
pickle.dump(self.CALL_DATA, open(filename, 'wb'))
|
||||
reactor.stop()
|
||||
print('Recording created, program terminating')
|
||||
|
||||
if tx_type == 'p':
|
||||
print('Initializing ro record PRIVATE VOICE transmission')
|
||||
def private_voice(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
def private_voice(self, _src_sub, _dst_sub, _ts, _end, _peerid, _data):
|
||||
if id == _dst_sub and _ts in ts:
|
||||
if not _end:
|
||||
if not self.CALL_DATA:
|
||||
print('({}) Recording transmission from subscriber: {}' .format(_network, int_id(_src_sub)))
|
||||
print('({}) Recording transmission from subscriber: {}' .format(self._system, int_id(_src_sub)))
|
||||
self.CALL_DATA.append(_data)
|
||||
if _end:
|
||||
self.CALL_DATA.append(_data)
|
||||
print('({}) Transmission ended, writing to disk: {}' .format(_network, filename))
|
||||
print('({}) Transmission ended, writing to disk: {}' .format(self._system, filename))
|
||||
pickle.dump(self.CALL_DATA, open(filename, 'wb'))
|
||||
reactor.stop()
|
||||
print('Recording created, program terminating')
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
# 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='CFG_FILE', help='/full/path/to/config.file (usually dmrlink.cfg)')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
if not cli_args.CFG_FILE:
|
||||
cli_args.CFG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
||||
|
||||
# Call the external routine to build the configuration dictionary
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'record.py\' (c) 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
for ipsc_network in NETWORK:
|
||||
if NETWORK[ipsc_network]['LOCAL']['ENABLED']:
|
||||
networks[ipsc_network] = recordIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
|
||||
for system in systems:
|
||||
this_ipsc = systems[system]
|
||||
logger.info('De-Registering from IPSC %s', system)
|
||||
de_reg_req_pkt = this_ipsc.hashed_packet(this_ipsc._local['AUTH_KEY'], this_ipsc.DE_REG_REQ_PKT)
|
||||
this_ipsc.send_to_ipsc(de_reg_req_pkt)
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = recordIPSC(system, CONFIG, logger)
|
||||
reactor.listenUDP(CONFIG['SYSTEMS'][system]['LOCAL']['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
|
||||
reactor.run()
|
||||
|
|
75733
subscriber_ids.csv
75733
subscriber_ids.csv
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1 @@
|
|||
Worldwide,1
|
||||
Local,2
|
||||
North America,3
|
||||
BrandMeister,9
|
||||
Worldwide English,13
|
||||
TAC 310,310
|
||||
DCI Bridge 2,3100
|
||||
DCI 1,3160
|
||||
Midwest,3169
|
||||
Northeast,3172
|
||||
Southeast,3174
|
||||
Flordia,3112
|
||||
Kansas Statewide,3120
|
||||
Massachussetts,3125
|
||||
Missouri,3129
|
||||
DCI Comm 1,3777215
|
||||
Echo Server,9998
|
||||
1,Worldwide
2,Local
3,North America
9,BrandMeister
13,Worldwide English
310,TAC 310
3100,DCI Bridge 2
3160,DCI 1
3169,Midwest
3172,Northeast
3174,Southeast
3112,Flordia
3120,Kansas Statewide
3125,Massachussetts
3129,Missouri
31201,BYRG KC
3777215,DCI Comm 1
9998,Echo Server
|
|
Loading…
Reference in New Issue