Further Modularization

This commit is contained in:
Cort Buffington 2016-11-26 14:36:24 -06:00
parent 31cc1f41b5
commit f27af4c4dd
6 changed files with 61 additions and 34 deletions

View File

@ -107,6 +107,12 @@ def get_info(_id, _dict):
return _dict[_id] return _dict[_id]
return _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 # 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 # But they don't work here because they reference dictionraries not avaialbe
# in this scope # in this scope

View File

@ -33,7 +33,7 @@ from twisted.internet import task
# Things we import from the main hblink module # Things we import from the main hblink module
from hblink import HBSYSTEM, systems, int_id, hblink_handler 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 dec_dmr
import bptc import bptc
import hb_config import hb_config
@ -51,7 +51,6 @@ __email__ = 'n0mjs@me.com'
__status__ = 'pre-alpha' __status__ = 'pre-alpha'
# Module gobal varaibles # Module gobal varaibles
GROUP_HANGTIME = 5
# Import Bridging rules # Import Bridging rules
# Note: A stanza *must* exist for any MASTER or CLIENT configured in the main # 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 # ACL may be a single list of subscriber IDs
# Global action is to allow or deny them. Multiple lists with different actions and ranges # Global action is to allow or deny them. Multiple lists with different actions and ranges
# are not yet implemented. # are not yet implemented.
def build_acl(): def build_acl(_sub_acl):
try: try:
from sub_acl import ACL_ACTION, ACL acl_file = import_module(_sub_acl)
# uses more memory to build hex strings, but processes MUCH faster when checking for matches for i, e in enumerate(acl_file.ACL):
for i, e in enumerate(ACL): acl_file.ACL[i] = hex_str_3(acl_file.ACL[i])
ACL[i] = hex_str_3(ACL[i]) logger.info('ACL file found and ACL entries imported')
logger.info('Subscriber access control file found, subscriber ACL imported')
except ImportError: 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' ACL_ACTION = 'NONE'
# Depending on which type of ACL is used (PERMIT, DENY... or there isn't one) # 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 # define a differnet function to be used to check the ACL
global allow_sub global allow_sub
if ACL_ACTION == 'PERMIT': if acl_file.ACL_ACTION == 'PERMIT':
def allow_sub(_sub): def allow_sub(_sub):
if _sub in ACL: if _sub in ACL:
return True return True
else: else:
return False return False
elif ACL_ACTION == 'DENY': elif acl_file.ACL_ACTION == 'DENY':
def allow_sub(_sub): def allow_sub(_sub):
if _sub not in ACL: if _sub not in ACL:
return True return True
@ -115,6 +113,8 @@ def build_acl():
else: else:
def allow_sub(_sub): def allow_sub(_sub):
return True return True
return acl_file.ACL
# Run this every minute for rule timer updates # Run this every minute for rule timer updates
@ -223,7 +223,8 @@ class routerSYSTEM(HBSYSTEM):
# This is a new call stream # This is a new call stream
self.STATUS['RX_START'] = pkt_time 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 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: 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 # 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 # 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: 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'])) 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 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: 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'])) 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 continue
@ -322,7 +323,8 @@ class routerSYSTEM(HBSYSTEM):
# Final actions - Is this a voice terminator? # 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): 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'] 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. # 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']) talkgroup_ids = mk_id_dict(CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['TGID_FILE'])
if talkgroup_ids: if talkgroup_ids:
logger.info('ID ALIAS MAPPER: talkgroup_ids dictionary is available') 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 # START HB_ROUTER
@ -474,7 +467,7 @@ if __name__ == '__main__':
BRIDGES = make_bridges('hb_confbridge_rules') BRIDGES = make_bridges('hb_confbridge_rules')
# Build the Access Control List # Build the Access Control List
build_acl() ACL = build_acl('sub_acl')
# HBlink instance creation # HBlink instance creation
logger.info('HBlink \'hb_router.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...') logger.info('HBlink \'hb_router.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...')

View File

@ -1,21 +1,45 @@
''' '''
THIS EXAMPLE WILL NOT WORK AS IT IS - YOU MUST SPECIFY YOUR OWN VALUES!!! 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 = { BRIDGES = {
'WORLDWIDE': [ 'WORLDWIDE': [
{'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, '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,]}, {'SYSTEM': 'CLIENT-1', 'TS': 1, 'TGID': 3100, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10]},
], ],
'ENGLISH': [ 'ENGLISH': [
{'SYSTEM': 'MASTER-1', '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,]}, {'SYSTEM': 'CLIENT-2', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10]},
], ],
'STATEWIDE': [ 'STATEWIDE': [
{'SYSTEM': 'MASTER-1', '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,]}, {'SYSTEM': 'CLIENT-2', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10]},
] ]
} }

View File

@ -105,7 +105,8 @@ def build_config(_config_file):
'SLOTS': config.get(section, 'SLOTS'), 'SLOTS': config.get(section, 'SLOTS'),
'URL': config.get(section, 'URL').ljust(124)[:124], 'URL': config.get(section, 'URL').ljust(124)[:124],
'SOFTWARE_ID': config.get(section, 'SOFTWARE_ID').ljust(40)[:40], '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': { CONFIG['SYSTEMS'][section].update({'STATS': {
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES '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'), 'EXPORT_AMBE': config.getboolean(section, 'EXPORT_AMBE'),
'IP': gethostbyname(config.get(section, 'IP')), 'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'), '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': {}}) CONFIG['SYSTEMS'][section].update({'CLIENTS': {}})

View File

@ -12,8 +12,8 @@ NOTES:
period. If you don't want to use timers, set it to anything else, but 'NONE' might be period. If you don't want to use timers, set it to anything else, but 'NONE' might be
a good value for documentation! 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 * 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". ask. Timers are performance "expense".
DO YOU THINK THIS FILE IS TOO COMPLICATED? DO YOU THINK THIS FILE IS TOO COMPLICATED?
Because you guys all want more and more features, this file is getting complicated. I have 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 dabbled with using a parser to make it easier to build. I'm torn. There is a HUGE benefit

View File

@ -68,6 +68,7 @@ EXPORT_AMBE: False
IP: IP:
PORT: 54000 PORT: 54000
PASSPHRASE: s3cr37w0rd PASSPHRASE: s3cr37w0rd
GROUP_HANGTIME: 5
# CLIENT INSTANCES - DUPLICATE SECTION FOR MULTIPLE CLIENTS # CLIENT INSTANCES - DUPLICATE SECTION FOR MULTIPLE CLIENTS
# There are a LOT of errors in the HB Protocol specifications on this one! # 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 URL: www.w1abc.org
SOFTWARE_ID: HBlink SOFTWARE_ID: HBlink
PACKAGE_ID: v0.1 PACKAGE_ID: v0.1
GROUP_HANGTIME: 5