Merge branch 'socket-reporting'

This commit is contained in:
Cort Buffington 2017-05-27 08:59:21 -05:00
commit fa8e53bd81
23 changed files with 742 additions and 64765 deletions

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ sub_acl.py
*.config
*.json
*.pickle
*.csv

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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,

View File

@ -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
View File

@ -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()

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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()

View File

@ -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()

240
proxy.py Executable file
View File

@ -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
View File

@ -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()

View File

@ -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()

View File

@ -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()

File diff suppressed because it is too large Load Diff

View File

@ -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
1 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