Compare commits
41 Commits
IPSC_Bridg
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
a55f73d059 | ||
|
1c684486ca | ||
|
67fd6d62a5 | ||
|
785f44e7e6 | ||
|
c023d4a565 | ||
|
ad399792c9 | ||
|
198278c288 | ||
|
2520f394ed | ||
|
bc59c75eb0 | ||
|
01a3fff754 | ||
|
2e1a4c3a58 | ||
|
59a59e4100 | ||
|
81c3467ec0 | ||
|
25d6bc08d0 | ||
|
e8311b1f54 | ||
|
cd11397416 | ||
|
cdd65d8edf | ||
|
2b63b5c111 | ||
|
3fc0bdc63d | ||
|
f5bc547d4d | ||
|
0acc6042e8 | ||
|
b4ab2d31a3 | ||
|
5950240787 | ||
|
63611f2c6c | ||
|
eb2ffe4ecb | ||
|
bf699dcfbc | ||
|
c6a543527f | ||
|
3279aeb527 | ||
|
f216300539 | ||
|
8e858e48a2 | ||
|
c16d549e94 | ||
|
ae9f71d715 | ||
|
4ac862b93c | ||
|
8fbd7ccf33 | ||
|
b8d1449d2f | ||
|
c8804d7231 | ||
|
7fde9d1ce8 | ||
|
d1143ddb7c | ||
|
0079ad1baa | ||
|
547d6e23ed | ||
|
62fc209b4f |
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
2
DO_NOT_README.md
Normal file → Executable file
2
DO_NOT_README.md
Normal file → Executable file
@ -81,7 +81,7 @@ The following illustrates the communication that a peer (us, for example) has wi
|
|||||||
^ v | v |
|
^ v | v |
|
||||||
| +--------------+ ++-------------+ +---------+ |
|
| +--------------+ ++-------------+ +---------+ |
|
||||||
| NO |Did The Master| YES |Set Keep Alive| |Peer List| NO |
|
| NO |Did The Master| YES |Set Keep Alive| |Peer List| NO |
|
||||||
+-------------+ Respond ? +---->| Counter To 0 | |Received?+----------+
|
+---------+ Respond ? +---->| Counter To 0 | |Received?+----------+
|
||||||
+--------------+ +--------------+ +---------+
|
+--------------+ +--------------+ +---------+
|
||||||
|
|
||||||
*COMMUNICATION WITH PEERS:*
|
*COMMUNICATION WITH PEERS:*
|
||||||
|
0
LICENSE.txt
Normal file → Executable file
0
LICENSE.txt
Normal file → Executable file
5
Retired/README.MD
Executable file
5
Retired/README.MD
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
**Retired files:**
|
||||||
|
|
||||||
|
The files in this directory are being kept for reference ONLY. They contain routines that may have been of use to someone.
|
||||||
|
Do not try to use these programs as is. They will not work!
|
0
ambe_audio.cfg → Retired/ambe_audio.cfg
Normal file → Executable file
0
ambe_audio.cfg → Retired/ambe_audio.cfg
Normal file → Executable file
0
ambe_audio_commands.txt → Retired/ambe_audio_commands.txt
Normal file → Executable file
0
ambe_audio_commands.txt → Retired/ambe_audio_commands.txt
Normal file → Executable file
@ -127,16 +127,28 @@ def build_bridges(_known_bridges):
|
|||||||
# are not yet implemented.
|
# are not yet implemented.
|
||||||
def build_acl(_sub_acl):
|
def build_acl(_sub_acl):
|
||||||
try:
|
try:
|
||||||
|
logger.info('ACL file found, importing entries. This will take about 1.5 seconds per 1 million IDs')
|
||||||
acl_file = import_module(_sub_acl)
|
acl_file = import_module(_sub_acl)
|
||||||
for i, e in enumerate(acl_file.ACL):
|
sections = acl_file.ACL.split(':')
|
||||||
acl_file.ACL[i] = hex_str_3(acl_file.ACL[i])
|
ACL_ACTION = sections[0]
|
||||||
logger.info('ACL file found and ACL entries imported')
|
entries_str = sections[1]
|
||||||
ACL_ACTION = acl_file.ACL_ACTION
|
ACL = set()
|
||||||
ACL = acl_file.ACL
|
|
||||||
|
for entry in entries_str.split(','):
|
||||||
|
if '-' in entry:
|
||||||
|
start,end = entry.split('-')
|
||||||
|
start,end = int(start), int(end)
|
||||||
|
for id in range(start, end+1):
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
else:
|
||||||
|
id = int(entry)
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
|
||||||
|
logger.info('ACL loaded: action "{}" for {:,} radio IDs'.format(ACL_ACTION, len(ACL)))
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
||||||
ACL_ACTION = 'NONE'
|
ACL_ACTION = 'NONE'
|
||||||
ACL = []
|
|
||||||
|
|
||||||
# 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
|
0
bridge_rules_SAMPLE.py → Retired/bridge_rules_SAMPLE.py
Normal file → Executable file
0
bridge_rules_SAMPLE.py → Retired/bridge_rules_SAMPLE.py
Normal file → Executable file
0
known_bridges_SAMPLE.py → Retired/known_bridges_SAMPLE.py
Normal file → Executable file
0
known_bridges_SAMPLE.py → Retired/known_bridges_SAMPLE.py
Normal file → Executable file
0
template.py → Retired/template.py
Normal file → Executable file
0
template.py → Retired/template.py
Normal file → Executable file
@ -137,10 +137,12 @@ def make_bridge_config(_confbridge_rules):
|
|||||||
_system['ON'][i] = hex_str_3(_system['ON'][i])
|
_system['ON'][i] = hex_str_3(_system['ON'][i])
|
||||||
for i, e in enumerate(_system['OFF']):
|
for i, e in enumerate(_system['OFF']):
|
||||||
_system['OFF'][i] = hex_str_3(_system['OFF'][i])
|
_system['OFF'][i] = hex_str_3(_system['OFF'][i])
|
||||||
|
for i, e in enumerate(_system['RESET']):
|
||||||
|
_system['RESET'][i] = hex_str_3(_system['RESET'][i])
|
||||||
_system['TIMEOUT'] = _system['TIMEOUT']*60
|
_system['TIMEOUT'] = _system['TIMEOUT']*60
|
||||||
_system['TIMER'] = time()
|
_system['TIMER'] = time()
|
||||||
|
|
||||||
return {'BRIDGE_CONF': bridge_file.BRIDGE_CONF, 'BRIDGES': bridge_file.BRIDGES}
|
return {'BRIDGE_CONF': bridge_file.BRIDGE_CONF, 'BRIDGES': bridge_file.BRIDGES, 'TRUNKS': bridge_file.TRUNKS}
|
||||||
|
|
||||||
|
|
||||||
# Import subscriber ACL
|
# Import subscriber ACL
|
||||||
@ -148,17 +150,28 @@ def make_bridge_config(_confbridge_rules):
|
|||||||
# 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(_sub_acl):
|
def build_acl(_sub_acl):
|
||||||
|
ACL = set()
|
||||||
try:
|
try:
|
||||||
|
logger.info('ACL file found, importing entries. This will take about 1.5 seconds per 1 million IDs')
|
||||||
acl_file = import_module(_sub_acl)
|
acl_file = import_module(_sub_acl)
|
||||||
for i, e in enumerate(acl_file.ACL):
|
sections = acl_file.ACL.split(':')
|
||||||
acl_file.ACL[i] = hex_str_3(acl_file.ACL[i])
|
ACL_ACTION = sections[0]
|
||||||
logger.info('ACL file found and ACL entries imported')
|
entries_str = sections[1]
|
||||||
ACL_ACTION = acl_file.ACL_ACTION
|
for entry in entries_str.split(','):
|
||||||
ACL = acl_file.ACL_ACTION
|
if '-' in entry:
|
||||||
|
start,end = entry.split('-')
|
||||||
|
start,end = int(start), int(end)
|
||||||
|
for id in range(start, end+1):
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
else:
|
||||||
|
id = int(entry)
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
|
||||||
|
logger.info('ACL loaded: action "{}" for {:,} radio IDs'.format(ACL_ACTION, len(ACL)))
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
||||||
ACL_ACTION = 'NONE'
|
ACL_ACTION = 'NONE'
|
||||||
ACL = []
|
|
||||||
|
|
||||||
# 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
|
||||||
@ -240,7 +253,7 @@ class confbridgeIPSC(IPSC):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Process the packet
|
# 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))
|
#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)
|
_burst_data_type = _data[30] # Determine the type of voice packet this is (see top of file for possible types)
|
||||||
_seq_id = _data[5]
|
_seq_id = _data[5]
|
||||||
|
|
||||||
@ -259,6 +272,8 @@ class confbridgeIPSC(IPSC):
|
|||||||
|
|
||||||
# BEGIN CONTENTION HANDLING
|
# BEGIN CONTENTION HANDLING
|
||||||
#
|
#
|
||||||
|
# If the system is listed as a "TRUNK", there will be no contention handling. All traffic is forwarded to it
|
||||||
|
#
|
||||||
# The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is:
|
# The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is:
|
||||||
# From a different group than last RX from this IPSC, but it has been less than Group Hangtime
|
# From a different group than last RX from this IPSC, but it has been less than Group Hangtime
|
||||||
# From a different group than last TX to this IPSC, but it has been less than Group Hangtime
|
# From a different group than last TX to this IPSC, but it has been less than Group Hangtime
|
||||||
@ -266,6 +281,7 @@ class confbridgeIPSC(IPSC):
|
|||||||
# From the same group as the last TX to this IPSC, but from a different subscriber, and it has been less than TS Clear Time
|
# From the same group as the last TX to this IPSC, but from a different subscriber, and it has been less than TS Clear Time
|
||||||
# The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
|
# The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
|
||||||
#
|
#
|
||||||
|
if _target not in TRUNKS:
|
||||||
if ((_target['TGID'] != _target_status[_target['TS']]['RX_TGID']) and ((now - _target_status[_target['TS']]['RX_TIME']) < _target_system['LOCAL']['GROUP_HANGTIME'])):
|
if ((_target['TGID'] != _target_status[_target['TS']]['RX_TGID']) and ((now - _target_status[_target['TS']]['RX_TIME']) < _target_system['LOCAL']['GROUP_HANGTIME'])):
|
||||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
|
||||||
self._logger.info('(%s) Call not bridged to TGID%s, target active or in group hangtime: IPSC: %s, TS: %s, TGID: %s', self._system, int_id(_target['TGID']), _target['SYSTEM'], _target['TS'], int_id(_target_status[_target['TS']]['RX_TGID']))
|
self._logger.info('(%s) Call not bridged to TGID%s, target active or in group hangtime: IPSC: %s, TS: %s, TGID: %s', self._system, int_id(_target['TGID']), _target['SYSTEM'], _target['TS'], int_id(_target_status[_target['TS']]['RX_TGID']))
|
||||||
@ -291,12 +307,14 @@ class confbridgeIPSC(IPSC):
|
|||||||
#
|
#
|
||||||
# Make a copy of the payload
|
# Make a copy of the payload
|
||||||
_tmp_data = _data
|
_tmp_data = _data
|
||||||
|
# Re-Write the PEER ID in the IPSC Header:
|
||||||
|
_tmp_data = _tmp_data.replace(_peerid, _target_system['LOCAL']['RADIO_ID'], 1)
|
||||||
|
|
||||||
# Re-Write the IPSC SRC to match the target network's ID
|
# Re-Write the IPSC SRC + DST GROUP in IPSC Headers:
|
||||||
_tmp_data = _tmp_data.replace(_peerid, _target_system['LOCAL']['RADIO_ID'])
|
_tmp_data = _tmp_data.replace(_src_sub + _dst_group, _src_sub + _target['TGID'], 1)
|
||||||
|
|
||||||
# Re-Write the destination Group ID
|
# Re-Write the DST GROUP + IPSC SRC in DMR LC (Header, Terminator and Voice Burst E):
|
||||||
_tmp_data = _tmp_data.replace(_dst_group, _target['TGID'])
|
_tmp_data = _tmp_data.replace(_dst_group + _src_sub, _target['TGID'] + _src_sub, 1)
|
||||||
|
|
||||||
# Re-Write IPSC timeslot value
|
# Re-Write IPSC timeslot value
|
||||||
_call_info = int_id(_data[17:18])
|
_call_info = int_id(_data[17:18])
|
||||||
@ -327,7 +345,6 @@ class confbridgeIPSC(IPSC):
|
|||||||
# END FRAME FORWARDING
|
# END FRAME FORWARDING
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# Set values for the contention handler to test next time there is a frame to forward
|
# Set values for the contention handler to test next time there is a frame to forward
|
||||||
_target_status[_target['TS']]['TX_TGID'] = _target['TGID']
|
_target_status[_target['TS']]['TX_TGID'] = _target['TGID']
|
||||||
_target_status[_target['TS']]['TX_TIME'] = now
|
_target_status[_target['TS']]['TX_TIME'] = now
|
||||||
@ -352,7 +369,7 @@ class confbridgeIPSC(IPSC):
|
|||||||
self.call_start = now
|
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))
|
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':
|
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)))
|
self._report.send_bridgeEvent('GROUP VOICE,START,{},{},{},{},{},{}'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group)))
|
||||||
|
|
||||||
# Action happens on un-key
|
# Action happens on un-key
|
||||||
if _burst_data_type == BURST_DATA_TYPE['VOICE_TERM']:
|
if _burst_data_type == BURST_DATA_TYPE['VOICE_TERM']:
|
||||||
@ -360,11 +377,11 @@ class confbridgeIPSC(IPSC):
|
|||||||
self.call_duration = now - 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)
|
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':
|
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))
|
self._report.send_bridgeEvent('GROUP VOICE,END,{},{},{},{},{},{},{:.2f}'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group), self.call_duration))
|
||||||
else:
|
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':
|
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)))
|
self._report.send_bridgeEvent('GROUP VOICE,UNMATCHED END,{},{},{},{},{},{}'.format(self._system, int_id(_seq_id), int_id(_peerid), int_id(_src_sub), _ts, int_id(_dst_group)))
|
||||||
|
|
||||||
|
|
||||||
# Iterate the rules dictionary
|
# Iterate the rules dictionary
|
||||||
@ -373,8 +390,9 @@ class confbridgeIPSC(IPSC):
|
|||||||
if _system['SYSTEM'] == self._system:
|
if _system['SYSTEM'] == self._system:
|
||||||
|
|
||||||
# TGID matches an ACTIVATION trigger
|
# TGID matches an ACTIVATION trigger
|
||||||
if _dst_group in _system['ON']:
|
if (_dst_group in _system['ON'] or _dst_group in _system['RESET']) and _ts == _system['TS']:
|
||||||
# Set the matching rule as ACTIVE
|
# Set the matching rule as ACTIVE
|
||||||
|
if _dst_group in _system['ON']:
|
||||||
if _system['ACTIVE'] == False:
|
if _system['ACTIVE'] == False:
|
||||||
_system['ACTIVE'] = True
|
_system['ACTIVE'] = True
|
||||||
self._logger.info('(%s) Bridge: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
self._logger.info('(%s) Bridge: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||||
@ -388,8 +406,9 @@ class confbridgeIPSC(IPSC):
|
|||||||
self._logger.info('(%s) Bridge: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - now)
|
self._logger.info('(%s) Bridge: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - now)
|
||||||
|
|
||||||
# TGID matches an DE-ACTIVATION trigger
|
# TGID matches an DE-ACTIVATION trigger
|
||||||
if _dst_group in _system['OFF']:
|
if (_dst_group in _system['OFF'] or _dst_group in _system['RESET']) and _ts == _system['TS']:
|
||||||
# Set the matching rule as ACTIVE
|
# Set the matching rule as ACTIVE
|
||||||
|
if _dst_group in _system['OFF']:
|
||||||
if _system['ACTIVE'] == True:
|
if _system['ACTIVE'] == True:
|
||||||
_system['ACTIVE'] = False
|
_system['ACTIVE'] = False
|
||||||
self._logger.info('(%s) Bridge: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
self._logger.info('(%s) Bridge: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||||
@ -397,12 +416,12 @@ class confbridgeIPSC(IPSC):
|
|||||||
if _system['TO_TYPE'] == 'ON':
|
if _system['TO_TYPE'] == 'ON':
|
||||||
_system['TIMER'] = now
|
_system['TIMER'] = now
|
||||||
self._logger.info('(%s) Bridge: %s set to ON with and "OFF" timer rule: timeout timer cancelled', self._system, _bridge)
|
self._logger.info('(%s) Bridge: %s set to ON with and "OFF" timer rule: timeout timer cancelled', self._system, _bridge)
|
||||||
# Reset tge timer for the rule
|
# Reset the timer for the rule
|
||||||
if _system['ACTIVE'] == False and _system['TO_TYPE'] == 'OFF':
|
if _system['ACTIVE'] == False and _system['TO_TYPE'] == 'OFF':
|
||||||
_system['TIMER'] = now + _system['TIMEOUT']
|
_system['TIMER'] = now + _system['TIMEOUT']
|
||||||
self._logger.info('(%s) Bridge: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - now)
|
self._logger.info('(%s) Bridge: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - now)
|
||||||
# Cancel the timer if we've enabled an "ON" type timeout
|
# Cancel the timer if we've enabled an "ON" type timeout
|
||||||
if _system['ACTIVE'] == True and _system['TO_TYPE'] == 'ON':
|
if _system['ACTIVE'] == True and _system['TO_TYPE'] == 'ON' and _dst_group in _system['OFF']:
|
||||||
_system['TIMER'] = now
|
_system['TIMER'] = now
|
||||||
self._logger.info('(%s) Bridge: %s set to ON with and "OFF" timer rule: timeout timer cancelled', self._system, _bridge)
|
self._logger.info('(%s) Bridge: %s set to ON with and "OFF" timer rule: timeout timer cancelled', self._system, _bridge)
|
||||||
|
|
||||||
@ -479,6 +498,7 @@ if __name__ == '__main__':
|
|||||||
# Build the routing rules and other configuration
|
# Build the routing rules and other configuration
|
||||||
CONFIG_DICT = make_bridge_config('confbridge_rules')
|
CONFIG_DICT = make_bridge_config('confbridge_rules')
|
||||||
BRIDGE_CONF = CONFIG_DICT['BRIDGE_CONF']
|
BRIDGE_CONF = CONFIG_DICT['BRIDGE_CONF']
|
||||||
|
TRUNKS = CONFIG_DICT['TRUNKS']
|
||||||
BRIDGES = CONFIG_DICT['BRIDGES']
|
BRIDGES = CONFIG_DICT['BRIDGES']
|
||||||
|
|
||||||
# Build the Access Control List
|
# Build the Access Control List
|
||||||
|
22
confbridge_rules_SAMPLE.py
Normal file → Executable file
22
confbridge_rules_SAMPLE.py
Normal file → Executable file
@ -19,6 +19,9 @@ configuration file.
|
|||||||
* ON and OFF are LISTS of Talkgroup IDs used to trigger this system off and on. Even if you
|
* 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
|
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': [] ".
|
handled with an empty list, such as " 'ON': [] ".
|
||||||
|
* RESET is a list of Talkgroup IDs that, in addition to the ON and OFF lists will cause a running
|
||||||
|
timer to be reset. This is useful if you are using different TGIDs for voice traffic than
|
||||||
|
triggering. If you are not, there is NO NEED to use this feature.
|
||||||
* TO_TYPE is timeout type. If you want to use timers, ON means when it's turned on, it will
|
* 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
|
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
|
period. If you don't want to use timers, set it to anything else, but 'NONE' might be
|
||||||
@ -38,18 +41,25 @@ BRIDGE_CONF = {
|
|||||||
'REPORT': True,
|
'REPORT': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TRUNK IPSC Systems -- trunk bypasses the contention handler and always transmits traffic
|
||||||
|
#
|
||||||
|
# This is a python LIST data type. It needs to be here, but just leave it empty if not used.
|
||||||
|
# The contents are a quoted, comma separated list of IPSC systems that are traffic trunks.
|
||||||
|
# Example: TRUNKS = ['MASTER-1', 'CLIENT-2']
|
||||||
|
TRUNKS = []
|
||||||
|
|
||||||
BRIDGES = {
|
BRIDGES = {
|
||||||
'WORLDWIDE': [
|
'WORLDWIDE': [
|
||||||
{'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10]},
|
{'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10], 'RESET': []},
|
||||||
{'SYSTEM': 'CLIENT-1', 'TS': 1, 'TGID': 3100, '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], 'RESET': []}
|
||||||
],
|
],
|
||||||
'ENGLISH': [
|
'ENGLISH': [
|
||||||
{'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10]},
|
{'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10], 'RESET': []},
|
||||||
{'SYSTEM': 'CLIENT-2', '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], 'RESET': []}
|
||||||
],
|
],
|
||||||
'STATEWIDE': [
|
'STATEWIDE': [
|
||||||
{'SYSTEM': 'MASTER-1', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10]},
|
{'SYSTEM': 'MASTER-1', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10], 'RESET': []},
|
||||||
{'SYSTEM': 'CLIENT-2', '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], 'RESET': []}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
dmrlink_SAMPLE.cfg
Normal file → Executable file
10
dmrlink_SAMPLE.cfg
Normal file → Executable file
@ -86,11 +86,11 @@ LOG_NAME: DMRlink
|
|||||||
TRY_DOWNLOAD: True
|
TRY_DOWNLOAD: True
|
||||||
LOCAL_FILE: False
|
LOCAL_FILE: False
|
||||||
PATH: ./
|
PATH: ./
|
||||||
PEER_FILE: peer_ids.csv
|
PEER_FILE: peer_ids.json
|
||||||
SUBSCRIBER_FILE: subscriber_ids.csv
|
SUBSCRIBER_FILE: subscriber_ids.json
|
||||||
TGID_FILE: talkgroup_ids.csv
|
TGID_FILE: talkgroup_ids.json
|
||||||
PEER_URL: http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=repeaters&format=csv&header=0
|
PEER_URL: https://www.radioid.net/static/rptrs.json
|
||||||
SUBSCRIBER_URL: http://www.dmr-marc.net/cgi-bin/trbo-database/datadump.cgi?table=users&format=csv&header=0
|
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
|
||||||
STALE_DAYS: 7
|
STALE_DAYS: 7
|
||||||
|
|
||||||
|
|
||||||
|
0
documents/FAQ.md
Normal file → Executable file
0
documents/FAQ.md
Normal file → Executable file
0
documents/internal_data_decode.txt
Normal file → Executable file
0
documents/internal_data_decode.txt
Normal file → Executable file
0
documents/voice_burst_decoding.txt
Normal file → Executable file
0
documents/voice_burst_decoding.txt
Normal file → Executable file
0
documents/voice_packets.txt
Normal file → Executable file
0
documents/voice_packets.txt
Normal file → Executable file
0
ipsc/.gitignore
vendored
Normal file → Executable file
0
ipsc/.gitignore
vendored
Normal file → Executable file
0
ipsc/__init__.py
Normal file → Executable file
0
ipsc/__init__.py
Normal file → Executable file
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
# Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -21,16 +21,32 @@
|
|||||||
import ConfigParser
|
import ConfigParser
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from socket import gethostbyname
|
from socket import getaddrinfo, IPPROTO_UDP
|
||||||
|
|
||||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||||
__author__ = 'Cortney T. Buffington, N0MJS'
|
__author__ = 'Cortney T. Buffington, N0MJS'
|
||||||
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group'
|
__copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group'
|
||||||
__license__ = 'GNU GPLv3'
|
__license__ = 'GNU GPLv3'
|
||||||
__maintainer__ = 'Cort Buffington, N0MJS'
|
__maintainer__ = 'Cort Buffington, N0MJS'
|
||||||
__email__ = 'n0mjs@me.com'
|
__email__ = 'n0mjs@me.com'
|
||||||
|
|
||||||
|
|
||||||
|
def get_address(_config):
|
||||||
|
ipv4 = ''
|
||||||
|
ipv6 = ''
|
||||||
|
socket_info = getaddrinfo(_config, None, 0, 0, IPPROTO_UDP)
|
||||||
|
for item in socket_info:
|
||||||
|
if item[0] == 2:
|
||||||
|
ipv4 = item[4][0]
|
||||||
|
elif item[0] == 30:
|
||||||
|
ipv6 = item[4][0]
|
||||||
|
|
||||||
|
if ipv4:
|
||||||
|
return ipv4
|
||||||
|
if ipv6:
|
||||||
|
return ipv6
|
||||||
|
return 'invalid address'
|
||||||
|
|
||||||
def build_config(_config_file):
|
def build_config(_config_file):
|
||||||
config = ConfigParser.ConfigParser()
|
config = ConfigParser.ConfigParser()
|
||||||
|
|
||||||
@ -115,7 +131,7 @@ def build_config(_config_file):
|
|||||||
|
|
||||||
# Things we need to know to connect and be a peer in this IPSC
|
# Things we need to know to connect and be a peer in this IPSC
|
||||||
'RADIO_ID': hex(int(config.get(section, 'RADIO_ID')))[2:].rjust(8,'0').decode('hex'),
|
'RADIO_ID': hex(int(config.get(section, 'RADIO_ID')))[2:].rjust(8,'0').decode('hex'),
|
||||||
'IP': gethostbyname(config.get(section, 'IP')),
|
'IP': config.get(section, 'IP'),
|
||||||
'PORT': config.getint(section, 'PORT'),
|
'PORT': config.getint(section, 'PORT'),
|
||||||
'ALIVE_TIMER': config.getint(section, 'ALIVE_TIMER'),
|
'ALIVE_TIMER': config.getint(section, 'ALIVE_TIMER'),
|
||||||
'MAX_MISSED': config.getint(section, 'MAX_MISSED'),
|
'MAX_MISSED': config.getint(section, 'MAX_MISSED'),
|
||||||
@ -144,7 +160,7 @@ def build_config(_config_file):
|
|||||||
})
|
})
|
||||||
if not CONFIG['SYSTEMS'][section]['LOCAL']['MASTER_PEER']:
|
if not CONFIG['SYSTEMS'][section]['LOCAL']['MASTER_PEER']:
|
||||||
CONFIG['SYSTEMS'][section]['MASTER'].update({
|
CONFIG['SYSTEMS'][section]['MASTER'].update({
|
||||||
'IP': gethostbyname(config.get(section, 'MASTER_IP')),
|
'IP': get_address(config.get(section, 'MASTER_IP')),
|
||||||
'PORT': config.getint(section, 'MASTER_PORT')
|
'PORT': config.getint(section, 'MASTER_PORT')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -219,7 +235,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Ensure we have a path for the config file, if one wasn't specified, then use the execution directory
|
# Ensure we have a path for the config file, if one wasn't specified, then use the execution directory
|
||||||
if not cli_args.CONFIG_FILE:
|
if not cli_args.CONFIG_FILE:
|
||||||
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/dmrlink.cfg'
|
cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/../dmrlink.cfg'
|
||||||
|
|
||||||
|
|
||||||
pprint(build_config(cli_args.CONFIG_FILE))
|
pprint(build_config(cli_args.CONFIG_FILE))
|
||||||
|
0
ipsc/ipsc_const.py
Normal file → Executable file
0
ipsc/ipsc_const.py
Normal file → Executable file
0
ipsc/ipsc_mask.py
Normal file → Executable file
0
ipsc/ipsc_mask.py
Normal file → Executable file
0
ipsc/reporting_const.py
Normal file → Executable file
0
ipsc/reporting_const.py
Normal file → Executable file
183
mk-dmrlink
183
mk-dmrlink
@ -1,8 +1,10 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
|
||||||
currentdir=`pwd`
|
PREFIX=/opt/dmrlink
|
||||||
|
echo "DMRlink will be installed in: $PREFIX"
|
||||||
|
|
||||||
echo "Current working directory is" $currentdir
|
currentdir=`pwd`
|
||||||
|
echo "Current working directory is: $currentdir"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@ -13,17 +15,44 @@ echo ""
|
|||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
# Install the required support programs
|
# Install the required support programs
|
||||||
apt-get install unzip -y
|
|
||||||
apt-get install python-dev -y
|
|
||||||
apt-get install python-pip -y
|
|
||||||
apt-get install python-twisted -y
|
|
||||||
pip install bitstring
|
|
||||||
pip install bitarray
|
|
||||||
|
|
||||||
cd /opt
|
distro=$(lsb_release -i | awk -F":" '{ gsub(/^[ \t]+/, "", $2); print $2 }')
|
||||||
git clone https://github.com/n0mjs710/dmr_utils.git
|
release=$(lsb_release -r | awk -F":" '{ gsub(/^[ \t]+/, "", $2); print $2 }')
|
||||||
cd dmr_utils/
|
echo "Current Linux distribution is: $distro $release"
|
||||||
pip install .
|
|
||||||
|
if [[ "$distro" =~ ^(CentOS|Fedora|openSUSE|)$ ]]; then
|
||||||
|
echo "$distro uses yum"
|
||||||
|
yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(echo $release | awk -F"." '{print $1}').noarch.rpm
|
||||||
|
yum install -y gcc gcc-c++ glibc-devel make
|
||||||
|
yum install -y unzip
|
||||||
|
yum install -y python-devel
|
||||||
|
yum install -y python-pip
|
||||||
|
yum install -y python-twisted
|
||||||
|
# pip install bitstring
|
||||||
|
# pip install bitarray
|
||||||
|
else
|
||||||
|
echo "$distro uses apt"
|
||||||
|
apt-get install -y build-essential
|
||||||
|
apt-get install -y unzip
|
||||||
|
apt-get install -y python-dev
|
||||||
|
apt-get install -y python-pip
|
||||||
|
apt-get install -y python-twisted
|
||||||
|
# pip install bitstring
|
||||||
|
# pip install bitarray
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install dmr_utils with pip install
|
||||||
|
pip install dmr_utils
|
||||||
|
###############################################################################
|
||||||
|
# Following lines should be removed due to the pip install method for dmr_utils
|
||||||
|
#cd /opt
|
||||||
|
#if [ ! -d /opt/dmr_utils ]; then
|
||||||
|
# git clone https://github.com/n0mjs710/dmr_utils.git
|
||||||
|
#fi
|
||||||
|
#cd dmr_utils/
|
||||||
|
#git pull
|
||||||
|
#pip install .
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
echo "Required programs installed, continuing"
|
echo "Required programs installed, continuing"
|
||||||
|
|
||||||
@ -32,19 +61,13 @@ echo "Required programs installed, continuing"
|
|||||||
# The needed files are copied to /opt/dmrlink
|
# The needed files are copied to /opt/dmrlink
|
||||||
|
|
||||||
# Make needed directories
|
# Make needed directories
|
||||||
mkdir -p /opt/dmrlink/ambe_audio/
|
mkdir -p $PREFIX/confbridge/
|
||||||
mkdir -p /opt/dmrlink/bridge/
|
mkdir -p $PREFIX/playback/
|
||||||
mkdir -p /opt/dmrlink/confbridge/
|
mkdir -p $PREFIX/proxy/
|
||||||
mkdir -p /opt/dmrlink/log/
|
mkdir -p $PREFIX/samples
|
||||||
mkdir -p /opt/dmrlink/playback/
|
mkdir -p /var/log/dmrlink
|
||||||
mkdir -p /opt/dmrlink/play_group/
|
|
||||||
mkdir -p /opt/dmrlink/proxy/
|
|
||||||
mkdir -p /opt/dmrlink/rcm/
|
|
||||||
mkdir -p /opt/dmrlink/record/
|
|
||||||
mkdir -p /opt/dmrlink/samples
|
|
||||||
mkdir -p /var/log/dmrlink
|
|
||||||
|
|
||||||
cd /opt/dmrlink
|
cd $PREFIX
|
||||||
|
|
||||||
# Put common files in /opt/dmrlink
|
# Put common files in /opt/dmrlink
|
||||||
# cp $currentdir/peer_ids.csv /opt/dmrlink
|
# cp $currentdir/peer_ids.csv /opt/dmrlink
|
||||||
@ -52,95 +75,89 @@ cd /opt/dmrlink
|
|||||||
# cp $currentdir/talkgroup_ids.csv /opt/dmrlink
|
# cp $currentdir/talkgroup_ids.csv /opt/dmrlink
|
||||||
|
|
||||||
# Copy ipsc directory into each app directory
|
# Copy ipsc directory into each app directory
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/ambe_audio/
|
cp -rf $currentdir/ipsc/ $PREFIX/confbridge/
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/bridge/
|
cp -rf $currentdir/ipsc/ $PREFIX/playback/
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/confbridge/
|
cp -rf $currentdir/ipsc/ $PREFIX/proxy/
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/log/
|
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/playback/
|
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/play_group/
|
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/proxy/
|
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/rcm/
|
|
||||||
cp -rf $currentdir/ipsc/ /opt/dmrlink/record/
|
|
||||||
|
|
||||||
# Put a copy of the samples together for easy reference
|
# Put a copy of the samples together for easy reference
|
||||||
cp $currentdir/bridge_rules_SAMPLE.py /opt/dmrlink/samples
|
#cp $currentdir/bridge_rules_SAMPLE.py /opt/dmrlink/samples
|
||||||
cp $currentdir/confbridge_rules_SAMPLE.py /opt/dmrlink/samples
|
cp $currentdir/confbridge_rules_SAMPLE.py $PREFIX/samples
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/samples
|
cp $currentdir/dmrlink_SAMPLE.cfg $PREFIX/samples
|
||||||
cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/samples
|
#cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/samples
|
||||||
cp $currentdir/playback_config_SAMPLE.py /opt/dmrlink/samples
|
cp $currentdir/playback_config_SAMPLE.py $PREFIX/samples
|
||||||
cp $currentdir/ambe_audio.cfg /opt/dmrlink/samples
|
#cp $currentdir/ambe_audio.cfg /opt/dmrlink/samples
|
||||||
cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/samples
|
cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/samples
|
||||||
|
|
||||||
# Put the doc together for easy reference
|
# Put the doc together for easy reference
|
||||||
cp -rf $currentdir/documents /opt/dmrlink
|
cp -rf $currentdir/documents $PREFIX
|
||||||
cp $currentdir/LICENSE.txt /opt/dmrlink/documents
|
cp $currentdir/LICENSE.txt $PREFIX/documents
|
||||||
cp $currentdir/requirements.txt /opt/dmrlink/documents
|
cp $currentdir/requirements.txt $PREFIX/documents
|
||||||
cp $currentdir/ambe_audio_commands.txt /opt/dmrlink/documents
|
#cp $currentdir/ambe_audio_commands.txt /opt/dmrlink/documents
|
||||||
|
|
||||||
# ambe_audio
|
# ambe_audio
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/ambe_audio/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/ambe_audio/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/ambe_audio/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/ambe_audio/
|
||||||
#
|
#
|
||||||
cp $currentdir/ambe_audio.cfg /opt/dmrlink/ambe_audio/
|
#cp $currentdir/ambe_audio.cfg /opt/dmrlink/ambe_audio/
|
||||||
cp $currentdir/ambe_audio.py /opt/dmrlink/ambe_audio/
|
#cp $currentdir/ambe_audio.py /opt/dmrlink/ambe_audio/
|
||||||
cp $currentdir/ambe_audio_commands.txt /opt/dmrlink/ambe_audio/
|
#cp $currentdir/ambe_audio_commands.txt /opt/dmrlink/ambe_audio/
|
||||||
cp $currentdir/template.bin /opt/dmrlink/ambe_audio/
|
#cp $currentdir/template.bin /opt/dmrlink/ambe_audio/
|
||||||
|
|
||||||
# Bridge app
|
# Bridge app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/bridge/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/bridge/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/bridge/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/bridge/
|
||||||
#
|
#
|
||||||
cp $currentdir/bridge.py /opt/dmrlink/bridge/
|
#cp $currentdir/bridge.py /opt/dmrlink/bridge/
|
||||||
cp $currentdir/bridge_rules_SAMPLE.py /opt/dmrlink/bridge/
|
#cp $currentdir/bridge_rules_SAMPLE.py /opt/dmrlink/bridge/
|
||||||
cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/bridge/
|
#cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/bridge/
|
||||||
cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/bridge/
|
#cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/bridge/
|
||||||
|
|
||||||
# ConfBridge app
|
# ConfBridge app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/confbridge/
|
cp $currentdir/dmrlink.py $PREFIX/confbridge/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/confbridge/
|
cp $currentdir/dmrlink_SAMPLE.cfg $PREFIX/confbridge/
|
||||||
#
|
#
|
||||||
cp $currentdir/confbridge.py /opt/dmrlink/confbridge/
|
cp $currentdir/confbridge.py $PREFIX/confbridge/
|
||||||
cp $currentdir/confbridge_rules_SAMPLE.py /opt/dmrlink/confbridge/
|
cp $currentdir/confbridge_rules_SAMPLE.py $PREFIX/confbridge/
|
||||||
cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/confbridge/
|
#cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/confbridge/
|
||||||
cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/confbridge/
|
cp $currentdir/sub_acl_SAMPLE.py $PREFIX/confbridge/
|
||||||
|
|
||||||
# Log app
|
# Log app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/log/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/log/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/log/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/log/
|
||||||
#
|
#
|
||||||
cp $currentdir/log.py /opt/dmrlink/log/
|
#cp $currentdir/log.py /opt/dmrlink/log/
|
||||||
|
|
||||||
# Playback (Parrot)
|
# Playback (Parrot)
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/playback/
|
cp $currentdir/dmrlink.py $PREFIX/playback/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/playback/
|
cp $currentdir/dmrlink_SAMPLE.cfg $PREFIX/playback/
|
||||||
#
|
#
|
||||||
cp $currentdir/playback.py /opt/dmrlink/playback/
|
cp $currentdir/playback.py $PREFIX/playback/
|
||||||
cp $currentdir/playback_config_SAMPLE.py /opt/dmrlink/playback/
|
cp $currentdir/playback_config_SAMPLE.py $PREFIX/playback/
|
||||||
|
|
||||||
# Play Group app
|
# Play Group app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/play_group/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/play_group/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/play_group/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/play_group/
|
||||||
#
|
#
|
||||||
cp $currentdir/play_group.py /opt/dmrlink/play_group/
|
#cp $currentdir/play_group.py /opt/dmrlink/play_group/
|
||||||
|
|
||||||
# proxy app
|
# proxy app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/proxy/
|
cp $currentdir/dmrlink.py $PREFIX/proxy/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/proxy/
|
cp $currentdir/dmrlink_SAMPLE.cfg $PREFIX/proxy/
|
||||||
#
|
#
|
||||||
cp $currentdir/proxy.py /opt/dmrlink/proxy/
|
cp $currentdir/proxy.py $PREFIX/proxy/
|
||||||
cp $currentdir/known_bridges_SAMPLE.py /opt/dmrlink/proxy/
|
#cp $currentdir/known_bridges_SAMPLE.py $PREFIX/proxy/
|
||||||
cp $currentdir/sub_acl_SAMPLE.py /opt/dmrlink/proxy/
|
cp $currentdir/sub_acl_SAMPLE.py $PREFIX/proxy/
|
||||||
|
|
||||||
# rcm app
|
# rcm app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/rcm/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/rcm/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/rcm/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/rcm/
|
||||||
#
|
#
|
||||||
cp $currentdir/rcm_db_log.py /opt/dmrlink/rcm/
|
#cp $currentdir/rcm_db_log.py /opt/dmrlink/rcm/
|
||||||
cp $currentdir/rcm.py /opt/dmrlink/rcm/
|
#cp $currentdir/rcm.py /opt/dmrlink/rcm/
|
||||||
|
|
||||||
# record app
|
# record app
|
||||||
cp $currentdir/dmrlink.py /opt/dmrlink/record/
|
#cp $currentdir/dmrlink.py /opt/dmrlink/record/
|
||||||
cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/record/
|
#cp $currentdir/dmrlink_SAMPLE.cfg /opt/dmrlink/record/
|
||||||
#
|
#
|
||||||
cp $currentdir/record.py /opt/dmrlink/record/
|
#cp $currentdir/record.py /opt/dmrlink/record/
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ TGID = 12345
|
|||||||
# TIMESLOT TO LISTEN FOR GROUP VOICE AND REPEAT
|
# TIMESLOT TO LISTEN FOR GROUP VOICE AND REPEAT
|
||||||
# This is a tuple of timeslots to listen to. Note, if there's only
|
# This is a tuple of timeslots to listen to. Note, if there's only
|
||||||
# one, you still have to use the parenthesis and comma. Just
|
# one, you still have to use the parenthesis and comma. Just
|
||||||
# deal with it, or make it better. TS1 = 0, TS2 = 1.
|
# deal with it, or make it better. TS1 = 1, TS2 = 2.
|
||||||
GROUP_TS = (1,)
|
GROUP_TS = (2,)
|
||||||
# ALTERNATE SOURCE SUBSCRIBER ID FOR REPEATED TRANSMISSION
|
# ALTERNATE SOURCE SUBSCRIBER ID FOR REPEATED TRANSMISSION
|
||||||
# Some folks have radios that don't respond to their own subscriber
|
# Some folks have radios that don't respond to their own subscriber
|
||||||
# IDs. Some just don't want to have the playback come from the same
|
# IDs. Some just don't want to have the playback come from the same
|
||||||
@ -31,5 +31,5 @@ SUB = 12345
|
|||||||
# TIMESLOT TO LISTEN FOR PRIVATE VOICE AND REPEAT
|
# TIMESLOT TO LISTEN FOR PRIVATE VOICE AND REPEAT
|
||||||
# This is a tuple of timeslots to listen to. Note, if there's only
|
# This is a tuple of timeslots to listen to. Note, if there's only
|
||||||
# one, you still have to use the parenthesis and comma. Just
|
# one, you still have to use the parenthesis and comma. Just
|
||||||
# deal with it, or make it better. TS1 = 0, TS2 = 1.
|
# deal with it, or make it better. TS1 = 1, TS2 = 2.
|
||||||
PRIVATE_TS = (0,1)
|
PRIVATE_TS = (1,2)
|
||||||
|
24
proxy.py
24
proxy.py
@ -72,16 +72,28 @@ __email__ = 'n0mjs@me.com'
|
|||||||
# are not yet implemented.
|
# are not yet implemented.
|
||||||
def build_acl(_sub_acl):
|
def build_acl(_sub_acl):
|
||||||
try:
|
try:
|
||||||
|
logger.info('ACL file found, importing entries. This will take about 1.5 seconds per 1 million IDs')
|
||||||
acl_file = import_module(_sub_acl)
|
acl_file = import_module(_sub_acl)
|
||||||
for i, e in enumerate(acl_file.ACL):
|
sections = acl_file.ACL.split(':')
|
||||||
acl_file.ACL[i] = hex_str_3(acl_file.ACL[i])
|
ACL_ACTION = sections[0]
|
||||||
logger.info('ACL file found and ACL entries imported')
|
entries_str = sections[1]
|
||||||
ACL_ACTION = acl_file.ACL_ACTION
|
ACL = set()
|
||||||
ACL = acl_file.ACL_ACTION
|
|
||||||
|
for entry in entries_str.split(','):
|
||||||
|
if '-' in entry:
|
||||||
|
start,end = entry.split('-')
|
||||||
|
start,end = int(start), int(end)
|
||||||
|
for id in range(start, end+1):
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
else:
|
||||||
|
id = int(entry)
|
||||||
|
ACL.add(hex_str_3(id))
|
||||||
|
|
||||||
|
logger.info('ACL loaded: action "{}" for {:,} radio IDs'.format(ACL_ACTION, len(ACL)))
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
logger.info('ACL file not found or invalid - all subscriber IDs are valid')
|
||||||
ACL_ACTION = 'NONE'
|
ACL_ACTION = 'NONE'
|
||||||
ACL = []
|
|
||||||
|
|
||||||
# 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
|
||||||
|
0
requirements.txt
Normal file → Executable file
0
requirements.txt
Normal file → Executable file
12
sub_acl_SAMPLE.py
Normal file → Executable file
12
sub_acl_SAMPLE.py
Normal file → Executable file
@ -1,6 +1,6 @@
|
|||||||
ACL_ACTION = "DENY" # May be PERMIT|DENY
|
# The 'action' May be PERMIT|DENY
|
||||||
ACL = [
|
# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999)
|
||||||
1234001,
|
# Format:
|
||||||
1234002,
|
# ACL = 'action:id|start-end|,id|start-end,....'
|
||||||
1234003
|
|
||||||
]
|
ACL = 'DENY:1-2999,16777215'
|
0
systemd/ambe_audio.service
Normal file → Executable file
0
systemd/ambe_audio.service
Normal file → Executable file
0
systemd/bridge.service
Normal file → Executable file
0
systemd/bridge.service
Normal file → Executable file
0
systemd/playback.service
Normal file → Executable file
0
systemd/playback.service
Normal file → Executable file
0
template.bin
Normal file → Executable file
0
template.bin
Normal file → Executable file
Loading…
Reference in New Issue
Block a user