Merge branch 'socket-reporting'
This commit is contained in:
commit
fa8e53bd81
|
@ -18,3 +18,4 @@ sub_acl.py
|
|||
*.config
|
||||
*.json
|
||||
*.pickle
|
||||
*.csv
|
||||
|
|
|
@ -29,7 +29,7 @@ from bitstring import BitArray
|
|||
import sys, socket, ConfigParser, thread, traceback
|
||||
import cPickle as pickle
|
||||
|
||||
from dmrlink import IPSC, systems
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
from dmr_utils.utils import int_id, hex_str_3, hex_str_4, get_alias, get_info
|
||||
|
||||
from time import time, sleep, clock, localtime, strftime
|
||||
|
@ -93,8 +93,8 @@ class ambeIPSC(IPSC):
|
|||
#_d = None
|
||||
###### DEBUGDEBUGDEBUG
|
||||
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
self.CALL_DATA = []
|
||||
|
||||
#
|
||||
|
@ -622,13 +622,12 @@ def get_subscriber_info(_src_sub):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -644,60 +643,36 @@ if __name__ == '__main__':
|
|||
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)
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'ambe_audio.py\' (c) 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# 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.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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()
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, ambeIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
80
bridge.py
80
bridge.py
|
@ -54,7 +54,7 @@ import sys
|
|||
|
||||
from dmr_utils.utils import hex_str_3, hex_str_4, int_id
|
||||
|
||||
from dmrlink import IPSC, systems, config_reports
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, REPORT_OPCODES, build_aliases, config_reports
|
||||
from ipsc.ipsc_const import BURST_DATA_TYPE
|
||||
|
||||
|
||||
|
@ -70,6 +70,7 @@ __email__ = 'n0mjs@me.com'
|
|||
#
|
||||
TS_CLEAR_TIME = .2
|
||||
|
||||
|
||||
# Import Bridging rules
|
||||
# Note: A stanza *must* exist for any IPSC configured in the main
|
||||
# configuration file and listed as "active". It can be empty,
|
||||
|
@ -158,7 +159,7 @@ def build_acl(_sub_acl):
|
|||
|
||||
return ACL
|
||||
|
||||
|
||||
|
||||
# Run this every minute for rule timer updates
|
||||
def rule_timer_loop():
|
||||
logger.debug('(ALL IPSC) Rule timer loop started')
|
||||
|
@ -186,9 +187,9 @@ def rule_timer_loop():
|
|||
|
||||
|
||||
class bridgeIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger, _bridges):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
self.BRIDGES = _bridges
|
||||
def __init__(self, _name, _config, _logger, report):
|
||||
IPSC.__init__(self, _name, _config, _logger, report)
|
||||
self.BRIDGES = BRIDGES
|
||||
if self.BRIDGES:
|
||||
self._logger.info('(%s) Initializing backup/polite bridging', self._system)
|
||||
self.BRIDGE = False
|
||||
|
@ -438,15 +439,14 @@ class bridgeIPSC(IPSC):
|
|||
# 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 sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -460,39 +460,32 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
config_reports(CONFIG)
|
||||
|
||||
logger.info('DMRlink \'bridge.py\' (c) 2013-2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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)
|
||||
|
||||
|
||||
|
||||
# BRIDGE.PY SPECIFIC ITEMS GO HERE:
|
||||
|
||||
# Build the routing rules file
|
||||
RULES = build_rules('bridge_rules')
|
||||
|
||||
|
@ -501,22 +494,25 @@ if __name__ == '__main__':
|
|||
|
||||
# Build the Access Control List
|
||||
ACL = build_acl('sub_acl')
|
||||
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
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']:
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP IF CONFIGURED
|
||||
rule_timer = task.LoopingCall(rule_timer_loop)
|
||||
rule_timer.start(60)
|
||||
|
||||
|
||||
|
||||
# MAIN INITIALIZATION ITEMS HERE
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGURED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, bridgeIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
||||
|
|
267
confbridge.py
267
confbridge.py
|
@ -44,20 +44,21 @@
|
|||
# Use to make test strings: #print('PKT:', "\\x".join("{:02x}".format(ord(c)) for c in _data))
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from twisted.internet.protocol import Factory, Protocol
|
||||
from twisted.protocols.basic import NetstringReceiver
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
|
||||
from binascii import b2a_hex as ahex
|
||||
from time import time
|
||||
from importlib import import_module
|
||||
import cPickle as pickle
|
||||
|
||||
import sys
|
||||
import cPickle as pickle
|
||||
|
||||
from dmr_utils.utils import hex_str_3, hex_str_4, int_id
|
||||
|
||||
from dmrlink import IPSC, systems, config_reports, hmac_new, sha1
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, REPORT_OPCODES, build_aliases
|
||||
from ipsc.ipsc_const import BURST_DATA_TYPE
|
||||
|
||||
|
||||
|
@ -73,6 +74,47 @@ __email__ = 'n0mjs@me.com'
|
|||
#
|
||||
TS_CLEAR_TIME = .2
|
||||
|
||||
# Declare this here so that we can define functions around it
|
||||
#
|
||||
BRIDGES = {}
|
||||
|
||||
# Timed loop used for reporting IPSC status
|
||||
#
|
||||
# REPORT BASED ON THE TYPE SELECTED IN THE MAIN CONFIG FILE
|
||||
def config_reports(_config, _logger, _factory):
|
||||
if _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)
|
||||
|
||||
reporting = task.LoopingCall(reporting_loop, _logger)
|
||||
reporting.start(_config['REPORTS']['REPORT_INTERVAL'])
|
||||
report_server = False
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
def reporting_loop(_logger, _server):
|
||||
_logger.debug('Periodic Reporting Loop Started (NETWORK)')
|
||||
_server.send_config()
|
||||
_server.send_bridge()
|
||||
|
||||
_logger.info('DMRlink TCP reporting server starting')
|
||||
|
||||
report_server = _factory(_config, _logger)
|
||||
report_server.clients = []
|
||||
reactor.listenTCP(_config['REPORTS']['REPORT_PORT'], report_server)
|
||||
|
||||
reporting = task.LoopingCall(reporting_loop, _logger, report_server)
|
||||
reporting.start(_config['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
else:
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
report_server = False
|
||||
|
||||
return report_server
|
||||
|
||||
# Build the conference bridging structure from the bridge file.
|
||||
#
|
||||
def make_bridge_config(_confbridge_rules):
|
||||
|
@ -87,9 +129,7 @@ def make_bridge_config(_confbridge_rules):
|
|||
#
|
||||
for _bridge in bridge_file.BRIDGES:
|
||||
for _system in bridge_file.BRIDGES[_bridge]:
|
||||
from pprint import pprint
|
||||
if _system['SYSTEM'] not in CONFIG['SYSTEMS']:
|
||||
print(_system['SYSTEM'])
|
||||
sys.exit('ERROR: Conference bridges found for system not configured main configuration')
|
||||
|
||||
_system['TGID'] = hex_str_3(_system['TGID'])
|
||||
|
@ -140,39 +180,7 @@ def build_acl(_sub_acl):
|
|||
return True
|
||||
|
||||
return ACL
|
||||
|
||||
# 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)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
_logger.error('I/O Error: %s', detail)
|
||||
|
||||
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)
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
def reporting_loop(_logger, _server):
|
||||
_logger.debug('Periodic Reporting Loop Started (NETWORK)')
|
||||
_server.send_config()
|
||||
_server.send_bridge()
|
||||
|
||||
else:
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
|
||||
return reporting_loop
|
||||
|
||||
# Run this every minute for rule timer updates
|
||||
def rule_timer_loop():
|
||||
|
@ -204,20 +212,13 @@ def rule_timer_loop():
|
|||
else:
|
||||
logger.debug('Conference Bridge NO ACTION: System: %s, Bridge: %s, TS: %s, TGID: %s', _system['SYSTEM'], _bridge, _system['TS'], int_id(_system['TGID']))
|
||||
|
||||
if BRIDGE_CONF['REPORT'] == 'pickle':
|
||||
try:
|
||||
with open(CONFIG['REPORTS']['REPORT_PATH']+'confbridge_stats.pickle', 'wb') as file:
|
||||
pickle_dump(BRIDGES, file, 2)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
_logger.error('I/O Error: %s', detail)
|
||||
elif BRIDGE_CONF['REPORT'] == 'network':
|
||||
if BRIDGE_CONF['REPORT'] == 'network':
|
||||
report_server.send_clients('bridge updated')
|
||||
|
||||
|
||||
class confbridgeIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
|
||||
self.STATUS = {
|
||||
1: {'RX_TGID':'\x00', 'TX_TGID':'\x00', 'RX_TIME':0, 'TX_TIME':0, 'RX_SRC_SUB':'\x00', 'TX_SRC_SUB':'\x00'},
|
||||
|
@ -346,18 +347,25 @@ class confbridgeIPSC(IPSC):
|
|||
|
||||
# Action happens on key up
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
if self.last_seq_id != _seq_id:
|
||||
if self.last_seq_id != _seq_id or (self.call_start + TS_CLEAR_TIME) < now:
|
||||
self.last_seq_id = _seq_id
|
||||
self.call_start = time()
|
||||
self.call_start = now
|
||||
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))
|
||||
|
||||
if self._CONFIG['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
self._report.send_bridgeEvent('({}) GROUP VOICE START: CallID: {} PEER: {}, SUB: {}, TS: {}, TGID: {}'.format(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
|
||||
self.call_duration = now - self.call_start
|
||||
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)
|
||||
if self._CONFIG['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
self._report.send_bridgeEvent('({}) GROUP VOICE END: CallID: {} PEER: {}, SUB: {}, TS: {}, TGID: {} Duration: {:.2f}s'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group), self.call_duration))
|
||||
else:
|
||||
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),)
|
||||
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))
|
||||
if self._CONFIG['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
self._report.send_bridgeEvent('(%s) GROUP VOICE END WITHOUT MATCHING START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group)))
|
||||
|
||||
|
||||
# Iterate the rules dictionary
|
||||
for _bridge in BRIDGES:
|
||||
|
@ -402,66 +410,25 @@ class confbridgeIPSC(IPSC):
|
|||
# END IN-BAND SIGNALLING
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Socket-based reporting section
|
||||
#
|
||||
class report(NetstringReceiver):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def connectionMade(self):
|
||||
report_server.clients.append(self)
|
||||
logger.info('DMRlink reporting client connected: %s', self.transport.getPeer())
|
||||
|
||||
def connectionLost(self, reason):
|
||||
logger.info('DMRlink reporting client disconnected: %s', self.transport.getPeer())
|
||||
report_server.clients.remove(self)
|
||||
|
||||
def stringReceived(self, data):
|
||||
self.process_message(data)
|
||||
|
||||
def process_message(self, _message):
|
||||
opcode = _message[:1]
|
||||
if opcode == REP_OPC['CONFIG_REQ']:
|
||||
logger.info('DMRlink reporting client sent \'CONFIG_REQ\': %s', self.transport.getPeer())
|
||||
self.send_config()
|
||||
else:
|
||||
print('got unknown opcode')
|
||||
|
||||
|
||||
class reportFactory(Factory):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
if (addr.host) in CONFIG['REPORTS']['REPORT_CLIENTS']:
|
||||
return report()
|
||||
else:
|
||||
return None
|
||||
|
||||
def send_clients(self, _message):
|
||||
for client in report_server.clients:
|
||||
client.sendString(_message)
|
||||
|
||||
def send_config(self):
|
||||
serialized = pickle.dumps(CONFIG['SYSTEMS'], protocol=pickle.HIGHEST_PROTOCOL)
|
||||
self.send_clients(REP_OPC['CONFIG_SND']+serialized)
|
||||
class confbridgeReportFactory(reportFactory):
|
||||
|
||||
def send_bridge(self):
|
||||
serialized = pickle.dumps(BRIDGES, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
self.send_clients(REP_OPC['BRIDGE_SND']+serialized)
|
||||
|
||||
|
||||
self.send_clients(REPORT_OPCODES['BRIDGE_SND']+serialized)
|
||||
|
||||
def send_bridgeEvent(self, _data):
|
||||
self.send_clients(REPORT_OPCODES['BRDG_EVENT']+_data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
|
||||
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
||||
|
@ -474,58 +441,40 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
config_reports(CONFIG)
|
||||
|
||||
logger.info('DMRlink \'confbridge.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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)
|
||||
|
||||
# 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')
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, confbridgeReportFactory)
|
||||
|
||||
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')
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGURED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, confbridgeIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# CONFBRIDGE.PY SPECIFIC ITEMS GO HERE:
|
||||
|
||||
# Build the routing rules and other configuration
|
||||
CONFIG_DICT = make_bridge_config('confbridge_rules')
|
||||
|
@ -534,34 +483,10 @@ if __name__ == '__main__':
|
|||
|
||||
# Build the Access Control List
|
||||
ACL = build_acl('sub_acl')
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGUED IPSC
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
systems[system] = confbridgeIPSC(system, CONFIG, logger)
|
||||
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'] == 'PRINT' or CONFIG['REPORTS']['REPORT_NETWORKS'] == 'PICKLE':
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
# INITIALIZE THE NETWORK-BASED REPORTING SERVER
|
||||
if CONFIG['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
logger.info('(confbridge.py) TCP reporting server starting')
|
||||
from ipsc.reporting_const import REPORT_OPCODES as REP_OPC
|
||||
|
||||
report_server = reportFactory()
|
||||
report_server.clients = []
|
||||
reactor.listenTCP(CONFIG['REPORTS']['REPORT_PORT'], reportFactory())
|
||||
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger, report_server)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP IF CONFIGURED
|
||||
rule_timer = task.LoopingCall(rule_timer_loop)
|
||||
rule_timer.start(60)
|
||||
|
||||
# Initialize the rule timer loop
|
||||
rule_timer = task.LoopingCall(rule_timer_loop)
|
||||
rule_timer.start(60)
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
||||
|
|
210
dmrlink.py
210
dmrlink.py
|
@ -25,17 +25,11 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
import ConfigParser
|
||||
import argparse
|
||||
import sys
|
||||
import csv
|
||||
import os
|
||||
# Full imports
|
||||
import logging
|
||||
import signal
|
||||
|
||||
import cPickle as pickle
|
||||
|
||||
from logging.config import dictConfig
|
||||
# Function Imports
|
||||
from hmac import new as hmac_new
|
||||
from binascii import b2a_hex as ahex
|
||||
from binascii import a2b_hex as bhex
|
||||
|
@ -44,15 +38,18 @@ from socket import inet_ntoa as IPAddr
|
|||
from socket import inet_aton as IPHexStr
|
||||
from time import time
|
||||
|
||||
# Twisted Imports
|
||||
from twisted.internet.protocol import DatagramProtocol, Factory, Protocol
|
||||
from twisted.protocols.basic import NetstringReceiver
|
||||
from twisted.internet import reactor, task
|
||||
|
||||
# Imports files in the dmrlink subdirectory (these things shouldn't change often)
|
||||
from ipsc.ipsc_const import *
|
||||
from ipsc.ipsc_mask import *
|
||||
from dmrlink_config import build_config
|
||||
from dmrlink_log import config_logging
|
||||
from dmr_utils.utils import hex_str_2, hex_str_3, hex_str_4, int_id
|
||||
from ipsc.reporting_const import *
|
||||
|
||||
# Imports from DMR Utilities package
|
||||
from dmr_utils.utils import hex_str_2, hex_str_3, hex_str_4, int_id, try_download, mk_id_dict
|
||||
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
|
@ -66,37 +63,83 @@ __email__ = 'n0mjs@me.com'
|
|||
# Global variables used whether we are a module or __main__
|
||||
systems = {}
|
||||
|
||||
|
||||
# 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)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
_logger.error('I/O Error: %s', detail)
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'PRINT':
|
||||
def config_reports(_config, _logger, _factory):
|
||||
if _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)
|
||||
|
||||
reporting = task.LoopingCall(reporting_loop, _logger)
|
||||
reporting.start(_config['REPORTS']['REPORT_INTERVAL'])
|
||||
report_server = False
|
||||
|
||||
elif _config['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
def reporting_loop(_logger, _server):
|
||||
_logger.debug('Periodic Reporting Loop Started (NETWORK)')
|
||||
_server.send_config()
|
||||
|
||||
_logger.info('DMRlink TCP reporting server starting')
|
||||
|
||||
report_server = _factory(_config, _logger)
|
||||
report_server.clients = []
|
||||
reactor.listenTCP(_config['REPORTS']['REPORT_PORT'], report_server)
|
||||
|
||||
reporting = task.LoopingCall(reporting_loop, _logger, report_server)
|
||||
reporting.start(_config['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
else:
|
||||
def reporting_loop(_logger):
|
||||
_logger.debug('Periodic Reporting Loop Started (NULL)')
|
||||
report_server = False
|
||||
|
||||
return reporting_loop
|
||||
return report_server
|
||||
|
||||
|
||||
# ID ALIAS CREATION
|
||||
# Download
|
||||
def build_aliases(_config, _logger):
|
||||
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')
|
||||
|
||||
local_ids = mk_id_dict(_config['ALIASES']['PATH'], _config['ALIASES']['LOCAL_FILE'])
|
||||
if local_ids:
|
||||
_logger.info('ID ALIAS MAPPER: local_ids dictionary is available')
|
||||
|
||||
return(peer_ids, subscriber_ids, talkgroup_ids, local_ids)
|
||||
|
||||
|
||||
# Make the IPSC systems from the config and the class used to build them.
|
||||
#
|
||||
def mk_ipsc_systems(_config, _logger, _systems, _ipsc, _report_server):
|
||||
for system in _config['SYSTEMS']:
|
||||
if _config['SYSTEMS'][system]['LOCAL']['ENABLED']:
|
||||
_systems[system] = _ipsc(system, _config, _logger, _report_server)
|
||||
reactor.listenUDP(_config['SYSTEMS'][system]['LOCAL']['PORT'], _systems[system], interface=_config['SYSTEMS'][system]['LOCAL']['IP'])
|
||||
return _systems
|
||||
|
||||
# Process the MODE byte in registration/peer list packets for determining master and peer capabilities
|
||||
#
|
||||
|
@ -236,7 +279,7 @@ def print_master(_config, _network):
|
|||
#************************************************
|
||||
|
||||
class IPSC(DatagramProtocol):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
|
||||
# Housekeeping: create references to the configuration and status data for this IPSC instance.
|
||||
# Some configuration objects that are used frequently and have lengthy names are shortened
|
||||
|
@ -246,7 +289,9 @@ class IPSC(DatagramProtocol):
|
|||
self._system = _name
|
||||
self._CONFIG = _config
|
||||
self._logger = _logger
|
||||
self._report = _report
|
||||
self._config = self._CONFIG['SYSTEMS'][self._system]
|
||||
self._rcm = self._CONFIG['REPORTS']['REPORT_RCM'] and self._report
|
||||
#
|
||||
self._local = self._config['LOCAL']
|
||||
self._local_id = self._local['RADIO_ID']
|
||||
|
@ -320,7 +365,13 @@ class IPSC(DatagramProtocol):
|
|||
else:
|
||||
self._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
|
||||
|
||||
|
||||
# De-register ourselves from the IPSC
|
||||
def de_register_self(self):
|
||||
self._logger.info('(%s) De-Registering self from the IPSC system', self._system)
|
||||
de_reg_req_pkt = self.hashed_packet(self._local['AUTH_KEY'], self.DE_REG_REQ_PKT)
|
||||
self.send_to_ipsc(de_reg_req_pkt)
|
||||
|
||||
# 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.
|
||||
#
|
||||
|
@ -391,15 +442,23 @@ class IPSC(DatagramProtocol):
|
|||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
|
||||
|
||||
# If RCM reporting and reporting is network-based in the global configuration,
|
||||
# send the RCM packet to the monitoring server
|
||||
def call_mon_status(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor Origin Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
if self._rcm:
|
||||
self._report.send_rcm(self._system + ','+ _data)
|
||||
|
||||
def call_mon_rpt(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor Repeating Packet Received: %s', self._system, ahex(_data))
|
||||
|
||||
if self._rcm:
|
||||
self._report.send_rcm(self._system + ',' + _data)
|
||||
|
||||
def call_mon_nack(self, _data):
|
||||
self._logger.debug('(%s) Repeater Call Monitor NACK Packet Received: %s', self._system, ahex(_data))
|
||||
if self._rcm:
|
||||
self._report.send_rcm(self._system + ',' + _data)
|
||||
|
||||
def xcmp_xnl(self, _data):
|
||||
self._logger.debug('(%s) XCMP/XNL Packet Received: %s', self._system, ahex(_data))
|
||||
|
@ -938,45 +997,51 @@ class IPSC(DatagramProtocol):
|
|||
# Socket-based reporting section
|
||||
#
|
||||
class report(NetstringReceiver):
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, factory):
|
||||
self._factory = factory
|
||||
|
||||
def connectionMade(self):
|
||||
report_server.clients.append(self)
|
||||
logger.info('DMRlink reporting client connected: %s', self.transport.getPeer())
|
||||
self._factory.clients.append(self)
|
||||
self._factory._logger.info('DMRlink reporting client connected: %s', self.transport.getPeer())
|
||||
|
||||
def connectionLost(self, reason):
|
||||
logger.info('DMRlink reporting client disconnected: %s', self.transport.getPeer())
|
||||
report_server.clients.remove(self)
|
||||
self._factory._logger.info('DMRlink reporting client disconnected: %s', self.transport.getPeer())
|
||||
self._factory.clients.remove(self)
|
||||
|
||||
def stringReceived(self, data):
|
||||
self.process_message(data)
|
||||
|
||||
def process_message(self, _message):
|
||||
opcode = _message[:1]
|
||||
if opcode == REP_OPC['CONFIG_REQ']:
|
||||
logger.info('DMRlink reporting client sent \'CONFIG_REQ\': %s', self.transport.getPeer())
|
||||
if opcode == REPORT_OPCODES['CONFIG_REQ']:
|
||||
self._factory._logger.info('DMRlink reporting client sent \'CONFIG_REQ\': %s', self.transport.getPeer())
|
||||
self.send_config()
|
||||
else:
|
||||
print('got unknown opcode')
|
||||
|
||||
class reportFactory(Factory):
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, config, logger):
|
||||
self._config = config
|
||||
self._logger = logger
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
if (addr.host) in CONFIG['REPORTS']['REPORT_CLIENTS']:
|
||||
return report()
|
||||
if (addr.host) in self._config['REPORTS']['REPORT_CLIENTS'] or '*' in self._config['REPORTS']['REPORT_CLIENTS']:
|
||||
self._logger.debug('Permitting report server connection attempt from: %s:%s', addr.host, addr.port)
|
||||
return report(self)
|
||||
else:
|
||||
self._logger.error('Invalid report server connection attempt from: %s:%s', addr.host, addr.port)
|
||||
return None
|
||||
|
||||
def send_clients(self, _message):
|
||||
for client in report_server.clients:
|
||||
for client in self.clients:
|
||||
client.sendString(_message)
|
||||
|
||||
def send_config(self):
|
||||
serialized = pickle.dumps(CONFIG['SYSTEMS'], protocol=pickle.HIGHEST_PROTOCOL)
|
||||
self.send_clients(REP_OPC['CONFIG_SND']+serialized)
|
||||
serialized = pickle.dumps(self._config['SYSTEMS'], protocol=pickle.HIGHEST_PROTOCOL)
|
||||
self.send_clients(REPORT_OPCODES['CONFIG_SND']+serialized)
|
||||
|
||||
def send_rcm(self, _data):
|
||||
self.send_clients(REPORT_OPCODES['RCM_SND']+_data)
|
||||
|
||||
|
||||
#************************************************
|
||||
|
@ -984,6 +1049,13 @@ class reportFactory(Factory):
|
|||
#************************************************
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -1007,50 +1079,28 @@ if __name__ == '__main__':
|
|||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
|
||||
config_reports(CONFIG)
|
||||
|
||||
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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] = IPSC(system, CONFIG, logger)
|
||||
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'] == 'PRINT' or CONFIG['REPORTS']['REPORT_NETWORKS'] == 'PICKLE':
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE THE NETWORK-BASED REPORTING SERVER
|
||||
elif CONFIG['REPORTS']['REPORT_NETWORKS'] == 'NETWORK':
|
||||
logger.info('(confbridge.py) TCP reporting server starting')
|
||||
from ipsc.reporting_const import REPORT_OPCODES as REP_OPC
|
||||
|
||||
report_server = reportFactory()
|
||||
report_server.clients = []
|
||||
reactor.listenTCP(CONFIG['REPORTS']['REPORT_PORT'], reportFactory())
|
||||
|
||||
reporting_loop = config_reports(CONFIG)
|
||||
reporting = task.LoopingCall(reporting_loop, logger, report_server)
|
||||
reporting.start(CONFIG['REPORTS']['REPORT_INTERVAL'])
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, IPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
||||
|
|
|
@ -16,9 +16,8 @@ PATH: /opt/dmrlink/
|
|||
# NETWORK REPORTING CONFIGURATION
|
||||
# Enabling "REPORT_NETWORKS" will cause a reporting action for
|
||||
# IPSC each time the periodic reporting loop runs, that period is
|
||||
# specifiec by "REPORT_INTERVAL" in seconds. Possible values
|
||||
# specified by "REPORT_INTERVAL" in seconds. Possible values
|
||||
# for "REPORT_NETWORKS" are:
|
||||
# PICKLE - a Python pickle file of the network's data structure
|
||||
#
|
||||
# PRINT - a pretty print (STDOUT) of the data structure
|
||||
# "PRINT_PEERS_INC_MODE" - Boolean to include mode bits
|
||||
|
@ -31,20 +30,23 @@ PATH: /opt/dmrlink/
|
|||
# goal here is a web dashboard that doesn't live on the
|
||||
# dmrlink machine itself.
|
||||
#
|
||||
# PRINT is the odd man out because it sends prettily formatted stuff
|
||||
# to STDOUT. The others send the internal data structure of the IPSC
|
||||
# instance and let some program on the other end sort it out.
|
||||
# PRINT should only be used for debugging; it sends prettily formatted
|
||||
# stuff to STDOUT. The others send the internal data structure of the
|
||||
# IPSC instance and let some program on the other end sort it out.
|
||||
#
|
||||
# REPORT_RCM - If True, and REPORT_NETWORKS = 'NETWORK', will send RCM
|
||||
# Packets to connected reporting clients. This also requires
|
||||
# individual IPSC systems to have RCM and CON_APP both set 'True'
|
||||
#
|
||||
# REPORT_INTERVAL - Seconds between reports
|
||||
# REPORT_PATH - Absolute path save data (pickle and json)
|
||||
# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK
|
||||
# REPORT_CLIENTS - comma separated list of IPs you will allow clients
|
||||
# to connect on.
|
||||
#
|
||||
[REPORTS]
|
||||
REPORT_NETWORKS:
|
||||
REPORT_NETWORKS:
|
||||
REPORT_RCM:
|
||||
REPORT_INTERVAL: 60
|
||||
REPORT_PATH:
|
||||
REPORT_PORT: 4321
|
||||
REPORT_CLIENTS: 127.0.0.1, 192.168.1.1
|
||||
PRINT_PEERS_INC_MODE: 0
|
||||
|
@ -97,13 +99,13 @@ STALE_DAYS: 7
|
|||
#
|
||||
# [NAME] The name you want to use to identify the IPSC instance (use
|
||||
# something better than "IPSC1"...)
|
||||
# ENABLED: Should we communiate with this network? Handy if you need to
|
||||
# ENABLED: Should we communicate with this network? Handy if you need to
|
||||
# shut one down but don't want to lose the config
|
||||
# RADIO_ID: This is the radio ID that DMRLink should use to communicate
|
||||
# IP: This is the local IPv4 address to listen on. It may be left
|
||||
# blank if you do not need or wish to specify. It is mostly
|
||||
# useful when DMRlink uses multiple interfaces to serve as an
|
||||
# application gatway/proxy from private and/or VPN networks
|
||||
# application gateway/proxy from private and/or VPN networks
|
||||
# to the real world.
|
||||
# PORT: This is the UDP source port for DMRLink to use for this
|
||||
# PSC network, must be unique!!!
|
||||
|
@ -117,9 +119,10 @@ STALE_DAYS: 7
|
|||
# CSBK_CALL: Should be False, we cannot process these, but may be useful
|
||||
# for debugging.
|
||||
# RCM: Repeater Call Monitoring - don't unable unless you plan to
|
||||
# actually use it, this craetes extra network traffic.
|
||||
# actually use it, this creates extra network traffic.
|
||||
# CON_APP: Third Party Console App - exactly what DMRlink is, should
|
||||
# be set to True.
|
||||
# be set to True, and must be if you intend to process RCM
|
||||
# packets (like with network-based reporting)
|
||||
# XNL_CALL: Can cause problems if not set to False, DMRlink does not
|
||||
# process XCMP/XNL calls.
|
||||
# XNL_MASTER: Obviously, should also be False, see XNL_CALL.
|
||||
|
@ -129,7 +132,7 @@ STALE_DAYS: 7
|
|||
# AUTH_ENABLED: Do we use authenticated IPSC?
|
||||
# AUTH_KEY: The Authentication key (up to 40 hex characters)
|
||||
# MASTER_IP: IP address of the IPSC master (ignored if DMRlink is the master)
|
||||
# MASTER_PORT: UDP port of the IPSC master (ignored if DMRlinkn is the master)
|
||||
# MASTER_PORT: UDP port of the IPSC master (ignored if DMRlink is the master)
|
||||
# GROUP_HANGTIME: Group hangtime, per DMR configuration
|
||||
#
|
||||
# ...Repeat the block for each IPSC network to join.
|
||||
|
|
35
get_ids.sh
35
get_ids.sh
|
@ -1,35 +0,0 @@
|
|||
#! /bin/sh
|
||||
|
||||
###################################################
|
||||
# #
|
||||
# Download Subscriber and Peer ID's from DMR-MARC #
|
||||
# #
|
||||
###################################################
|
||||
|
||||
# To provide more readable output from DMRlink with current subscriber and repeater IDs, we download the CSV files from DMR-MARC
|
||||
# If you are going to use this in a cron task, don't run it more then once a day.
|
||||
# It might be good to find alternale a source as a backup.
|
||||
|
||||
# <http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi>
|
||||
|
||||
# wget -O users.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0"
|
||||
|
||||
# Options are:
|
||||
|
||||
# table { users | repeaters }
|
||||
|
||||
# format { table | csv | csvq | json }
|
||||
|
||||
# header { 0 | 1 } (only applies to table and csv formats)
|
||||
|
||||
# id { nnnnnn } (query an individual record)
|
||||
|
||||
# Get the user IDs.
|
||||
wget -O subscriber_ids.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0"
|
||||
|
||||
# Get the peer IDs
|
||||
wget -O peer_ids.csv -q "http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=repeaters&format=csv&header=0"
|
||||
|
||||
# Tell ambe_audio to re-read the files
|
||||
echo -n "reread_subscribers" | nc 127.0.0.1 31002
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2017 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of tde GNU General Public License as published by
|
||||
# the Free Software Foundation; eitder version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
from cPickle import load
|
||||
from pprint import pprint
|
||||
from time import time, strftime, localtime
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
from binascii import b2a_hex as h
|
||||
from dmr_utils.utils import int_id, get_alias
|
||||
from os.path import getmtime
|
||||
|
||||
__autdor__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2017 Cortney T. Buffington, N0MJS'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
|
||||
# This is the only user-configuration necessary
|
||||
# Tell the program where the pickle file is
|
||||
# Tell the program where to write the html table file
|
||||
# Tell the program how often to print a report -- should match dmrlink report period
|
||||
stat_file = '../confbridge_stats.pickle'
|
||||
html_table_file = '../confbridge_stats.html'
|
||||
frequency = 10
|
||||
|
||||
def read_dict():
|
||||
try:
|
||||
with open(stat_file, 'rb') as file:
|
||||
BRIDGES = load(file)
|
||||
return BRIDGES
|
||||
except IOError as detail:
|
||||
print('I/O Error: {}'.format(detail))
|
||||
except EOFError:
|
||||
print('EOFError')
|
||||
|
||||
def write_file(_string):
|
||||
try:
|
||||
with open(html_table_file, 'w') as file:
|
||||
file.write(_string)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
print('I/O Error: {}'.format(detail))
|
||||
except EOFError:
|
||||
print('EOFError')
|
||||
|
||||
def build_table():
|
||||
_now = time()
|
||||
_last_update = strftime('%Y-%m-%d %H:%M:%S', localtime(getmtime(stat_file)))
|
||||
_cnow = strftime('%Y-%m-%d %H:%M:%S', localtime(_now))
|
||||
BRIDGES = read_dict()
|
||||
if BRIDGES != 'None':
|
||||
|
||||
stuff = 'Table Generated: {}<br>'.format(_cnow)
|
||||
stuff += 'Last Stat Data Recieved: {}<br>'.format(_last_update)
|
||||
|
||||
#style="font: 10pt arial, sans-serif;"
|
||||
|
||||
for bridge in BRIDGES:
|
||||
stuff += '<style>table, td, th {border: .5px solid black; padding: 2px; border-collapse: collapse}</style>'
|
||||
stuff += '<table style="width:90%; font: 10pt arial, sans-serif">'
|
||||
stuff += '<colgroup>\
|
||||
<col style="width: 20%" />\
|
||||
<col style="width: 5%" />\
|
||||
<col style="width: 5%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 10%" />\
|
||||
</colgroup>'
|
||||
stuff += '<caption>{}</caption>'.format(bridge)
|
||||
stuff += '<tr><th>System</th>\
|
||||
<th>Slot</th>\
|
||||
<th>TGID</th>\
|
||||
<th>Status</th>\
|
||||
<th>Timeout</th>\
|
||||
<th>Timeout Action</th>\
|
||||
<th>ON Triggers</th>\
|
||||
<th>OFF Triggers</th></tr>'
|
||||
|
||||
|
||||
for system in BRIDGES[bridge]:
|
||||
on = ''
|
||||
off = ''
|
||||
active = '<td bgcolor="#FFFF00">Unknown</td>'
|
||||
|
||||
if system['TO_TYPE'] == 'ON' or system['TO_TYPE'] == 'OFF':
|
||||
if system['TIMER'] - _now > 0:
|
||||
exp_time = int(system['TIMER'] - _now)
|
||||
else:
|
||||
exp_time = 'Expired'
|
||||
if system['TO_TYPE'] == 'ON':
|
||||
to_action = 'Turn OFF'
|
||||
else:
|
||||
to_action = 'Turn ON'
|
||||
else:
|
||||
exp_time = 'N/A'
|
||||
to_action = 'None'
|
||||
|
||||
if system['ACTIVE'] == True:
|
||||
active = '<td bgcolor="#00FF00">Connected</td>'
|
||||
elif system['ACTIVE'] == False:
|
||||
active = '<td bgcolor="#FF0000">Disconnected</td>'
|
||||
|
||||
for trigger in system['ON']:
|
||||
on += str(int_id(trigger)) + ' '
|
||||
|
||||
for trigger in system['OFF']:
|
||||
off += str(int_id(trigger)) + ' '
|
||||
|
||||
stuff += '<tr> <td>{}</td> <td>{}</td> <td>{}</td> {} <td>{}</td> <td>{}</td> <td>{}</td> <td>{}</td> </tr>'.format(\
|
||||
system['SYSTEM'],\
|
||||
system['TS'],\
|
||||
int_id(system['TGID']),\
|
||||
active,\
|
||||
exp_time,\
|
||||
to_action,\
|
||||
on,\
|
||||
off)
|
||||
|
||||
stuff += '</table><br>'
|
||||
|
||||
write_file(stuff)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
output_stats = task.LoopingCall(build_table)
|
||||
output_stats.start(frequency)
|
||||
reactor.run()
|
139
html_stats.py
139
html_stats.py
|
@ -1,139 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2017 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of tde GNU General Public License as published by
|
||||
# the Free Software Foundation; eitder version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
from cPickle import load
|
||||
from pprint import pprint
|
||||
from time import time, strftime, localtime
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
from binascii import b2a_hex as h
|
||||
from dmr_utils.utils import int_id, get_alias
|
||||
|
||||
__autdor__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2017 Cortney T. Buffington, N0MJS'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
|
||||
# This is the only user-configuration necessary
|
||||
# Tell the program where the pickle file is
|
||||
# Tell the program where to write the html table file
|
||||
# Tell the program how often to print a report -- should match dmrlink report period
|
||||
stat_file = '../dmrlink_stats.pickle'
|
||||
html_table_file = '../stats.html'
|
||||
frequency = 30
|
||||
|
||||
def read_dict():
|
||||
try:
|
||||
with open(stat_file, 'rb') as file:
|
||||
NETWORK = load(file)
|
||||
return NETWORK
|
||||
except IOError as detail:
|
||||
print('I/O Error: {}'.format(detail))
|
||||
except EOFError:
|
||||
print('EOFError')
|
||||
|
||||
def write_file(_string):
|
||||
try:
|
||||
with open(html_table_file, 'w') as file:
|
||||
file.write(_string)
|
||||
file.close()
|
||||
except IOError as detail:
|
||||
print('I/O Error: {}'.format(detail))
|
||||
except EOFError:
|
||||
print('EOFError')
|
||||
|
||||
def build_table():
|
||||
NETWORK = read_dict()
|
||||
if NETWORK != 'None':
|
||||
_cnow = strftime('%Y-%m-%d %H:%M:%S', localtime(time()))
|
||||
stuff = 'Last Update: {}'.format(_cnow)
|
||||
stuff += '<style>table, td, th {border: .5px solid black; padding: 2px; border-collapse: collapse}</style>'
|
||||
|
||||
for ipsc in NETWORK:
|
||||
stat = NETWORK[ipsc]['MASTER']['STATUS']
|
||||
master = NETWORK[ipsc]['LOCAL']['MASTER_PEER']
|
||||
|
||||
stuff += '<table style="width:90%; font: 10pt arial, sans-serif">'
|
||||
|
||||
stuff += '<colgroup>\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 20%" />\
|
||||
<col style="width: 20%" />\
|
||||
<col style="width: 10%" />\
|
||||
<col style="width: 15%" />\
|
||||
<col style="width: 15%" />\
|
||||
<col style="width: 10%" />\
|
||||
</colgroup>'
|
||||
|
||||
stuff += '<caption>{} '.format(ipsc)
|
||||
if master:
|
||||
stuff += '(master)'
|
||||
else:
|
||||
stuff += '(peer)'
|
||||
stuff +='</caption>'
|
||||
|
||||
stuff += '<tr><th rowspan="2">Type</th>\
|
||||
<th rowspan="2">Radio ID</th>\
|
||||
<th rowspan="2">IP Address</th>\
|
||||
<th rowspan="2">Connected</th>\
|
||||
<th colspan="3">Keep Alives</th></tr>\
|
||||
<tr><th>Sent</th><th>Received</th><th>Missed</th></tr>'
|
||||
|
||||
if not master:
|
||||
stuff += '<tr><td>Master</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>'.format(\
|
||||
str(int_id(NETWORK[ipsc]['MASTER']['RADIO_ID'])).rjust(8,'0'),\
|
||||
NETWORK[ipsc]['MASTER']['IP'],\
|
||||
stat['CONNECTED'],\
|
||||
stat['KEEP_ALIVES_SENT'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'],\
|
||||
stat['KEEP_ALIVES_MISSED'],)
|
||||
|
||||
if master:
|
||||
for peer in NETWORK[ipsc]['PEERS']:
|
||||
stat = NETWORK[ipsc]['PEERS'][peer]['STATUS']
|
||||
stuff += '<tr><td>Peer</td><td>{}</td><td>{}</td><td>{}</td><td>n/a</td><td>{}</td><td>n/a</td></tr>'.format(\
|
||||
str(int_id(peer)).rjust(8,'0'),\
|
||||
NETWORK[ipsc]['PEERS'][peer]['IP'],\
|
||||
stat['CONNECTED'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'])
|
||||
|
||||
else:
|
||||
for peer in NETWORK[ipsc]['PEERS']:
|
||||
stat = NETWORK[ipsc]['PEERS'][peer]['STATUS']
|
||||
if peer != NETWORK[ipsc]['LOCAL']['RADIO_ID']:
|
||||
stuff += '<tr><td>Peer</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>'.format(\
|
||||
str(int_id(peer)).rjust(8,'0'),\
|
||||
NETWORK[ipsc]['PEERS'][peer]['IP'],\
|
||||
stat['CONNECTED'],\
|
||||
stat['KEEP_ALIVES_SENT'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'],\
|
||||
stat['KEEP_ALIVES_MISSED'])
|
||||
stuff += '</table><br>'
|
||||
|
||||
write_file(stuff)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
output_stats = task.LoopingCall(build_table)
|
||||
output_stats.start(frequency)
|
||||
reactor.run()
|
|
@ -54,8 +54,8 @@ def build_config(_config_file):
|
|||
elif section == 'REPORTS':
|
||||
CONFIG['REPORTS'].update({
|
||||
'REPORT_NETWORKS': config.get(section, 'REPORT_NETWORKS'),
|
||||
'REPORT_RCM': config.get(section, 'REPORT_RCM'),
|
||||
'REPORT_INTERVAL': config.getint(section, 'REPORT_INTERVAL'),
|
||||
'REPORT_PATH': config.get(section, 'REPORT_PATH'),
|
||||
'REPORT_PORT': config.get(section, 'REPORT_PORT'),
|
||||
'REPORT_CLIENTS': config.get(section, 'REPORT_CLIENTS').split(','),
|
||||
'PRINT_PEERS_INC_MODE': config.getboolean(section, 'PRINT_PEERS_INC_MODE'),
|
||||
|
@ -63,6 +63,8 @@ def build_config(_config_file):
|
|||
})
|
||||
if CONFIG['REPORTS']['REPORT_PORT']:
|
||||
CONFIG['REPORTS']['REPORT_PORT'] = int(CONFIG['REPORTS']['REPORT_PORT'])
|
||||
if CONFIG['REPORTS']['REPORT_RCM']:
|
||||
CONFIG['REPORTS']['REPORT_RCM'] = bool(CONFIG['REPORTS']['REPORT_RCM'])
|
||||
|
||||
elif section == 'LOGGER':
|
||||
CONFIG['LOGGER'].update({
|
||||
|
@ -79,6 +81,7 @@ def build_config(_config_file):
|
|||
'PEER_FILE': config.get(section, 'PEER_FILE'),
|
||||
'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE'),
|
||||
'TGID_FILE': config.get(section, 'TGID_FILE'),
|
||||
'LOCAL_FILE': config.get(section, 'LOCAL_FILE'),
|
||||
'PEER_URL': config.get(section, 'PEER_URL'),
|
||||
'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL'),
|
||||
'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400,
|
|
@ -26,5 +26,6 @@ REPORT_OPCODES = {
|
|||
'CONFIG_UPD': '\x04',
|
||||
'BRIDGE_UPD': '\x05',
|
||||
'LINK_EVENT': '\x06',
|
||||
'BRDG_EVENT': '\x07'
|
||||
'BRDG_EVENT': '\x07',
|
||||
'RCM_SND': '\x08'
|
||||
}
|
79
log.py
79
log.py
|
@ -25,7 +25,9 @@ from twisted.internet import reactor
|
|||
from binascii import b2a_hex as h
|
||||
|
||||
import time
|
||||
from dmrlink import IPSC, systems
|
||||
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
|
||||
from dmr_utils.utils import hex_str_3, hex_str_4, int_id, get_alias
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
|
@ -37,8 +39,8 @@ __email__ = 'n0mjs@me.com'
|
|||
|
||||
|
||||
class logIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
self.ACTIVE_CALLS = []
|
||||
|
||||
#************************************************
|
||||
|
@ -89,13 +91,12 @@ class logIPSC(IPSC):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -103,62 +104,44 @@ if __name__ == '__main__':
|
|||
# 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)')
|
||||
parser.add_argument('-ll', '--log_level', action='store', dest='LOG_LEVEL', help='Override config file logging level.')
|
||||
parser.add_argument('-lh', '--log_handle', action='store', dest='LOG_HANDLERS', help='Override config file logging handler.')
|
||||
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)
|
||||
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...')
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# 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.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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'])
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, logIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
3514
peer_ids.csv
3514
peer_ids.csv
File diff suppressed because it is too large
Load Diff
|
@ -1,110 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
from cPickle import load
|
||||
from pprint import pprint
|
||||
from time import ctime
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
from binascii import b2a_hex as h
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2015 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
__credits__ = 'Adam Fast, KC0YLK, Dave Kierzkowski, KD8EYF'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
|
||||
# This is the only user-configuration necessary
|
||||
# Tell the program where the pickle file is
|
||||
# Tell the program how often to print a report
|
||||
stat_file = '../dmrlink_stats.pickle'
|
||||
frequency = 30
|
||||
|
||||
|
||||
def int_id(_hex_string):
|
||||
return int(h(_hex_string), 16)
|
||||
|
||||
def read_dict():
|
||||
try:
|
||||
with open(stat_file, 'rb') as file:
|
||||
NETWORK = load(file)
|
||||
return NETWORK
|
||||
except IOError as detail:
|
||||
print('I/O Error: {}'.format(detail))
|
||||
except EOFError:
|
||||
print('EOFError')
|
||||
|
||||
def print_stats():
|
||||
NETWORK = read_dict()
|
||||
if NETWORK != "None":
|
||||
print('NETWORK STATISTICS REPORT:', ctime())
|
||||
|
||||
for ipsc in NETWORK:
|
||||
stat = NETWORK[ipsc]['MASTER']['STATUS']
|
||||
master = NETWORK[ipsc]['LOCAL']['MASTER_PEER']
|
||||
|
||||
print(ipsc)
|
||||
|
||||
if master:
|
||||
print(' MASTER Information:')
|
||||
print(' RADIO ID: {} (self)'.format(str(int_id(NETWORK[ipsc]['LOCAL']['RADIO_ID'])).rjust(8,'0')))
|
||||
else:
|
||||
print(' MASTER Information:')
|
||||
print(' RADIO ID: {} CONNECTED: {}, KEEP ALIVES: SENT {} RECEIVED {} MISSED {} ({})'.format(\
|
||||
str(int_id(NETWORK[ipsc]['MASTER']['RADIO_ID'])).rjust(8,'0'),\
|
||||
stat['CONNECTED'],stat['KEEP_ALIVES_SENT'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'],\
|
||||
stat['KEEP_ALIVES_MISSED'],\
|
||||
NETWORK[ipsc]['MASTER']['IP']))
|
||||
|
||||
|
||||
print(' PEER Information:')
|
||||
|
||||
if master:
|
||||
for peer in NETWORK[ipsc]['PEERS']:
|
||||
stat = NETWORK[ipsc]['PEERS'][peer]['STATUS']
|
||||
print(' RADIO ID: {} CONNECTED: {}, KEEP ALIVES: RECEIVED {} ({})'.format(\
|
||||
str(int_id(peer)).rjust(8,'0'),\
|
||||
stat['CONNECTED'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'],\
|
||||
NETWORK[ipsc]['PEERS'][peer]['IP']))
|
||||
else:
|
||||
for peer in NETWORK[ipsc]['PEERS']:
|
||||
stat = NETWORK[ipsc]['PEERS'][peer]['STATUS']
|
||||
if peer == NETWORK[ipsc]['LOCAL']['RADIO_ID']:
|
||||
print(' RADIO ID: {} (self)'.format(str(int_id(peer)).rjust(8,'0')))
|
||||
else:
|
||||
print(' RADIO ID: {} CONNECTED: {}, KEEP ALIVES: SENT {} RECEIVED {} MISSED {} ({})'.format(\
|
||||
str(int_id(peer)).rjust(8,'0'),\
|
||||
stat['CONNECTED'],\
|
||||
stat['KEEP_ALIVES_SENT'],\
|
||||
stat['KEEP_ALIVES_RECEIVED'],\
|
||||
stat['KEEP_ALIVES_MISSED'],\
|
||||
NETWORK[ipsc]['PEERS'][peer]['IP']))
|
||||
print()
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
output_stats = task.LoopingCall(print_stats)
|
||||
output_stats.start(frequency)
|
||||
reactor.run()
|
|
@ -36,7 +36,7 @@ from twisted.internet import reactor
|
|||
import sys, time
|
||||
import cPickle as pickle
|
||||
|
||||
from dmrlink import IPSC, systems
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
|
||||
from dmr_utils.utils import int_id, hex_str_3
|
||||
from ipsc.ipsc_const import BURST_DATA_TYPE
|
||||
|
@ -61,8 +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, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger,_report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
self.CALL_DATA = []
|
||||
self.event_id = 1
|
||||
|
||||
|
@ -134,12 +134,12 @@ class playIPSC(IPSC):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -153,39 +153,38 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'record.py\' (c) 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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()
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, playIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
51
playback.py
51
playback.py
|
@ -25,7 +25,7 @@ from twisted.internet import reactor
|
|||
from binascii import b2a_hex as ahex
|
||||
|
||||
import sys, time
|
||||
from dmrlink import IPSC, systems
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
from dmr_utils.utils import int_id, hex_str_3
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
|
@ -46,8 +46,8 @@ HEX_SUB = hex_str_3(SUB)
|
|||
BOGUS_SUB = '\xFF\xFF\xFF'
|
||||
|
||||
class playbackIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
self.CALL_DATA = []
|
||||
|
||||
if GROUP_SRC_SUB:
|
||||
|
@ -115,12 +115,12 @@ class playbackIPSC(IPSC):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -134,39 +134,38 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'playback.py\' (c) 2013, 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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'])
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, playbackIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
# This is a sample application to bridge traffic between IPSC systems. it uses
|
||||
# one required (bridge_rules.py) and one optional (known_bridges.py) additional
|
||||
# configuration files. Both files have their own documentation for use.
|
||||
#
|
||||
# "bridge_rules" contains the IPSC network, Timeslot and TGID matching rules to
|
||||
# determine which voice calls are bridged between IPSC systems and which are
|
||||
# not.
|
||||
#
|
||||
# "known_bridges" contains DMR radio ID numbers of known bridges. This file is
|
||||
# used when you want bridge.py to be "polite" or serve as a backup bridge. If
|
||||
# a known bridge exists in either a source OR target IPSC network, then no
|
||||
# bridging between those IPSC systems will take place. This behavior is
|
||||
# dynamic and updates each keep-alive interval (main configuration file).
|
||||
# For faster failover, configure a short keep-alive time and a low number of
|
||||
# missed keep-alives before timout. I recommend 5 sec keep-alive and 3 missed.
|
||||
# That gives a worst-case scenario of 15 seconds to fail over. Recovery will
|
||||
# typically happen with a single "blip" in the transmission up to about 5
|
||||
# seconds.
|
||||
#
|
||||
# While this file is listed as Beta status, K0USY Group depends on this code
|
||||
# for the bridigng of it's many repeaters. We consider it reliable, but you
|
||||
# get what you pay for... as usual, no guarantees.
|
||||
#
|
||||
# Use to make test strings: #print('PKT:', "\\x".join("{:02x}".format(ord(c)) for c in _data))
|
||||
|
||||
from __future__ import print_function
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import task
|
||||
from binascii import b2a_hex as ahex
|
||||
from time import time
|
||||
from importlib import import_module
|
||||
|
||||
import sys
|
||||
|
||||
from dmr_utils.utils import hex_str_3, hex_str_4, int_id
|
||||
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, REPORT_OPCODES, build_aliases, config_reports
|
||||
from ipsc.ipsc_const import BURST_DATA_TYPE
|
||||
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2017 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
__credits__ = 'Adam Fast, KC0YLK; Dave Kierzkowski, KD8EYF; Steve Zingman, N4IRS; Mike Zingman, N4IRR'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
|
||||
# Import subscriber ACL
|
||||
# ACL may be a single list of subscriber IDs
|
||||
# Global action is to allow or deny them. Multiple lists with different actions and ranges
|
||||
# are not yet implemented.
|
||||
def build_acl(_sub_acl):
|
||||
try:
|
||||
acl_file = import_module(_sub_acl)
|
||||
for i, e in enumerate(acl_file.ACL):
|
||||
acl_file.ACL[i] = hex_str_3(acl_file.ACL[i])
|
||||
logger.info('ACL file found and ACL entries imported')
|
||||
ACL_ACTION = acl_file.ACL_ACTION
|
||||
ACL = acl_file.ACL_ACTION
|
||||
except ImportError:
|
||||
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
||||
ACL_ACTION = 'NONE'
|
||||
ACL = []
|
||||
|
||||
# Depending on which type of ACL is used (PERMIT, DENY... or there isn't one)
|
||||
# define a differnet function to be used to check the ACL
|
||||
global allow_sub
|
||||
if ACL_ACTION == 'PERMIT':
|
||||
def allow_sub(_sub):
|
||||
if _sub in ACL:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif ACL_ACTION == 'DENY':
|
||||
def allow_sub(_sub):
|
||||
if _sub not in ACL:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
def allow_sub(_sub):
|
||||
return True
|
||||
|
||||
return ACL
|
||||
|
||||
|
||||
class proxyIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger, report):
|
||||
IPSC.__init__(self, _name, _config, _logger, report)
|
||||
|
||||
self.last_seq_id = '\x00'
|
||||
self.call_start = 0
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
#************************************************
|
||||
#
|
||||
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:
|
||||
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
|
||||
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]
|
||||
|
||||
for system in systems:
|
||||
if system != self._system:
|
||||
#
|
||||
# BEGIN FRAME FORWARDING
|
||||
#
|
||||
# Make a copy of the payload
|
||||
_tmp_data = _data
|
||||
|
||||
# Re-Write the IPSC SRC to match the target network's ID
|
||||
_tmp_data = _tmp_data.replace(_peerid, self._CONFIG['SYSTEMS'][system]['LOCAL']['RADIO_ID'])
|
||||
|
||||
# Send the packet to all peers in the target IPSC
|
||||
systems[system].send_to_ipsc(_tmp_data)
|
||||
#
|
||||
# END FRAME FORWARDING
|
||||
#
|
||||
|
||||
#
|
||||
# BEGIN IN-BAND SIGNALING BASED ON TGID & VOICE TERMINATOR FRAME
|
||||
#
|
||||
|
||||
# Action happens on key up
|
||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||
if self.last_seq_id != _seq_id:
|
||||
self.last_seq_id = _seq_id
|
||||
self.call_start = time()
|
||||
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))
|
||||
self._report.send_proxyEvent('({}) GROUP VOICE START: CallID: {} PEER: {}, SUB: {}, TS: {}, TGID: {}'.format(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
|
||||
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)
|
||||
self._report.send_proxyEvent('({}) GROUP VOICE END: CallID: {} PEER: {}, SUB: {}, TS: {}, TGID: {} Duration: {:.2f}s'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group), self.call_duration))
|
||||
else:
|
||||
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),)
|
||||
self._report.send_proxyEvent('(%s) GROUP VOICE END WITHOUT MATCHING START: CallID: %s PEER: %s, SUB: %s, TS: %s, TGID: %s'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group)))
|
||||
|
||||
|
||||
class proxyReportFactory(reportFactory):
|
||||
def send_proxyEvent(self, _data):
|
||||
self.send_clients(REPORT_OPCODES['BRDG_EVENT']+_data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# 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)')
|
||||
parser.add_argument('-ll', '--log_level', action='store', dest='LOG_LEVEL', help='Override config file logging level.')
|
||||
parser.add_argument('-lh', '--log_handle', action='store', dest='LOG_HANDLERS', help='Override config file logging handler.')
|
||||
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 = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
for system in systems:
|
||||
systems[system].de_register_self()
|
||||
reactor.stop()
|
||||
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
|
||||
|
||||
# PROXY.PY SPECIFIC ITEMS GO HERE:
|
||||
|
||||
# Build the Access Control List
|
||||
ACL = build_acl('sub_acl')
|
||||
|
||||
|
||||
# MAIN INITIALIZATION ITEMS HERE
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, proxyReportFactory)
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGURED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, proxyIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
81
rcm.py
81
rcm.py
|
@ -32,8 +32,9 @@ import datetime
|
|||
import binascii
|
||||
import dmrlink
|
||||
import sys
|
||||
from dmrlink import IPSC, systems
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
from dmr_utils.utils import get_alias, int_id
|
||||
from ipsc.ipsc_const import *
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2013, 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -43,18 +44,13 @@ __maintainer__ = 'Cort Buffington, N0MJS'
|
|||
__email__ = 'n0mjs@me.com'
|
||||
|
||||
|
||||
try:
|
||||
from ipsc.ipsc_const import *
|
||||
except ImportError:
|
||||
sys.exit('IPSC message types file not found or invalid')
|
||||
|
||||
status = True
|
||||
rpt = True
|
||||
nack = True
|
||||
|
||||
class rcmIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
|
||||
#************************************************
|
||||
# CALLBACK FUNCTIONS FOR USER PACKET TYPES
|
||||
|
@ -149,13 +145,12 @@ class rcmIPSC(IPSC):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from dmr_utils.utils import try_download, mk_id_dict
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -169,62 +164,38 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
logger.info('DMRlink \'rcm.py\' (c) 2013, 2014 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# 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.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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'])
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, rcmIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
||||
|
|
|
@ -44,7 +44,9 @@ from twisted.internet import task
|
|||
|
||||
import pymysql
|
||||
import dmrlink
|
||||
from dmrlink import IPSC, NETWORK, networks, get_info, int_id, logger
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
|
||||
from ipsc.ipsc_const import *
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
__copyright__ = 'Copyright (c) 2013, 2014 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||
|
@ -70,10 +72,6 @@ db_name = 'dmrlink'
|
|||
#
|
||||
#************************************
|
||||
|
||||
try:
|
||||
from ipsc.ipsc_message_types import *
|
||||
except ImportError:
|
||||
sys.exit('IPSC message types file not found or invalid')
|
||||
|
||||
class rcmIPSC(IPSC):
|
||||
|
||||
|
@ -110,9 +108,58 @@ class rcmIPSC(IPSC):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.info('DMRlink \'rcm_db_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] = rcmIPSC(ipsc_network)
|
||||
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], networks[ipsc_network], interface=NETWORK[ipsc_network]['LOCAL']['IP'])
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# 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)')
|
||||
parser.add_argument('-ll', '--log_level', action='store', dest='LOG_LEVEL', help='Override config file logging level.')
|
||||
parser.add_argument('-lh', '--log_handle', action='store', dest='LOG_HANDLERS', help='Override config file logging handler.')
|
||||
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 = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
def sig_handler(_signal, _frame):
|
||||
logger.info('*** DMRLINK IS TERMINATING WITH SIGNAL %s ***', str(_signal))
|
||||
for system in systems:
|
||||
systems[system].de_register_self()
|
||||
reactor.stop()
|
||||
|
||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
# INITIALIZE THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, rcmIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
58
record.py
58
record.py
|
@ -27,7 +27,7 @@ from binascii import b2a_hex as h
|
|||
|
||||
import sys
|
||||
import cPickle as pickle
|
||||
from dmrlink import IPSC, systems
|
||||
from dmrlink import IPSC, mk_ipsc_systems, systems, reportFactory, build_aliases, config_reports
|
||||
from dmr_utils.utils import hex_str_3, int_id
|
||||
|
||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||
|
@ -65,8 +65,8 @@ id = hex_str_3(id)
|
|||
filename = raw_input('Filename to use for this recording? ')
|
||||
|
||||
class recordIPSC(IPSC):
|
||||
def __init__(self, _name, _config, _logger):
|
||||
IPSC.__init__(self, _name, _config, _logger)
|
||||
def __init__(self, _name, _config, _logger, _report):
|
||||
IPSC.__init__(self, _name, _config, _logger, _report)
|
||||
self.CALL_DATA = []
|
||||
|
||||
#************************************************
|
||||
|
@ -106,12 +106,12 @@ class recordIPSC(IPSC):
|
|||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
import dmrlink_log
|
||||
import dmrlink_config
|
||||
from ipsc.dmrlink_config import build_config
|
||||
from ipsc.dmrlink_log import config_logging
|
||||
|
||||
# Change the current directory to the location of the application
|
||||
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
|
||||
|
@ -125,44 +125,38 @@ if __name__ == '__main__':
|
|||
|
||||
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)
|
||||
|
||||
CONFIG = build_config(cli_args.CFG_FILE)
|
||||
|
||||
# Call the external routing to start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL
|
||||
if cli_args.LOG_HANDLERS:
|
||||
CONFIG['LOGGER']['LOG_HANDLERS'] = cli_args.LOG_HANDLERS
|
||||
logger = dmrlink_log.config_logging(CONFIG['LOGGER'])
|
||||
|
||||
CONFIG = dmrlink_config.build_config(cli_args.CFG_FILE)
|
||||
logger = config_logging(CONFIG['LOGGER'])
|
||||
logger.info('DMRlink \'dmrlink.py\' (c) 2013 - 2015 N0MJS & the K0USY Group - SYSTEM STARTING...')
|
||||
|
||||
# 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...')
|
||||
|
||||
# Shut ourselves down gracefully with the IPSC peers.
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
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)
|
||||
systems[system].de_register_self()
|
||||
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 THE REPORTING LOOP
|
||||
report_server = config_reports(CONFIG, logger, reportFactory)
|
||||
|
||||
# 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()
|
||||
# Build ID Aliases
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_ids = build_aliases(CONFIG, logger)
|
||||
|
||||
# INITIALIZE AN IPSC OBJECT (SELF SUSTAINING) FOR EACH CONFIGRUED IPSC
|
||||
systems = mk_ipsc_systems(CONFIG, logger, systems, recordIPSC, report_server)
|
||||
|
||||
|
||||
|
||||
# INITIALIZATION COMPLETE -- START THE REACTOR
|
||||
reactor.run()
|
60261
subscriber_ids.csv
60261
subscriber_ids.csv
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
|||
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