From f27af4c4dd01b807fda958cdc463ea746651c2f9 Mon Sep 17 00:00:00 2001 From: Cort Buffington Date: Sat, 26 Nov 2016 14:36:24 -0600 Subject: [PATCH] Further Modularization --- dmr_utils.py | 6 +++++ hb_confbridge.py | 43 +++++++++++++++-------------------- hb_confbridge_rules-SAMPLE.py | 36 ++++++++++++++++++++++++----- hb_config.py | 6 +++-- hb_routing_rules-SAMPLE.py | 2 +- hblink-SAMPLE.cfg | 2 ++ 6 files changed, 61 insertions(+), 34 deletions(-) diff --git a/dmr_utils.py b/dmr_utils.py index ee7eb2b..c89c983 100755 --- a/dmr_utils.py +++ b/dmr_utils.py @@ -107,6 +107,12 @@ def get_info(_id, _dict): return _dict[_id] return _id +def get_alias(_id, _dict): + _int_id = int_id(_id) + if _int_id in _dict: + return _dict[_int_id] + return _int_id + # These are the functions you should use to look up IDs in the dictionaries # But they don't work here because they reference dictionraries not avaialbe # in this scope diff --git a/hb_confbridge.py b/hb_confbridge.py index ca53a94..2b1aa42 100755 --- a/hb_confbridge.py +++ b/hb_confbridge.py @@ -33,7 +33,7 @@ from twisted.internet import task # Things we import from the main hblink module from hblink import HBSYSTEM, systems, int_id, hblink_handler -from dmr_utils import hex_str_3, int_id, get_info +from dmr_utils import hex_str_3, int_id, get_alias import dec_dmr import bptc import hb_config @@ -51,7 +51,6 @@ __email__ = 'n0mjs@me.com' __status__ = 'pre-alpha' # Module gobal varaibles -GROUP_HANGTIME = 5 # Import Bridging rules # Note: A stanza *must* exist for any MASTER or CLIENT configured in the main @@ -86,27 +85,26 @@ def make_bridges(_hb_confbridge_bridges): # 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(): +def build_acl(_sub_acl): try: - from sub_acl import ACL_ACTION, ACL - # uses more memory to build hex strings, but processes MUCH faster when checking for matches - for i, e in enumerate(ACL): - ACL[i] = hex_str_3(ACL[i]) - logger.info('Subscriber access control file found, subscriber ACL imported') + 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') except ImportError: - logger.critical('\'sub_acl.py\' not found - all subscriber IDs are valid') + logger.info('ACL file not found or invalid - all subscriber IDs are valid') ACL_ACTION = 'NONE' # 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': + if acl_file.ACL_ACTION == 'PERMIT': def allow_sub(_sub): if _sub in ACL: return True else: return False - elif ACL_ACTION == 'DENY': + elif acl_file.ACL_ACTION == 'DENY': def allow_sub(_sub): if _sub not in ACL: return True @@ -115,6 +113,8 @@ def build_acl(): else: def allow_sub(_sub): return True + + return acl_file.ACL # Run this every minute for rule timer updates @@ -223,7 +223,8 @@ class routerSYSTEM(HBSYSTEM): # This is a new call stream self.STATUS['RX_START'] = pkt_time - logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', self._system, int_id(_stream_id), sub_alias(_rf_src), int_id(_rf_src), peer_alias(_radio_id), int_id(_radio_id), tg_alias(_dst_id), int_id(_dst_id), _slot) + logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ + self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) # If we can, use the LC from the voice header as to keep all options intact if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD: @@ -254,11 +255,11 @@ class routerSYSTEM(HBSYSTEM): # From the same group as the last TX to this HBSystem, but from a different subscriber, and it has been less than stream timeout # The "continue" at the end of each means the next iteration of the for loop that tests for matching rules # - if ((_target['TGID'] != _target_status[_target['TS']]['RX_TGID']) and ((pkt_time - _target_status[_target['TS']]['RX_TIME']) < GROUP_HANGTIME)): + if ((_target['TGID'] != _target_status[_target['TS']]['RX_TGID']) and ((pkt_time - _target_status[_target['TS']]['RX_TIME']) < self._config['GROUP_HANGTIME'])): if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD and self.STATUS[_slot]['RX_STREAM_ID'] != _seq: logger.info('(%s) Call not routed to TGID %s, target active or in group hangtime: HBSystem: %s, TS: %s, TGID: %s', self._system, int_id(_target['TGID']), _target['SYSTEM'], _target['TS'], int_id(_target_status[_target['TS']]['RX_TGID'])) continue - if ((_target['TGID'] != _target_status[_target['TS']]['TX_TGID']) and ((pkt_time - _target_status[_target['TS']]['TX_TIME']) < GROUP_HANGTIME)): + if ((_target['TGID'] != _target_status[_target['TS']]['TX_TGID']) and ((pkt_time - _target_status[_target['TS']]['TX_TIME']) < self._config['GROUP_HANGTIME'])): if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD and self.STATUS[_slot]['RX_STREAM_ID'] != _seq: logger.info('(%s) Call not routed to TGID%s, target in group hangtime: HBSystem: %s, TS: %s, TGID: %s', self._system, int_id(_target['TGID']), _target['SYSTEM'], _target['TS'], int_id(_target_status[_target['TS']]['TX_TGID'])) continue @@ -322,7 +323,8 @@ class routerSYSTEM(HBSYSTEM): # Final actions - Is this a voice terminator? if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM): call_duration = pkt_time - self.STATUS['RX_START'] - logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', self._system, int_id(_stream_id), sub_alias(_rf_src), int_id(_rf_src), peer_alias(_radio_id), int_id(_radio_id), tg_alias(_dst_id), int_id(_dst_id), _slot, call_duration) + logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ + self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) # # Begin in-band signalling for call end. This has nothign to do with routing traffic directly. @@ -455,16 +457,7 @@ if __name__ == '__main__': 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') - - # These are the functions to look up IDs in the dictionaries - def sub_alias(_sub_id): - return get_info(int_id(_sub_id), subscriber_ids) - - def peer_alias(_peer_id): - return get_info(int_id(_peer_id), peer_ids) - def tg_alias(_tgid): - return get_info(int_id(_tgid), talkgroup_ids) # # START HB_ROUTER @@ -474,7 +467,7 @@ if __name__ == '__main__': BRIDGES = make_bridges('hb_confbridge_rules') # Build the Access Control List - build_acl() + ACL = build_acl('sub_acl') # HBlink instance creation logger.info('HBlink \'hb_router.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...') diff --git a/hb_confbridge_rules-SAMPLE.py b/hb_confbridge_rules-SAMPLE.py index 6409cf3..9696030 100644 --- a/hb_confbridge_rules-SAMPLE.py +++ b/hb_confbridge_rules-SAMPLE.py @@ -1,21 +1,45 @@ ''' THIS EXAMPLE WILL NOT WORK AS IT IS - YOU MUST SPECIFY YOUR OWN VALUES!!! +This file is organized around the "Conference Bridges" that you wish to use. If you're a c-Bridge +person, think of these as "bridge groups". You might also liken them to a "reflector". If a particular +system is "ACTIVE" on a particular conference bridge, any traffid from that system will be sent +to any other system that is active on the bridge as well. This is not an "end to end" method, because +each system must independently be activated on the bridge. + +The first level (e.g. "WORLDWIDE" or "STATEWIDE" in the examples) is the name of the conference +bridge. This is any arbitrary ASCII text string you want to use. Under each conference bridge +definition are the following items -- one line for each HBSystem as defined in the main HBlink +configuration file. + + * SYSTEM - The name of the sytem as listed in the main hblink configuration file (e.g. hblink.cfg) + This MUST be the exact same name as in the main config file!!! + * TS - Timeslot used for matching traffic to this confernce bridge + * TGID - Talkgroup ID used for matching traffic to this conference bridge + * ON and OFF are LISTS of Talkgroup IDs used to trigger this system off and on. Even if you + only want one (as shown in the ON example), it has to be in list format. None can be + handled with an empty list, such as " 'ON': [] ". + * TO_TYPE is timeout type. If you want to use timers, ON means when it's turned on, it will + turn off afer the timout period and OFF means it will turn back on after the timout + period. If you don't want to use timers, set it to anything else, but 'NONE' might be + a good value for documentation! + * TIMOUT is a value in minutes for the timout timer. No, I won't make it 'seconds', so don't + ask. Timers are performance "expense". ''' BRIDGES = { 'WORLDWIDE': [ - {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,]}, - {'SYSTEM': 'CLIENT-1', 'TS': 1, 'TGID': 3100, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,]}, + {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10]}, + {'SYSTEM': 'CLIENT-1', 'TS': 1, 'TGID': 3100, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10]}, ], 'ENGLISH': [ - {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,]}, - {'SYSTEM': 'CLIENT-2', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,]}, + {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10]}, + {'SYSTEM': 'CLIENT-2', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10]}, ], 'STATEWIDE': [ - {'SYSTEM': 'MASTER-1', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,]}, - {'SYSTEM': 'CLIENT-2', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,]}, + {'SYSTEM': 'MASTER-1', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10]}, + {'SYSTEM': 'CLIENT-2', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10]}, ] } diff --git a/hb_config.py b/hb_config.py index 6944ed3..a65fcab 100755 --- a/hb_config.py +++ b/hb_config.py @@ -105,7 +105,8 @@ def build_config(_config_file): 'SLOTS': config.get(section, 'SLOTS'), 'URL': config.get(section, 'URL').ljust(124)[:124], 'SOFTWARE_ID': config.get(section, 'SOFTWARE_ID').ljust(40)[:40], - 'PACKAGE_ID': config.get(section, 'PACKAGE_ID').ljust(40)[:40] + 'PACKAGE_ID': config.get(section, 'PACKAGE_ID').ljust(40)[:40], + 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME') }}) CONFIG['SYSTEMS'][section].update({'STATS': { 'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES @@ -124,7 +125,8 @@ def build_config(_config_file): 'EXPORT_AMBE': config.getboolean(section, 'EXPORT_AMBE'), 'IP': gethostbyname(config.get(section, 'IP')), 'PORT': config.getint(section, 'PORT'), - 'PASSPHRASE': config.get(section, 'PASSPHRASE') + 'PASSPHRASE': config.get(section, 'PASSPHRASE'), + 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME') }}) CONFIG['SYSTEMS'][section].update({'CLIENTS': {}}) diff --git a/hb_routing_rules-SAMPLE.py b/hb_routing_rules-SAMPLE.py index 53033f5..778c9aa 100644 --- a/hb_routing_rules-SAMPLE.py +++ b/hb_routing_rules-SAMPLE.py @@ -12,8 +12,8 @@ NOTES: period. If you don't want to use timers, set it to anything else, but 'NONE' might be a good value for documentation! * TIMOUT is a value in minutes for the timout timer. No, I won't make it 'seconds', so don't - ask. Timers are performance "expense". + DO YOU THINK THIS FILE IS TOO COMPLICATED? Because you guys all want more and more features, this file is getting complicated. I have dabbled with using a parser to make it easier to build. I'm torn. There is a HUGE benefit diff --git a/hblink-SAMPLE.cfg b/hblink-SAMPLE.cfg index 2adcbc3..41bbfc8 100644 --- a/hblink-SAMPLE.cfg +++ b/hblink-SAMPLE.cfg @@ -68,6 +68,7 @@ EXPORT_AMBE: False IP: PORT: 54000 PASSPHRASE: s3cr37w0rd +GROUP_HANGTIME: 5 # CLIENT INSTANCES - DUPLICATE SECTION FOR MULTIPLE CLIENTS # There are a LOT of errors in the HB Protocol specifications on this one! @@ -100,3 +101,4 @@ DESCRIPTION: This is a cool repeater URL: www.w1abc.org SOFTWARE_ID: HBlink PACKAGE_ID: v0.1 +GROUP_HANGTIME: 5 \ No newline at end of file